Author Topic: Function Timestamp  (Read 12397 times)

0 Members and 1 Guest are viewing this topic.

This topic contains a post which is marked as Best Answer. Press here if you would like to see it.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Function Timestamp
« on: August 20, 2019, 12:55:30 am »
Code: QB64: [Select]
  1.     _LIMIT 10
  2.     LOCATE 1, 1
  3.     Countdown## = TimeStamp("01-01-2020", 0) - TimeStamp(DATE$, TIMER)
  4.     PRINT USING "###,###,###,###.### seconds till Jan 01, 2020"; Countdown##
  5. LOOP UNTIL Countdown## <= 0
  6. PRINT "HAPPY NEW YEARS!!!!"
  7.  
  8. FUNCTION TimeStamp## (d$, t##) 'date and timer
  9.     'Based on Unix Epoch time, which starts at year 1970.
  10.     'Minor fixes to leap year logic by Bplus.  Many thanks!
  11.     l = INSTR(d$, "-")
  12.     l1 = INSTR(l + 1, d$, "-")
  13.     m = VAL(LEFT$(d$, l))
  14.     d = VAL(MID$(d$, l + 1))
  15.     y = VAL(MID$(d$, l1 + 1)) - 1970
  16.     FOR i = 1 TO m
  17.         SELECT CASE i 'Add the number of days for each previous month passed
  18.             CASE 1: d = d 'January doestn't have any carry over days.
  19.             CASE 2, 4, 6, 8, 9, 11: d = d + 31
  20.             CASE 3: d = d + 28
  21.             CASE 5, 7, 10, 12: d = d + 30
  22.         END SELECT
  23.     NEXT
  24.     FOR i = 1 TO y
  25.         d = d + 365
  26.     NEXT
  27.     FOR i = 2 TO y - 1 STEP 4 'from 1972 onwards.
  28.         d = d + 1 'add an extra day for leap year every 4 years, starting in 1970
  29.     NEXT
  30.     IF y > 30 THEN d = d - 1 'for year 2000, which wasn't a leap year.
  31.     IF m > 2 AND (y MOD 4 = 2) THEN d = d + 1 'fix for leap year, for the current year.
  32.     s~&& = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  33.     TimeStamp## = (s~&& + t##)

Function gives a timestamp based on Unix Epoch Time, when we give it a date a time.  Simple enough, and thanks go to Bplus for helping catch a logic error in the leap year calculations. 
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Function Timestamp
« Reply #1 on: August 20, 2019, 06:21:44 am »
Thanks for making a new thread about this 'cause my time is so limited I got scared of losing this in the next few days - and the Librarian has even less time than *me*, so thanks again!

I can see that this code should replace the existing code in the Toolbox, no problem... But without studying the issue at all, I have a naive question - why did the function pass the test that led to the (obligatory) screenshot? Adjusting for local time it seemed correct - even when compared to the freebasic timecore and the one i translated for sxript. We all agreed on that test. Was the bug deep enough that only certain dates would trigger it? (Sorry, didn't read into this at all yet.)
You're not done when it works, you're done when it's right.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Function Timestamp
« Reply #2 on: August 20, 2019, 06:37:40 am »
Code: [Select]

FUNCTION ExtendedTimer##
    d$ = DATE$
    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)) - 1970
    FOR i = 1 TO m
        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: d = d + 28
            CASE 5, 7, 10, 12: d = d + 30
        END SELECT
    NEXT
    FOR i = 1 TO y
        d = d + 365
    NEXT
    FOR i = 2 TO y STEP 4
        IF m > 2 THEN d = d + 1 'add an extra day for leap year every 4 years, starting in 1970
    NEXT
    d = d - 1 'for year 2000
    s~&& = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
    ExtendedTimer## = (s~&& + TIMER)
END FUNCTION
 

The glitch was in this little line of code:
IF m > 2 THEN d = d + 1 'add an extra day for leap year every 4 years, starting in 1970

So if January and February, we didn’t add a day for each leap year...

The fix is as in the first post above; let the loop go to Year - 1 (don’t check the current year yet), and simply add one for each leap year.  Then check to see if the current year is leap year, and if so, is it after februrary where we need to count that extra day.

Note:  TimeStamp and ExtendedTimer are very similar functions, but slightly different,  TimeStamp lets you specify a time and date, creating a UET value for that moment; ExtendedTimer works exclusively with your current date and time, as a replacement for the TIMER command.



Corrected version of ExtendedTimer is as follows:

Code: QB64: [Select]
  1. SHELL "https://www.epochconverter.com/"
  2.     CLS
  3.     PRINT TIMER, INT(ExtendedTimer)
  4.     PRINT "Compare to the time at https://www.epochconverter.com/"
  5.     _DISPLAY
  6.     _LIMIT 10
  7.  
  8. FUNCTION ExtendedTimer##
  9.     d$ = DATE$
  10.     l = INSTR(d$, "-")
  11.     l1 = INSTR(l + 1, d$, "-")
  12.     m = VAL(LEFT$(d$, l))
  13.     d = VAL(MID$(d$, l + 1))
  14.     y = VAL(MID$(d$, l1 + 1)) - 1970
  15.     FOR i = 1 TO m
  16.         SELECT CASE i 'Add the number of days for each previous month passed
  17.             CASE 1: d = d 'January doestn't have any carry over days.
  18.             CASE 2, 4, 6, 8, 9, 11: d = d + 31
  19.             CASE 3: d = d + 28
  20.             CASE 5, 7, 10, 12: d = d + 30
  21.         END SELECT
  22.     NEXT
  23.     FOR i = 1 TO y
  24.         d = d + 365
  25.     NEXT
  26.     FOR i = 2 TO y - 1 STEP 4 'from 1972 onwards, skipping the current year.
  27.         IF i <> 30 THEN d = d + 1 'add an extra day for leap year every 4 years, starting in 1970, but skip year 2000
  28.     NEXT
  29.     IF m > 2 AND (y MOD 4 = 2) THEN d = d + 1 'fix for leap year, for the current year.
  30.     s~&& = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  31.     ExtendedTimer## = (s~&& + TIMER)
  32.  

Or, for the minimalist folks out there, if they like TimeStamp, they could also have a quick version for:

Code: QB64: [Select]
  1. FUNCTION ExtendedTimer##
  2.     ExtendedTimer## = TimeStamp(DATE$, TIMER)
« Last Edit: August 20, 2019, 10:58:00 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Function Timestamp
« Reply #3 on: August 20, 2019, 06:46:37 am »
Alright -

So bplus hinted that this bug propagated out of the Toolbox somehow - does the Extended Timer code need to be updated? If it did something so wrong with leap years, then why did the screenshot check out?

Or is that code fine as-is?

Gotta fly - I won't be able to hit this again for 12 hours so...
You're not done when it works, you're done when it's right.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Function Timestamp
« Reply #4 on: August 20, 2019, 06:54:18 am »
Alright -

So bplus hinted that this bug propagated out of the Toolbox somehow - does the Extended Timer code need to be updated? If it did something so wrong with leap years, then why did the screenshot check out?

Or is that code fine as-is?

Gotta fly - I won't be able to hit this again for 12 hours so...

The screenshot checks out because we're currently past the 2nd month...

IF m > 2 THEN d = d + 1   ...  We didn't need that IF check in there; we needed one after for the current year only.

Change is to replace:
Code: [Select]
    FOR i = 2 TO y STEP 4
        IF m > 2 THEN d = d + 1 'add an extra day for leap year every 4 years, starting in 1970
    NEXT
    d = d - 1 'for year 2000

With:
Code: [Select]
    FOR i = 2 TO y - 1 STEP 4 'from 1972 onwards, skipping the current year.
        IF i <> 30 THEN d = d + 1 'add an extra day for leap year every 4 years, starting in 1970, but skip year 2000
    NEXT
    IF m > 2 AND (y MOD 4 = 2) THEN d = d + 1 'fix for leap year, for the current year.

As long as your current month was > 2, the existing code worked out right.  You just suddenly lost out on a bunch of leap days when Jan/Feb rolled around, to gain them all back once we were March again.  ;)
« Last Edit: August 20, 2019, 10:58:42 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Function Timestamp
« Reply #5 on: August 20, 2019, 09:51:41 am »
Steve, maybe you mean if i <> 30 for this part:
Code: QB64: [Select]
  1.     FOR i = 2 TO y - 1 STEP 4 'from 1972 onwards, skipping the current year.
  2.         'IF i <> 30....
  3.         IF y <> 30 THEN d = d + 1 'add an extra day for leap year every 4 years, starting in 1970, but skip year 2000
  4.     NEXT
  5.  

This could be replaced by: d = d + y * 365  ' y or y -1 ?????????? OK y I think
Code: QB64: [Select]
  1.     FOR i = 1 TO y  'y-1???? first 1970 y = 0, 1971 y = 1, 1972 y = 2  OK y not y-1
  2.         d = d + 365
  3.     NEXT
  4.  

Update:
but now, I am wondering should't this be to y-1 as we count the days in y the partial year separately.
EDIT Update: No y starts at 0 not 1, so not y-1...

OK I am writing a tester for this Function. Starting at 01-01-1970, 0 time should be 0
« Last Edit: August 20, 2019, 10:51:40 am by bplus »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Function Timestamp
« Reply #6 on: August 20, 2019, 12:06:26 pm »
Update: If timestamp##("01-01-1970", 0) should be 0, it is a whole day off.

This probably offsets all time##, so when compare differences in date and time will be OK but calc of absolute seconds from 01-01-1970 will be off a day = 86400 secs.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Function Timestamp
« Reply #7 on: August 20, 2019, 12:38:28 pm »
Update: If timestamp##("01-01-1970", 0) should be 0, it is a whole day off.

This probably offsets all time##, so when compare differences in date and time will be OK but calc of absolute seconds from 01-01-1970 will be off a day = 86400 secs.

And, to think, we could avoid all these issues if we just used c++ to give us the solution in one line: 
Code: C++: [Select]
  1. double now = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();

There’s a page here which can be used to compare results: https://www.epochconverter.com/

Somehow, I was matching results before; how the heck did I break things so we’re off so badly now?  I’ll dig into it later this evening when my time frees up a little again.  ;)

(And I’ll probably add that single line into my repo and just make an _EXTENDEDTIMER command and use it.  I imagine it’d be much faster than doing all the calculations manually anyway.). ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Function Timestamp
« Reply #8 on: August 20, 2019, 02:46:37 pm »
Quote
There’s a page here which can be used to compare results: https://www.epochconverter.com/

OK it does look like 0 AM Jan 1, 1970 is suppose to be 0 at GMT:
 
01-01-1970 0 AM GMT.PNG


So far minimal testing subtracting 1 from d, just after it is calc'd from date$. OK

Kinda makes sense to subtract a day from the date because it isn't complete yet, the fraction of it is in the time.
« Last Edit: August 20, 2019, 02:58:52 pm by bplus »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Function Timestamp
« Reply #9 on: August 20, 2019, 08:46:48 pm »
Alert!

 I caught another bug on day counts and I think I have worked up a fix, leap year again or rather not a leap year this time! Be right back! Still running checks with my code tester.

Good that was the last bug for day counts anyway.

The last bug was about checking for leap year for y. We forgot to consider if y is year 2000 which is NOT a leap year.

Here is my test code that tests day counts from 01-01-1970 to 01-01-2035 by comparing the day count current test date to the day count for the test date for day before, if the day count <> last day count + 1 the code stops running on the error producing date.
Code: QB64: [Select]
  1. _TITLE "Test TimeStamp##" 'b+ started 2019-08-20
  2. ' Steve's TimeStamp## even better than Toolbox tool!
  3.  
  4. d = 0: m = 1: yr = 1970
  5.     d = d + 1
  6.     IF d = 29 AND m = 2 THEN
  7.         'is it leap year
  8.         IF yr - 1970 = 30 THEN
  9.             m = 3: d = 1
  10.         ELSEIF ((yr - 1970) MOD 4 = 2) THEN
  11.             'd is ok
  12.         ELSE
  13.             m = 3: d = 1
  14.         END IF
  15.     ELSEIF d = 30 AND m = 2 THEN 'must be leap year
  16.         m = 3: d = 1
  17.     ELSEIF d = 31 THEN
  18.         IF m = 4 OR m = 6 OR m = 9 OR m = 11 THEN
  19.             m = m + 1: d = 1
  20.         END IF
  21.     ELSEIF d = 32 THEN
  22.         IF m = 12 THEN
  23.             d = 1: m = 1: yr = yr + 1
  24.         ELSE
  25.             d = 1: m = m + 1
  26.         END IF
  27.     END IF
  28.     d2$ = RIGHT$("0" + _TRIM$(STR$(m)), 2) + "-" + RIGHT$("0" + _TRIM$(STR$(d)), 2) + "-" + _TRIM$(STR$(yr))
  29.     d1$ = "01-01-1970"
  30.  
  31.     a## = TimeStamp##(d1$, 5)
  32.     b## = TimeStamp##(d2$, 5)
  33.     moda## = BmodTimeStamp##(d1$, 5)
  34.     modb## = BmodTimeStamp##(d2$, 5)
  35.  
  36.     dab& = (a## - b##) \ 1
  37.     PRINT dab&
  38.     IF dab& < 0 THEN
  39.         flagNeg = -1: dab& = -dab&
  40.     ELSE
  41.         flagNeg = 0
  42.     END IF
  43.     days% = dab& \ (86400)
  44.     dab& = dab& - days% * 86400
  45.     hours% = dab& \ 3600
  46.     dab& = dab& - hours% * 3600
  47.     minutes% = dab& \ 60
  48.     secs% = dab& - minutes% * 60
  49.  
  50.     moddab& = (moda## - modb##) \ 1
  51.  
  52.     IF moddab& < 0 THEN
  53.         modflagNeg = -1: moddab& = -moddab&
  54.     ELSE
  55.         modflagNeg = 0
  56.     END IF
  57.     moddays% = moddab& \ (86400)
  58.     moddab& = moddab& - moddays% * 86400
  59.     modhours% = moddab& \ 3600
  60.     moddab& = moddab& - modhours% * 3600
  61.     modminutes% = moddab& \ 60
  62.     modsecs% = moddab& - modminutes% * 60
  63.  
  64.     CLS
  65.     PRINT "The two dates A and B: "; d1$, d2$
  66.     PRINT
  67.     'PRINT "Updated 2019-08-20 10:58 AM if i <> 30 fix from if y <> 30"
  68.     PRINT "Steve TimeStamp## Function:"
  69.     PRINT "A##, B## TimeStamps: "; a##, b##
  70.     PRINT "Difference between A## and B##:"; days%; "days,"; hours%; "hours,"; minutes%; "minutes,"; secs%; "secs, ";
  71.     IF flagNeg THEN
  72.         PRINT "B## was later than A##"
  73.     ELSE
  74.         PRINT "A## was later than B##"
  75.     END IF
  76.  
  77.     PRINT: PRINT "B mods compare:"
  78.     PRINT "mod A##, mod B## TimeStamps: "; moda##, modb##
  79.     PRINT "Difference between mod A## and mod B##:"; moddays%; "days,"; modhours%; "hours,"; modminutes%; "minutes,"; modsecs%; "secs, ";
  80.     IF modflagNeg THEN
  81.         PRINT "mod B## was later than mod A##"
  82.     ELSE
  83.         PRINT "mod A## was later than mod B##"
  84.     END IF
  85.     PRINT
  86.     PRINT "DayCount this loop: "; moddays%, "DayCount last loop:"; lastDay%
  87.  
  88.     'INPUT "OK ... press enter "; w$
  89.     IF lastDay% <> 0 THEN
  90.         IF moddays% <> lastDay% + 1 THEN END
  91.     END IF
  92.     lastDay% = moddays%
  93.     _DISPLAY
  94.     _LIMIT 1000
  95. LOOP UNTIL yr = 2035
  96. PRINT "Done!"
  97.  
  98. ' b mod TimeStamp fixed. 2019-08-20 twiddled somemore and tested with code above
  99. FUNCTION BmodTimeStamp## (d$, t##) 'date and timer
  100.     DIM l AS INTEGER, l1 AS INTEGER, i AS INTEGER, s~&&
  101.     DIM m AS INTEGER, y AS INTEGER, d AS INTEGER
  102.     l = INSTR(d$, "-")
  103.     l1 = INSTR(l + 1, d$, "-")
  104.     m = VAL(LEFT$(d$, l))
  105.     d = VAL(MID$(d$, l + 1)) - 1 '<<<<<<<<<< the day of date is partial, so count of full days is 1 less!!
  106.     y = VAL(MID$(d$, l1 + 1)) - 1970
  107.     FOR i = 1 TO m 'count up days for year y
  108.         SELECT CASE i 'Add the number of days for each previous month passed, case 1 no change needed <<<<<<<<<<< removed case 1 line
  109.             CASE 2, 4, 6, 8, 9, 11: d = d + 31
  110.             CASE 3: d = d + 28
  111.             CASE 5, 7, 10, 12: d = d + 30
  112.         END SELECT
  113.     NEXT
  114.     d = d + 365 * y ' <<<<<<<<<<<<<<< Steve uses loop number of days before counting leap years
  115.     FOR i = 2 TO y - 1 STEP 4 ' to y-1 not y, leap years 1972, 1976, 1980... 2020 but not 2000
  116.         IF i <> 30 THEN d = d + 1 'fix for year 2000
  117.     NEXT
  118.     IF y <> 30 THEN '!!!!!!!!!!!!!!!!!! Unless the current year is 2000 !!!!!!!!!!!!!
  119.         IF m > 2 AND (y MOD 4 = 2) THEN d = d + 1 'fix leap year adjustment for year y
  120.     END IF
  121.     s~&& = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  122.     BmodTimeStamp## = (s~&& + t##)
  123.  
  124.  
  125. 'https://www.qb64.org/forum/index.php?topic=1638.msg108566#msg108566
  126. 'Steve last edit 10:58 AM 2019-08-20
  127. FUNCTION TimeStamp## (d$, t##) 'date and timer
  128.     'Based on Unix Epoch time, which starts at year 1970.
  129.     'Minor fixes to leap year logic by Bplus.  Many thanks!
  130.     l = INSTR(d$, "-")
  131.     l1 = INSTR(l + 1, d$, "-")
  132.     m = VAL(LEFT$(d$, l))
  133.     d = VAL(MID$(d$, l + 1))
  134.     y = VAL(MID$(d$, l1 + 1)) - 1970
  135.     FOR i = 1 TO m
  136.         SELECT CASE i 'Add the number of days for each previous month passed
  137.             CASE 1: d = d 'January doestn't have any carry over days.
  138.             CASE 2, 4, 6, 8, 9, 11: d = d + 31
  139.             CASE 3: d = d + 28
  140.             CASE 5, 7, 10, 12: d = d + 30
  141.         END SELECT
  142.     NEXT
  143.     FOR i = 1 TO y
  144.         d = d + 365
  145.     NEXT
  146.     FOR i = 2 TO y - 1 STEP 4 'from 1972 onwards, skipping the current year.
  147.         IF i <> 30 THEN d = d + 1 'add an extra day for leap year every 4 years, starting in 1970, but skip year 2000
  148.     NEXT
  149.  
  150.     'unless the current year is 2000!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1
  151.     IF y <> 30 THEN
  152.         IF m > 2 AND (y MOD 4 = 2) THEN d = d + 1 'fix for leap year, for the current year.
  153.     END IF
  154.  
  155.     s~&& = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  156.     TimeStamp## = (s~&& + t##)
  157.  
  158.  
It stopped twice while I was testing. The first time because I didn't increment days for leap years correctly because I was using a different year formula, March 1, 1972, the 2nd time caught the bug for checking the y year if 2000, March 1, 2000. Hey Y2K bug strikes again!

The two TimeStamp## functions produce different results:

BmodTimeStamp## has 0 for date 01-01-1970 0 AM, because I subtract 1 from d calculated off Date$, that Day should not count as a full day. Also all the sub variables are DIM and the loop counting Days up is a one liner formula: d = d + 365 * y. I also removed CASE 1: d = d because it wasn't needed.

Steve's TimeStamp## function as of 11 AM this morning is exactly a days worth of seconds more for 01-01-1970 0 AM and I have fixed the same for the 2000 no leap year bug.

I think the BmodTimeStamp## is the more accrate.



« Last Edit: August 20, 2019, 09:33:54 pm by bplus »

Offline petoro

  • Newbie
  • Posts: 27
    • View Profile
Re: Function Timestamp
« Reply #10 on: August 21, 2019, 04:40:02 am »
In fact 2000 WAS a leap year as usual. It is because it was divisible but 100 BUT ALSO by 400!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Function Timestamp
« Reply #11 on: August 21, 2019, 04:57:34 am »
You're still glitching bplus...

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(640, 640, 32)
  2. FOR i = 1970 TO 2005
  3.  
  4.     PRINT i, BmodTimeStamp##("03-01-" + STR$(i), 0), TimeStamp("03-01-" + STR$(i), 0);
  5.     IF i = 2000 THEN PRINT " <== GLITCH STARTING" ELSE PRINT
  6.  
  7.  
  8. ' b mod TimeStamp fixed. 2019-08-20 twiddled somemore and tested with code above
  9. FUNCTION BmodTimeStamp## (d$, t##) 'date and timer
  10.     DIM l AS INTEGER, l1 AS INTEGER, i AS INTEGER, s~&&
  11.     DIM m AS INTEGER, y AS INTEGER, d AS INTEGER
  12.     l = INSTR(d$, "-")
  13.     l1 = INSTR(l + 1, d$, "-")
  14.     m = VAL(LEFT$(d$, l))
  15.     d = VAL(MID$(d$, l + 1)) - 1 '<<<<<<<<<< the day of date is partial, so count of full days is 1 less!!
  16.     y = VAL(MID$(d$, l1 + 1)) - 1970
  17.     FOR i = 1 TO m 'count up days for year y
  18.         SELECT CASE i 'Add the number of days for each previous month passed, case 1 no change needed <<<<<<<<<<< removed case 1 line
  19.             CASE 2, 4, 6, 8, 9, 11: d = d + 31
  20.             CASE 3: d = d + 28
  21.             CASE 5, 7, 10, 12: d = d + 30
  22.         END SELECT
  23.     NEXT
  24.     d = d + 365 * y ' <<<<<<<<<<<<<<< Steve uses loop number of days before counting leap years
  25.     FOR i = 2 TO y - 1 STEP 4 ' to y-1 not y, leap years 1972, 1976, 1980... 2020 but not 2000
  26.         IF i <> 30 THEN d = d + 1 'fix for year 2000
  27.     NEXT
  28.     IF y <> 30 THEN '!!!!!!!!!!!!!!!!!! Unless the current year is 2000 !!!!!!!!!!!!!
  29.         IF m > 2 AND (y MOD 4 = 2) THEN d = d + 1 'fix leap year adjustment for year y
  30.     END IF
  31.     s~&& = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  32.     BmodTimeStamp## = (s~&& + t##)
  33.  
  34. FUNCTION TimeStamp## (d$, t##) 'date and timer
  35.     'Based on Unix Epoch time, which starts at year 1970.
  36.     IF y < 1970 THEN TimeStamp## = -1 'a failure message, as there was no Unix Epoch Time before Jan 01, 1970.
  37.     l = INSTR(d$, "-")
  38.     l1 = INSTR(l + 1, d$, "-")
  39.     m = VAL(LEFT$(d$, l))
  40.     d = VAL(MID$(d$, l + 1))
  41.     y = VAL(MID$(d$, l1 + 1)) - 1970
  42.     FOR i = 1 TO m 'for this year,
  43.         SELECT CASE i 'Add the number of days for each previous month passed
  44.             CASE 1: d = d 'January doestn't have any carry over days.
  45.             CASE 2, 4, 6, 8, 9, 11: d = d + 31
  46.             CASE 3 'Feb might be a leap year
  47.                 IF (y MOD 4) = 2 THEN 'if this year is divisible by 4 (starting in 1972)
  48.                     d = d + 29 'its a leap year
  49.                     IF (y MOD 100) = 30 AND (y MOD 400) <> 30 THEN 'unless..
  50.                         d = d - 1 'the year is divisible by 100, and not divisible by 400
  51.                     END IF
  52.                 ELSE 'year not divisible by 4, no worries
  53.                     d = d + 28
  54.                 END IF
  55.             CASE 5, 7, 10, 12: d = d + 30
  56.         END SELECT
  57.     NEXT
  58.     d = (d - 1) + 365 * y 'current month days passed + 365 days per each standard year
  59.     FOR i = 2 TO y - 1 STEP 4 'from 1972 onwards, skipping the current year (which we handled previously in the FOR loopp)
  60.         d = d + 1 'add an extra day every leap year
  61.         IF (i MOD 100) = 30 AND (i MOD 400) <> 30 THEN d = d - 1 'but skiping every year divisible by 100, but not 400
  62.     NEXT
  63.     s~&& = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  64.     TimeStamp## = (s~&& + t##)

With the original ExtendedTimer on the forums here, I was wondering WHY we got the correct answer, without subtracting a day from our date entry.  It's because we were completely handling the leap years wrongly to begin with... The year 2000 *IS* a leap year, so by taking a day off for it (when we shouldn't have), it shifted the answer back a day, giving us the correct time for TODAY...

Which ended up making it one of those glitches which actually fixed another glitch, which ended up giving the proper result when it shouldn't have! Testing it as a TimeStamp, which allows us to flow up and down to any particular date, allowed us to finally showcase the glitch and make adjustments to it.

Unfortunately, by correcting one glitch (subtracting one from the day), you've now left the second glitch to run without correction (the false 2000 leap year subtraction), meaning things are still just as broken as ever!

I'm thinking the above fixes all issues and gets us up to EUT compliance, but test it out for yourself and see what you think.  If it passes muster this time around, I'll redo ExtendedTimer to be correct as well, and we'll have STx correct the code in the toolbox.

The toolbox ExtendedTimer gives us the right result just because it's wrong at being wrong!  0_o!!
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Function Timestamp
« Reply #12 on: August 21, 2019, 04:59:11 am »
In fact 2000 WAS a leap year as usual. It is because it was divisible but 100 BUT ALSO by 400!

I was correcting that as you posted.  I think the latest version here takes all those subtle variations into account:

Code: QB64: [Select]
  1. FUNCTION TimeStamp## (d$, t##) 'date and timer
  2.     'Based on Unix Epoch time, which starts at year 1970.
  3.     IF y < 1970 THEN TimeStamp## = -1 'a failure message, as there was no Unix Epoch Time before Jan 01, 1970.
  4.     l = INSTR(d$, "-")
  5.     l1 = INSTR(l + 1, d$, "-")
  6.     m = VAL(LEFT$(d$, l))
  7.     d = VAL(MID$(d$, l + 1))
  8.     y = VAL(MID$(d$, l1 + 1)) - 1970
  9.     FOR i = 1 TO m 'for this year,
  10.         SELECT CASE i 'Add the number of days for each previous month passed
  11.             CASE 1: d = d 'January doestn't have any carry over days.
  12.             CASE 2, 4, 6, 8, 9, 11: d = d + 31
  13.             CASE 3 'Feb might be a leap year
  14.                 IF (y MOD 4) = 2 THEN 'if this year is divisible by 4 (starting in 1972)
  15.                     d = d + 29 'its a leap year
  16.                     IF (y MOD 100) = 30 AND (y MOD 400) <> 30 THEN 'unless..
  17.                         d = d - 1 'the year is divisible by 100, and not divisible by 400
  18.                     END IF
  19.                 ELSE 'year not divisible by 4, no worries
  20.                     d = d + 28
  21.                 END IF
  22.             CASE 5, 7, 10, 12: d = d + 30
  23.         END SELECT
  24.     NEXT
  25.     d = (d - 1) + 365 * y 'current month days passed + 365 days per each standard year
  26.     FOR i = 2 TO y - 1 STEP 4 'from 1972 onwards, skipping the current year (which we handled previously in the FOR loopp)
  27.         d = d + 1 'add an extra day every leap year
  28.         IF (i MOD 100) = 30 AND (i MOD 400) <> 30 THEN d = d - 1 'but skiping every year divisible by 100, but not 400
  29.     NEXT
  30.     s~&& = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  31.     TimeStamp## = (s~&& + t##)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Function Timestamp
« Reply #13 on: August 21, 2019, 05:41:46 am »
And if you notice, I just toss an error value for when we end up below 1970 and look for a timestamp.  At that point, we have to count time in reverse to generate negative numbers, and I just didn't feel like putting in the effort for it.  :P

DEC 31, 1969, 23:59:59 = -1
DEC 31, 1969, 23:59:58 = -2
... and so on.

With the medications I'm on, I'm surprised I managed to count forward to get to the point where it's currently working properly; I sure don't feel like making it count backwards at the moment.  I leave that mod to Bplus, if he feels like adding it, otherwise positive timestamps are good enough for my needs...  ;)
« Last Edit: August 21, 2019, 05:43:56 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Function Timestamp
« Reply #14 on: August 21, 2019, 06:20:08 am »
Thanks for your diligence boys. I'll let this topic settle down for a short bit before updating our curated code. I unfortunately don't have the most time in the world to follow all the posts so preemptive apologies if I ask for something in a few days that should have been obvious to me.
« Last Edit: August 21, 2019, 06:28:31 am by STxAxTIC »
You're not done when it works, you're done when it's right.