Extended Timer and TimeStamp - SMcNeill - 04-20-2022
Code: (Select All) SHELL "https://www.epochconverter.com/"
PRINT "Compare to time stamps generated at the website which popped up in your browser.https://www.epochconverter.com/"
CONST MyTimeZone## = 4 * 3600
DO
_LIMIT 1
CLS
PRINT TimeStamp(DATE$, TIMER + MyTimeZone) 'Timezone difference with GMT, which is what the webpage sometimes points to.
' If the times seem off from the website, you'll want to change the timezone
' offset to match your current time zone.
PRINT ExtendedTimer 'Unix Epoch Timer based on local time.
_DISPLAY
LOOP
FUNCTION TimeStamp## (d$, t##) 'date and timer
'Based on Unix Epoch time, which starts at year 1970.
DIM l AS _INTEGER64, l1 AS _INTEGER64, m AS _INTEGER64
DIM d AS _INTEGER64, y AS _INTEGER64, i AS _INTEGER64
DIM s AS _FLOAT
l = INSTR(d$, "-")
l1 = INSTR(l + 1, d$, "-")
m = VAL(LEFT$(d$, l))
d = VAL(MID$(d$, l + 1))
y = VAL(MID$(d$, l1 + 1))
IF y < 1970 THEN 'calculate shit backwards
SELECT CASE m 'turn the day backwards for the month
CASE 1, 3, 5, 7, 8, 10, 12: d = 31 - d '31 days
CASE 2: d = 28 - d 'special 28 or 29.
CASE 4, 6, 9, 11: d = 30 - d '30 days
END SELECT
IF y MOD 4 = 0 AND m < 3 THEN 'check for normal leap year, and we're before it...
d = d + 1 'assume we had a leap year, subtract another day
IF y MOD 100 = 0 AND y MOD 400 <> 0 THEN d = d - 1 'not a leap year if year is divisible by 100 and not 400
END IF
'then count the months that passed after the current month
FOR i = m + 1 TO 12
SELECT CASE i
CASE 2: d = d + 28
CASE 3, 5, 7, 8, 10, 12: d = d + 31
CASE 4, 6, 9, 11: d = d + 30
END SELECT
NEXT
'we should now have the entered year calculated. Now lets add in for each year from this point to 1970
d = d + 365 * (1969 - y) '365 days per each standard year
FOR i = 1968 TO y + 1 STEP -4 'from 1968 onwards,backwards, skipping the current year (which we handled previously in the FOR loop)
d = d + 1 'subtract an extra day every leap year
IF (i MOD 100) = 0 AND (i MOD 400) <> 0 THEN d = d - 1 'but skipping every year divisible by 100, but not 400
NEXT
s## = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
TimeStamp## = -(s## + 24 * 60 * 60 - t##)
EXIT FUNCTION
ELSE
y = y - 1970
END IF
FOR i = 1 TO m 'for this year,
SELECT CASE i 'Add the number of days for each previous month passed
CASE 1: d = d 'January doestn't have any carry over days.
CASE 2, 4, 6, 8, 9, 11: d = d + 31
CASE 3 'Feb might be a leap year
IF (y MOD 4) = 2 THEN 'if this year is divisible by 4 (starting in 1972)
d = d + 29 'its a leap year
IF (y MOD 100) = 30 AND (y MOD 400) <> 30 THEN 'unless..
d = d - 1 'the year is divisible by 100, and not divisible by 400
END IF
ELSE 'year not divisible by 4, no worries
d = d + 28
END IF
CASE 5, 7, 10, 12: d = d + 30
END SELECT
NEXT
d = (d - 1) + 365 * y 'current month days passed + 365 days per each standard year
FOR i = 2 TO y - 1 STEP 4 'from 1972 onwards, skipping the current year (which we handled previously in the FOR loopp)
d = d + 1 'add an extra day every leap year
IF (i MOD 100) = 30 AND (i MOD 400) <> 30 THEN d = d - 1 'but skiping every year divisible by 100, but not 400
NEXT
s## = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
TimeStamp## = (s## + t##)
END FUNCTION
FUNCTION ExtendedTimer##
'Simplified version of the TimeStamp routine, streamlined to only give positive values based on the current timer.
'Note: Only good until the year 2100, as we don't do all the fancy calculations for leap years.
'A timer should work quickly and efficiently in the background; and the less we do, the less lag we might insert
'into a program.
DIM m AS INTEGER, d AS INTEGER, y AS INTEGER
DIM s AS _FLOAT, day AS STRING
day = DATE$
m = VAL(LEFT$(day, 2))
d = VAL(MID$(day, 4, 2))
y = VAL(RIGHT$(day, 4)) - 1970
SELECT CASE m 'Add the number of days for each previous month passed
CASE 2: d = d + 31
CASE 3: d = d + 59
CASE 4: d = d + 90
CASE 5: d = d + 120
CASE 6: d = d + 151
CASE 7: d = d + 181
CASE 8: d = d + 212
CASE 9: d = d + 243
CASE 10: d = d + 273
CASE 11: d = d + 304
CASE 12: d = d + 334
END SELECT
IF (y MOD 4) = 2 AND m > 2 THEN d = d + 1 'add a day if this is leap year and we're past february
d = (d - 1) + 365 * y 'current month days passed + 365 days per each standard year
d = d + (y + 2) \ 4 'add in days for leap years passed
s = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
ExtendedTimer## = (s + TIMER)
END FUNCTION
RE: Extended Timer and TimeStamp - dcromley - 04-22-2022
@SMcNeill ,
You can replace 13 lines of code with 2 lines (see code). And running the code will show it works.
EDIT: .. with 1 line of code .. (code was changed)
Re "What Happened?":
I don't see how you can get so much done in one day!
But thanks!
Code: (Select All) Option _Explicit
DefLng A-Z
Dim d, m, t
If 0 Then
' ---- you can replace the following: ----
Select Case m 'Add the number of days for each previous month passed
Case 2: d = d + 31
Case 3: d = d + 59
Case 4: d = d + 90
Case 5: d = d + 120
Case 6: d = d + 151
Case 7: d = d + 181
Case 8: d = d + 212
Case 9: d = d + 243
Case 10: d = d + 273
Case 11: d = d + 304
Case 12: d = d + 334
End Select
' ---- with the following: ----
d = d + Int((295 + 153 * ((m + 9) Mod 12) + 2) / 5) Mod 365
End If
' -- proof:
Print "Date", "Method1", "Method2", "Difference"
For m = 1 To 12
Print Str$(m) + "/1", Method1(m, 1), Method2(m, 1), Method1(m, 1) - Method2(m, 1)
Next m
Print "Dec 31", Method1(12, 31), Method2(12, 31), Method1(12, 31) - Method2(12, 31)
Function Method1 (m, d)
Select Case m 'Add the number of days for each previous month passed
Case 2: d = d + 31
Case 3: d = d + 59
Case 4: d = d + 90
Case 5: d = d + 120
Case 6: d = d + 151
Case 7: d = d + 181
Case 8: d = d + 212
Case 9: d = d + 243
Case 10: d = d + 273
Case 11: d = d + 304
Case 12: d = d + 334
End Select
Method1 = d
End Function
Function Method2 (m, d)
Method2 = d + Int((295 + 153 * ((m + 9) Mod 12) + 2) / 5) Mod 365
End Function
RE: Extended Timer and TimeStamp - SMcNeill - 01-02-2023
And here's a nice human readable timestamp:
Code: (Select All) Type SYSTIME
year As Integer
month As Integer
weekday As Integer
day As Integer
hour As Integer
minute As Integer
second As Integer
millis As Integer
End Type
Declare Dynamic Library "Kernel32"
Sub GetSystemTime (lpSystemTime As SYSTIME)
Sub GetLocalTime (lpSystemTime As SYSTIME)
End Declare
Dim st As SYSTIME
GetSystemTime st
Print st.year, st.month, st.day, st.weekday
Print st.hour, st.minute, st.second, st.millis
GetLocalTime st
Print st.year, st.month, st.day, st.weekday
Print st.hour, st.minute, st.second, st.millis
Do
_Limit 100
Locate 10, 1: Print " "; LocalTimeStamp$
Print LocalTimeStamp64
Loop Until _KeyHit = 27
Function LocalTimeStamp$
Dim st As SYSTIME
GetLocalTime st
t$ = AddString(t$, st.year, 4)
t$ = AddString(t$, st.month, 2)
t$ = AddString(t$, st.day, 2)
t$ = AddString(t$, st.weekday, 1)
t$ = AddString(t$, st.hour, 2)
t$ = AddString(t$, st.minute, 2)
t$ = AddString(t$, st.second, 2)
t$ = AddString(t$, st.millis, 3)
LocalTimeStamp = t$
End Function
Function LocalTimeStamp64~&&
Dim st As SYSTIME: GetLocalTime st
LocalTimeStamp64 = st.year * 1E14 + st.month * 1E12 + st.day * 1E10 + st.weekday * 1E9 + st.hour * 1E7 + st.minute * 1E5 + st.second * 1E3 + st.millis
End Function
Function AddString$ (what$, value As _Integer64, minDigits As Integer)
v$ = Right$(String$(minDigits, "0") + _Trim$(Str$(value)), minDigits)
AddString = what$ + v$
End Function
Comes in both a string and an integer64 version. Reading it is:
year
month
day
weekday (0 for sunday, 6 for saturday)
hour
minute
second
millisecond
Personally, I think this is a better method than the UTC Timestamps just because I -- as a human being -- can read and understand them with just one quick glance at them.
RE: Extended Timer and TimeStamp - SMcNeill - 01-02-2023
Note: Strings are inherently slow, so if you need a time stamp for use inside a loop, use the integer64 version above. For example, try the code here:
Code: (Select All) Type SYSTIME
year As Integer
month As Integer
weekday As Integer
day As Integer
hour As Integer
minute As Integer
second As Integer
millis As Integer
End Type
Declare Dynamic Library "Kernel32"
Sub GetSystemTime (lpSystemTime As SYSTIME)
Sub GetLocalTime (lpSystemTime As SYSTIME)
End Declare
Dim st As SYSTIME
Dim As _Integer64 count, count1
GetSystemTime st
Print st.year, st.month, st.day, st.weekday
Print st.hour, st.minute, st.second, st.millis
GetLocalTime st
Print st.year, st.month, st.day, st.weekday
Print st.hour, st.minute, st.second, st.millis
Do
_Limit 100
Locate 10, 1: Print " "; LocalTimeStamp$
Print LocalTimeStamp64
Loop Until _KeyHit
Print "Timing routines for 5 seconds each:"
t# = Timer + 5
Do Until Timer > t#
count = count + 1
test$ = LocalTimeStamp$
Loop
t# = Timer + 5
Do Until Timer > t#
count1 = count1 + 1
t = LocalTimeStamp64
Loop
Print "In 5 seconds, each routine ran:"
Print Using "###,###,###,### (String)"; count
Print Using "###,###,###,### (int64)"; count1
Function LocalTimeStamp$
Dim st As SYSTIME
GetLocalTime st
t$ = AddString(t$, st.year, 4)
t$ = AddString(t$, st.month, 2)
t$ = AddString(t$, st.day, 2)
t$ = AddString(t$, st.weekday, 1)
t$ = AddString(t$, st.hour, 2)
t$ = AddString(t$, st.minute, 2)
t$ = AddString(t$, st.second, 2)
t$ = AddString(t$, st.millis, 3)
LocalTimeStamp = t$
End Function
Function LocalTimeStamp64~&&
Dim st As SYSTIME: GetLocalTime st
LocalTimeStamp64 = st.year * 1E14 + st.month * 1E12 + st.day * 1E10 + st.weekday * 1E9 + st.hour * 1E7 + st.minute * 1E5 + st.second * 1E3 + st.millis
End Function
Function AddString$ (what$, value As _Integer64, minDigits As Integer)
v$ = Right$(String$(minDigits, "0") + _Trim$(Str$(value)), minDigits)
AddString = what$ + v$
End Function
I get about 3 million calls to the string version of the timestamp, and about 56 million calls to the integer64 version. Numbers are just faster than excessive amounts of string manipulation like this.
|