Author Topic: Function Timestamp  (Read 14395 times)

0 Members and 1 Guest are viewing this topic.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Function Timestamp
« Reply #30 on: August 24, 2019, 10:48:20 pm »
Okies...  I have no idea what's going on, but somebody out there is screwing with me.  (Or else my medication is really screwing with me.  The function works, as posted, except the signs are backwards?!!)  How the BLEEP did I manage to screw up that perfectly???!!

Code: QB64: [Select]
  1. _TITLE "Test Steve timeStamp" 'B+ test 2019-08-24
  2. '2019-08-24 Quick test Steve timestamp by comparing DiffDateTime$ to TimeStampDiff$
  3. 'I am just testing day counts
  4.  
  5. SCREEN _NEWIMAGE(600, 760, 32)
  6. times$ = "00:00:00": times## = 0
  7.  
  8. dates$(3) = "08-24-2019": dates$(2) = "01-01-2020": dates$(1) = "01-01-2021": dates$(0) = "01-01-2025"
  9. dates$(4) = "01-01-2019": dates$(5) = "01-01-1970": dates$(6) = "01-01-1815"
  10.  
  11. FOR i = 0 TO 5
  12.     FOR j = i + 1 TO 6
  13.         PRINT dates$(i); " Minus "; dates$(j)
  14.         PRINT DiffDateTime$(DateTime$(dates$(i), times$), DateTime$(dates$(j), times$))
  15.         PRINT TimeStampDiff$(dates$(i), time##, dates$(j), time##)
  16.         PRINT
  17.     NEXT
  18.  
  19.     PRINT
  20.     PRINT
  21.     PRINT "UET for 01-01-1915:"; TimeStamp("01-01-1915", 0)
  22.     PRINT
  23.     PRINT
  24.     INPUT "OK ... press enter "; w$
  25.     CLS
  26.  
  27.  
  28.  
  29.  
  30. d1970$ = "01-01-1970"
  31. FOR y = 1970 TO 1950 STEP -1
  32.     d$ = "01-01-" + _TRIM$(STR$(y))
  33.     PRINT d1970$; " Minus "; d$
  34.     PRINT DiffDateTime$(DateTime$(d1970$, times$), DateTime$(d$, times$))
  35.     PRINT TimeStampDiff$(d1970$, time##, d$, time##)
  36.     PRINT
  37.     IF y MOD 5 = 0 THEN
  38.         INPUT "OK ... press enter "; w$
  39.         CLS
  40.     END IF
  41.  
  42. PRINT TimeStamp("01-01-1915", 0)
  43.  
  44.  
  45.  
  46.  
  47. ' I used this code for Count Down 2020
  48. 'note: this uses 2 dates and times formatted with DateTime$() Function
  49. FUNCTION DiffDateTime$ (LaterDate$, MinusEarlierDate$)
  50.     es = VAL(MID$(MinusEarlierDate$, 18, 2))
  51.     ls = VAL(MID$(LaterDate$, 18, 2))
  52.  
  53.     em = VAL(MID$(MinusEarlierDate$, 15, 2))
  54.     lm = VAL(MID$(LaterDate$, 15, 2))
  55.  
  56.     eh = VAL(MID$(MinusEarlierDate$, 12, 2))
  57.     lh = VAL(MID$(LaterDate$, 12, 2))
  58.  
  59.     ed = VAL(MID$(MinusEarlierDate$, 9, 2))
  60.     ld = VAL(MID$(LaterDate$, 9, 2))
  61.  
  62.     emm = VAL(MID$(MinusEarlierDate$, 6, 2))
  63.     lmm = VAL(MID$(LaterDate$, 6, 2))
  64.  
  65.     ey = VAL(MID$(MinusEarlierDate$, 1, 4))
  66.     ly = VAL(MID$(LaterDate$, 1, 4))
  67.  
  68.     IF es > ls THEN ls = ls + 60: lm = lm - 1
  69.     DiffDateTime$ = STR$(ls - es) + " secs"
  70.  
  71.     IF em > lm THEN lm = lm + 60: lh = lh - 1
  72.     DiffDateTime$ = STR$(lm - em) + " mins" + DiffDateTime$
  73.  
  74.     IF eh > lh THEN lh = lh + 24: ld = ld - 1
  75.     DiffDateTime$ = STR$(lh - eh) + " hours" + DiffDateTime$
  76.  
  77.     ''did we barrow off day
  78.     IF ld = 0 THEN
  79.         SELECT CASE lmm
  80.             CASE 1: ly = ly - 1: lmm = 12: ld = 31
  81.             CASE 3: IF IsLeapYear(ly) = 1 THEN lmm = 2: ld = 29 ELSE lmm = 2: ld = 28
  82.             CASE 5, 7, 8, 10, 12: lmm = lmm - 1: ld = 30
  83.             CASE 2, 4, 6, 9, 11: lmm = lmm - 1: ld = 31
  84.         END SELECT
  85.     END IF
  86.     'PRINT ly, lmm, ld   ' check target and start
  87.     'PRINT ey, emm, ed
  88.  
  89.     'now count days
  90.     DO
  91.         IF ly = ey THEN
  92.             IF lmm = emm THEN
  93.                 IF ld = ed THEN EXIT DO '  all date numbers match, done calc
  94.             END IF
  95.         END IF
  96.         ed = ed + 1
  97.         cnt = cnt + 1
  98.         IF ed = 29 AND emm = 2 THEN
  99.             'is it leap year
  100.             IF IsLeapYear%(ey) = 1 THEN
  101.                 'pass
  102.             ELSE
  103.                 emm = 3: ed = 1
  104.             END IF
  105.         ELSEIF ed = 30 AND emm = 2 THEN 'must be leap year
  106.             emm = 3: ed = 1
  107.         ELSEIF ed = 31 THEN
  108.             IF emm = 4 OR emm = 6 OR emm = 9 OR emm = 11 THEN
  109.                 emm = emm + 1: ed = 1
  110.             END IF
  111.         ELSEIF ed = 32 THEN
  112.             IF emm = 12 THEN
  113.                 ed = 1: emm = 1: ey = ey + 1
  114.             ELSE
  115.                 ed = 1: emm = emm + 1
  116.             END IF
  117.         END IF
  118.         'PRINT ey, emm, ed, ly; " "; lmm; " "; ld   'Check progress
  119.         '_LIMIT 2
  120.     LOOP
  121.     DiffDateTime$ = _TRIM$(STR$(cnt)) + " Days and" + DiffDateTime$
  122.  
  123. FUNCTION CurrentDateTime$
  124.     CurrentDateTime$ = "    -  -  _  :  :  "
  125.     MID$(CurrentDateTime$, 1, 4) = MID$(DATE$, 7, 4)
  126.     MID$(CurrentDateTime$, 6, 5) = MID$(DATE$, 1, 5)
  127.     MID$(CurrentDateTime$, 12, 8) = TIME$
  128.  
  129. FUNCTION DateTime$ (mm_dd_yy$, hh_mm_ss$)
  130.     DateTime$ = "    -  -  _  :  :  "
  131.     MID$(DateTime$, 1, 4) = MID$(mm_dd_yy$, 7, 4)
  132.     MID$(DateTime$, 6, 5) = MID$(mm_dd_yy$, 1, 5)
  133.     MID$(DateTime$, 12, 8) = hh_mm_ss$
  134.  
  135. FUNCTION IsLeapYear% (yr) 'mod from Pete's calendar this is a very clear calc
  136.     IF yr MOD 4 = 0 THEN
  137.         IF yr MOD 100 = 0 THEN
  138.             IF yr MOD 400 = 0 THEN IsLeapYear = 1
  139.         ELSE
  140.             IsLeapYear = 1
  141.         END IF
  142.     END IF
  143.  
  144. 'this calc date time diff using Steve' TimeStamp## secs
  145. FUNCTION TimeStampDiff$ (LaterDate$, LaterTime##, EarlierDate$, EarlierTime##)
  146.     secLate## = TimeStamp##(LaterDate$, LaterTime##)
  147.     secEarly## = TimeStamp##(EarlierDate$, EarlierTime##)
  148.     secDiff = secLate## - secEarly##
  149.     'covert to days, hours, minutes, secs I am dropping fraction secs for test
  150.     days = INT(secDiff / 86400)
  151.     secDiff = secDiff - days * 86400
  152.     hours = INT(secDiff / 3600)
  153.     secDiff = secDiff - hours * 3600
  154.     minutes = INT(secDiff / 60)
  155.     secs = INT(secDiff - minutes * 60)
  156.     TimeStampDiff$ = _TRIM$(STR$(days)) + " Days and " + _TRIM$(STR$(hours)) + " Hours, " + _TRIM$(STR$(minutes)) + " Minutes, " + _TRIM$(STR$(secs))
  157.  
  158. ' Steve's latest version
  159. ' https://www.qb64.org/forum/index.php?topic=1638.msg108650#msg108650
  160. FUNCTION TimeStamp## (d$, t##) 'date and timer
  161.     'Based on Unix Epoch time, which starts at year 1970.
  162.  
  163.     m = VAL(LEFT$(d$, 2))
  164.     d = VAL(MID$(d$, 4, 2))
  165.     y = VAL(RIGHT$(d$, 4))
  166.     IF y < 1970 THEN 'calculate shit backwards
  167.         SELECT CASE m 'turn the day backwards for the month
  168.             CASE 1, 3, 5, 7, 8, 10, 12: d = 31 - d '31 days
  169.             CASE 2: d = 28 - d 'special 28 or 29.
  170.             CASE 4, 6, 9, 11: d = 30 - d '30 days
  171.         END SELECT
  172.         IF y MOD 4 = 0 AND m < 3 THEN 'check for normal leap year, and we're before it...
  173.             d = d + 1 'assume we had a leap year, subtract another day
  174.             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
  175.         END IF
  176.  
  177.         'then count the months that passed after the current month
  178.         FOR i = m + 1 TO 12
  179.             SELECT CASE i
  180.                 CASE 2: d = d + 28
  181.                 CASE 3, 5, 7, 8, 10, 12: d = d + 31
  182.                 CASE 4, 6, 9, 11: d = d + 30
  183.             END SELECT
  184.         NEXT
  185.  
  186.         'we should now have the entered year calculated.  Now lets add in for each year from this point to 1970
  187.         d = d + 365 * (1969 - y) '365 days per each standard year
  188.         FOR i = 1968 TO y + 1 STEP -4 'from 1968 onwards,backwards, skipping the current year (which we handled previously in the FOR loopp)
  189.             d = d + 1 'subtract an extra day every leap year
  190.             IF (i MOD 100) = 30 AND (i MOD 400) <> 30 THEN d = d - 1 'but skiping every year divisible by 100, but not 400
  191.         NEXT
  192.         s## = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  193.         TimeStamp## = -(s## + 24 * 60 * 60 - t##)
  194.         EXIT FUNCTION
  195.     ELSE
  196.         y = y - 1970
  197.     END IF
  198.  
  199.     FOR i = 1 TO m 'for this year,
  200.         SELECT CASE i 'Add the number of days for each previous month passed
  201.             CASE 1: d = d 'January doestn't have any carry over days.
  202.             CASE 2, 4, 6, 8, 9, 11: d = d + 31
  203.             CASE 3 'Feb might be a leap year
  204.                 IF (y MOD 4) = 2 THEN 'if this year is divisible by 4 (starting in 1972)
  205.                     d = d + 29 'its a leap year
  206.                     IF (y MOD 100) = 30 AND (y MOD 400) <> 30 THEN 'unless..
  207.                         d = d - 1 'the year is divisible by 100, and not divisible by 400
  208.                     END IF
  209.                 ELSE 'year not divisible by 4, no worries
  210.                     d = d + 28
  211.                 END IF
  212.             CASE 5, 7, 10, 12: d = d + 30
  213.         END SELECT
  214.     NEXT
  215.     d = (d - 1) + 365 * y 'current month days passed + 365 days per each standard year
  216.     FOR i = 2 TO y - 1 STEP 4 'from 1972 onwards, skipping the current year (which we handled previously in the FOR loopp)
  217.         d = d + 1 'add an extra day every leap year
  218.         IF (i MOD 100) = 30 AND (i MOD 400) <> 30 THEN d = d - 1 'but skiping every year divisible by 100, but not 400
  219.     NEXT
  220.     s## = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  221.     TimeStamp## = (s## + t##)

According to the website (and screenshot attached), the above is now calculating the dates properly. 

All it took was to change the signs in this area to + instead of -:
Code: [Select]
        '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

Now, how the flip did I manage to post it with the signs reversed?  And how the flippy flip did I manage to get it sooo close to working, and yet never catch that the results were completely screwed up?  All I can say is, "I'm damn talented!!"

My times are matching the official times, but yours are now off by a day.
Screenshot.jpg
* Screenshot.jpg (Filesize: 119.09 KB, Dimensions: 1092x686, Views: 472)
« Last Edit: August 24, 2019, 11:10:40 pm 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 #31 on: August 24, 2019, 11:05:47 pm »
Wow that is way better, but leap years look like they are off 2 days, as if instead of adding one one is subtracted or vice versa...

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Function Timestamp
« Reply #32 on: August 24, 2019, 11:09:40 pm »
Wow that is way better, but leap years look like they are off 2 days, as if instead of adding one one is subtracted or vice versa...

Stupid sign was reversed on them as well.  /sigh. 

Got any explanation on how the heck that could happen??
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 #33 on: August 24, 2019, 11:25:08 pm »
Stupid sign was reversed on them as well.  /sigh. 

Got any explanation on how the heck that could happen??

Your evil twin came in a sabotaged your work?

Meds? seems a pretty intelligent and conscious act for meds but I've heard of people driving when sleep walking.

Enemies with forum personnel? Don't even want to go there...

If this is what happened it is truly weird!!

What kind of books do you write? I was just looking tonight when I read your Repo post. I found a Steve McNeill author of GameOn and Rebirth, that you?
« Last Edit: August 24, 2019, 11:27:48 pm by bplus »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Function Timestamp
« Reply #34 on: August 24, 2019, 11:39:34 pm »
What kind of books do you write? I was just looking tonight when I read your Repo post. I found a Steve McNeill author of GameOn and Rebirth, that you?

Those are mine.  As well as a ton of stuff over at RoyalRoad, Inkitt, and Goodreads.

https://www.royalroad.com/fictions/search?author=Darkbringer&tagsAdd=fantasy&maxPages=20000&status=HIATUS
https://www.inkitt.com/Darkbringer

(Here's hoping the links above work for you.)

Note -- the links above are the unedited versions which I tend to share for promotional purposes, before they're all polished up nice and neat and then published to earn me a little extra hamburger money.  ;)

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 #35 on: August 25, 2019, 12:40:28 am »
Wow, so cool! Your avatar too!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Function Timestamp
« Reply #36 on: August 25, 2019, 04:17:24 pm »
For the record, we are still occasionally a day off, it doesn't start until before 1900, 1899

Here is adjustment to test code that points to the on / off day amounts,
Code: QB64: [Select]
  1. d1970$ = "01-01-1970"
  2. FOR y = 1920 TO 1850 STEP -1
  3.     d$ = "01-01-" + _TRIM$(STR$(y))
  4.     PRINT d1970$; " Minus "; d$
  5.     PRINT DiffDateTime$(DateTime$(d1970$, times$), DateTime$(d$, times$))
  6.     PRINT TimeStampDiff$(d1970$, time##, d$, time##)
  7.     PRINT
  8.     IF y MOD 5 = 0 THEN
  9.         INPUT "OK ... press enter "; w$
  10.         CLS
  11.     END IF
  12.  

The code I am comparing to arrives at the day amounts the same old way regardless of when the earlier date starts and counts day by day up to the later date. I am pretty sure it's correct no matter the earlier date because it is following the leap year rules and is always going in the same time direction but it's likely to be much slower than Steve's where you just do subtraction of seconds of the 2 time dates and convert the secs to days, hours, minutes... but the 2 time dates do need to be correct of course.

Ah I know check New Years 1899 time## = 0 with link.

Update: took me awhile to figure out 24hr option on preference, got it 1899-01-01:
 
1899-01-01.PNG


The difference is exactly 1 days worth of seconds 86400.


« Last Edit: August 25, 2019, 05:18:24 pm by bplus »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Function Timestamp
« Reply #37 on: August 25, 2019, 05:50:56 pm »
It's a glitch for negative years divisible by 100 and not 400....

Here's the fix:
Code: QB64: [Select]
  1. _TITLE "Test Steve timeStamp" 'B+ test 2019-08-24
  2. '2019-08-24 Quick test Steve timestamp by comparing DiffDateTime$ to TimeStampDiff$
  3. 'I am just testing day counts
  4.  
  5. SCREEN _NEWIMAGE(600, 760, 32)
  6. times$ = "00:00:00": times## = 0
  7.  
  8. dates$(3) = "08-24-2019": dates$(2) = "01-01-2020": dates$(1) = "01-01-2021": dates$(0) = "01-01-2025"
  9. dates$(4) = "01-01-2019": dates$(5) = "01-01-1970": dates$(6) = "01-01-1815"
  10.  
  11. FOR i = 0 TO 5
  12.     FOR j = i + 1 TO 6
  13.         PRINT dates$(i); " Minus "; dates$(j)
  14.         PRINT DiffDateTime$(DateTime$(dates$(i), times$), DateTime$(dates$(j), times$))
  15.         PRINT TimeStampDiff$(dates$(i), time##, dates$(j), time##)
  16.         PRINT
  17.     NEXT
  18.     INPUT "OK ... press enter "; w$
  19.     CLS
  20. d1970$ = "01-01-1970"
  21. FOR y = 1970 TO 1950 STEP -1
  22.     d$ = "01-01-" + _TRIM$(STR$(y))
  23.     PRINT d1970$; " Minus "; d$
  24.     PRINT DiffDateTime$(DateTime$(d1970$, times$), DateTime$(d$, times$))
  25.     PRINT TimeStampDiff$(d1970$, time##, d$, time##)
  26.     PRINT
  27.     IF y MOD 5 = 0 THEN
  28.         INPUT "OK ... press enter "; w$
  29.         CLS
  30.     END IF
  31.  
  32. ' I used this code for Count Down 2020
  33. 'note: this uses 2 dates and times formatted with DateTime$() Function
  34. FUNCTION DiffDateTime$ (LaterDate$, MinusEarlierDate$)
  35.     es = VAL(MID$(MinusEarlierDate$, 18, 2))
  36.     ls = VAL(MID$(LaterDate$, 18, 2))
  37.  
  38.     em = VAL(MID$(MinusEarlierDate$, 15, 2))
  39.     lm = VAL(MID$(LaterDate$, 15, 2))
  40.  
  41.     eh = VAL(MID$(MinusEarlierDate$, 12, 2))
  42.     lh = VAL(MID$(LaterDate$, 12, 2))
  43.  
  44.     ed = VAL(MID$(MinusEarlierDate$, 9, 2))
  45.     ld = VAL(MID$(LaterDate$, 9, 2))
  46.  
  47.     emm = VAL(MID$(MinusEarlierDate$, 6, 2))
  48.     lmm = VAL(MID$(LaterDate$, 6, 2))
  49.  
  50.     ey = VAL(MID$(MinusEarlierDate$, 1, 4))
  51.     ly = VAL(MID$(LaterDate$, 1, 4))
  52.  
  53.     IF es > ls THEN ls = ls + 60: lm = lm - 1
  54.     DiffDateTime$ = STR$(ls - es) + " secs"
  55.  
  56.     IF em > lm THEN lm = lm + 60: lh = lh - 1
  57.     DiffDateTime$ = STR$(lm - em) + " mins" + DiffDateTime$
  58.  
  59.     IF eh > lh THEN lh = lh + 24: ld = ld - 1
  60.     DiffDateTime$ = STR$(lh - eh) + " hours" + DiffDateTime$
  61.  
  62.     ''did we barrow off day
  63.     IF ld = 0 THEN
  64.         SELECT CASE lmm
  65.             CASE 1: ly = ly - 1: lmm = 12: ld = 31
  66.             CASE 3: IF IsLeapYear(ly) = 1 THEN lmm = 2: ld = 29 ELSE lmm = 2: ld = 28
  67.             CASE 5, 7, 8, 10, 12: lmm = lmm - 1: ld = 30
  68.             CASE 2, 4, 6, 9, 11: lmm = lmm - 1: ld = 31
  69.         END SELECT
  70.     END IF
  71.     'PRINT ly, lmm, ld   ' check target and start
  72.     'PRINT ey, emm, ed
  73.  
  74.     'now count days
  75.     DO
  76.         IF ly = ey THEN
  77.             IF lmm = emm THEN
  78.                 IF ld = ed THEN EXIT DO '  all date numbers match, done calc
  79.             END IF
  80.         END IF
  81.         ed = ed + 1
  82.         cnt = cnt + 1
  83.         IF ed = 29 AND emm = 2 THEN
  84.             'is it leap year
  85.             IF IsLeapYear%(ey) = 1 THEN
  86.                 'pass
  87.             ELSE
  88.                 emm = 3: ed = 1
  89.             END IF
  90.         ELSEIF ed = 30 AND emm = 2 THEN 'must be leap year
  91.             emm = 3: ed = 1
  92.         ELSEIF ed = 31 THEN
  93.             IF emm = 4 OR emm = 6 OR emm = 9 OR emm = 11 THEN
  94.                 emm = emm + 1: ed = 1
  95.             END IF
  96.         ELSEIF ed = 32 THEN
  97.             IF emm = 12 THEN
  98.                 ed = 1: emm = 1: ey = ey + 1
  99.             ELSE
  100.                 ed = 1: emm = emm + 1
  101.             END IF
  102.         END IF
  103.         'PRINT ey, emm, ed, ly; " "; lmm; " "; ld   'Check progress
  104.         '_LIMIT 2
  105.     LOOP
  106.     DiffDateTime$ = _TRIM$(STR$(cnt)) + " Days and" + DiffDateTime$
  107.  
  108. FUNCTION CurrentDateTime$
  109.     CurrentDateTime$ = "    -  -  _  :  :  "
  110.     MID$(CurrentDateTime$, 1, 4) = MID$(DATE$, 7, 4)
  111.     MID$(CurrentDateTime$, 6, 5) = MID$(DATE$, 1, 5)
  112.     MID$(CurrentDateTime$, 12, 8) = TIME$
  113.  
  114. FUNCTION DateTime$ (mm_dd_yy$, hh_mm_ss$)
  115.     DateTime$ = "    -  -  _  :  :  "
  116.     MID$(DateTime$, 1, 4) = MID$(mm_dd_yy$, 7, 4)
  117.     MID$(DateTime$, 6, 5) = MID$(mm_dd_yy$, 1, 5)
  118.     MID$(DateTime$, 12, 8) = hh_mm_ss$
  119.  
  120. FUNCTION IsLeapYear% (yr) 'mod from Pete's calendar this is a very clear calc
  121.     IF yr MOD 4 = 0 THEN
  122.         IF yr MOD 100 = 0 THEN
  123.             IF yr MOD 400 = 0 THEN IsLeapYear = 1
  124.         ELSE
  125.             IsLeapYear = 1
  126.         END IF
  127.     END IF
  128.  
  129. 'this calc date time diff using Steve' TimeStamp## secs
  130. FUNCTION TimeStampDiff$ (LaterDate$, LaterTime##, EarlierDate$, EarlierTime##)
  131.     secLate## = TimeStamp##(LaterDate$, LaterTime##)
  132.     secEarly## = TimeStamp##(EarlierDate$, EarlierTime##)
  133.     secDiff = secLate## - secEarly##
  134.     'covert to days, hours, minutes, secs I am dropping fraction secs for test
  135.     days = INT(secDiff / 86400)
  136.     secDiff = secDiff - days * 86400
  137.     hours = INT(secDiff / 3600)
  138.     secDiff = secDiff - hours * 3600
  139.     minutes = INT(secDiff / 60)
  140.     secs = INT(secDiff - minutes * 60)
  141.     TimeStampDiff$ = _TRIM$(STR$(days)) + " Days and " + _TRIM$(STR$(hours)) + " Hours, " + _TRIM$(STR$(minutes)) + " Minutes, " + _TRIM$(STR$(secs))
  142.  
  143. ' Steve's latest version
  144. ' https://www.qb64.org/forum/index.php?topic=1638.msg108650#msg108650
  145. FUNCTION TimeStamp## (d$, t##) 'date and timer
  146.     'Based on Unix Epoch time, which starts at year 1970.
  147.  
  148.     m = VAL(LEFT$(d$, 2))
  149.     d = VAL(MID$(d$, 4, 2))
  150.     y = VAL(RIGHT$(d$, 4))
  151.     IF y < 1970 THEN 'calculate shit backwards
  152.         SELECT CASE m 'turn the day backwards for the month
  153.             CASE 1, 3, 5, 7, 8, 10, 12: d = 31 - d '31 days
  154.             CASE 2: d = 28 - d 'special 28 or 29.
  155.             CASE 4, 6, 9, 11: d = 30 - d '30 days
  156.         END SELECT
  157.         IF y MOD 4 = 0 AND m < 3 THEN 'check for normal leap year, and we're before it...
  158.             d = d - 1 'assume we had a leap year, subtract another day
  159.             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
  160.         END IF
  161.  
  162.         'then count the months that passed after the current month
  163.         FOR i = m + 1 TO 12
  164.             SELECT CASE i
  165.                 CASE 2: d = d - 28
  166.                 CASE 3, 5, 7, 8, 10, 12: d = d - 31
  167.                 CASE 4, 6, 9, 11: d = d - 30
  168.             END SELECT
  169.         NEXT
  170.  
  171.         'we should now have the entered year calculated.  Now lets add in for each year from this point to 1970
  172.         d = d + 365 * (1969 - y) '365 days per each standard year
  173.         FOR i = 1968 TO y + 1 STEP -4 'from 1968 onwards,backwards, skipping the current year (which we handled previously in the FOR loopp)
  174.             d = d + 1 'subtract an extra day every leap year
  175.             IF (i MOD 100) = 0 AND (i MOD 400) <> 0 THEN d = d - 1 'but skiping every year divisible by 100, but not 400
  176.         NEXT
  177.         s## = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  178.         TimeStamp## = -(s## + 24 * 60 * 60 - t##)
  179.         EXIT FUNCTION
  180.     ELSE
  181.         y = y - 1970
  182.     END IF
  183.  
  184.     FOR i = 1 TO m 'for this year,
  185.         SELECT CASE i 'Add the number of days for each previous month passed
  186.             CASE 1: d = d 'January doestn't have any carry over days.
  187.             CASE 2, 4, 6, 8, 9, 11: d = d + 31
  188.             CASE 3 'Feb might be a leap year
  189.                 IF (y MOD 4) = 2 THEN 'if this year is divisible by 4 (starting in 1972)
  190.                     d = d + 29 'its a leap year
  191.                     IF (y MOD 100) = 30 AND (y MOD 400) <> 30 THEN 'unless..
  192.                         d = d - 1 'the year is divisible by 100, and not divisible by 400
  193.                     END IF
  194.                 ELSE 'year not divisible by 4, no worries
  195.                     d = d + 28
  196.                 END IF
  197.             CASE 5, 7, 10, 12: d = d + 30
  198.         END SELECT
  199.     NEXT
  200.     d = (d - 1) + 365 * y 'current month days passed + 365 days per each standard year
  201.     FOR i = 2 TO y - 1 STEP 4 'from 1972 onwards, skipping the current year (which we handled previously in the FOR loopp)
  202.         d = d + 1 'add an extra day every leap year
  203.         IF (i MOD 100) = 30 AND (i MOD 400) <> 30 THEN d = d - 1 'but skiping every year divisible by 100, but not 400
  204.     NEXT
  205.     s## = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  206.     TimeStamp## = (s## + t##)

This glitch makes perfect sense to me (unlike the swapped signs):

In the second half of the code (where we count forwards), we see this line --         IF (i MOD 100) = 30 AND (i MOD 400) <> 30 THEN d = d - 1 'but skiping every year divisible by 100, but not 400 

And then, it was simply copy/pasted into the top half of the code, as is, which doesn't work.

In the bottom half, we subtract 1970 from our year, and start calculating as if we're working up from year 0.  30 years would be the year 2000.  130 years would be the year 2100...    Years mod 100 = 30, are the years we  need to check to see if they're leap years or not...

In the top half, we work with the date directly, without subtracting that 1970...  1900 is 1900, not -70...  We have to year mod 100 = 0 to check for those special "are they, aren't they" leap years?

A simple change of the remainder in those MOD statements, and all appears to be working correctly.  (Of course, I've thought that before, and some edge case always seems to show up to toss that idea upside down on its head...)

« Last Edit: August 30, 2019, 09:52:52 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Marked as best answer by SMcNeill on March 26, 2020, 07:14:30 pm

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Function Timestamp
« Reply #38 on: August 25, 2019, 05:56:09 pm »
Unless there's another edge case which seems screwed up, this version seems to finally work for us:

Code: QB64: [Select]
  1. SHELL "https://www.epochconverter.com/"
  2. PRINT "Compare to time stamps generated at the website which popped up in your browser.https://www.epochconverter.com/"
  3.  
  4. CONST MyTimeZone## = 4 * 3600
  5.  
  6.     _LIMIT 1
  7.     CLS
  8.     PRINT TimeStamp(DATE$, TIMER + MyTimeZone) 'Timezone difference with GMT, which is what the webpage sometimes points to.
  9.     '                                           If the times seem off from the website, you'll want to change the timezone
  10.     '                                           offset to match your current time zone.
  11.     PRINT ExtendedTimer 'Unix Epoch Timer based on local time.
  12.     _DISPLAY
  13.  
  14.  
  15. FUNCTION TimeStamp## (d$, t##) 'date and timer
  16.     'Based on Unix Epoch time, which starts at year 1970.
  17.     DIM s AS _FLOAT
  18.  
  19.     l = INSTR(d$, "-")
  20.     l1 = INSTR(l + 1, d$, "-")
  21.     m = VAL(LEFT$(d$, l))
  22.     d = VAL(MID$(d$, l + 1))
  23.     y = VAL(MID$(d$, l1 + 1))
  24.     IF y < 1970 THEN 'calculate shit backwards
  25.         SELECT CASE m 'turn the day backwards for the month
  26.             CASE 1, 3, 5, 7, 8, 10, 12: d = 31 - d '31 days
  27.             CASE 2: d = 28 - d 'special 28 or 29.
  28.             CASE 4, 6, 9, 11: d = 30 - d '30 days
  29.         END SELECT
  30.         IF y MOD 4 = 0 AND m < 3 THEN 'check for normal leap year, and we're before it...
  31.             d = d + 1 'assume we had a leap year, subtract another day
  32.             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
  33.         END IF
  34.  
  35.         'then count the months that passed after the current month
  36.         FOR i = m + 1 TO 12
  37.             SELECT CASE i
  38.                 CASE 2: d = d + 28
  39.                 CASE 3, 5, 7, 8, 10, 12: d = d + 31
  40.                 CASE 4, 6, 9, 11: d = d + 30
  41.             END SELECT
  42.         NEXT
  43.  
  44.         'we should now have the entered year calculated.  Now lets add in for each year from this point to 1970
  45.         d = d + 365 * (1969 - y) '365 days per each standard year
  46.         FOR i = 1968 TO y + 1 STEP -4 'from 1968 onwards,backwards, skipping the current year (which we handled previously in the FOR loop)
  47.             d = d + 1 'subtract an extra day every leap year
  48.             IF (i MOD 100) = 0 AND (i MOD 400) <> 0 THEN d = d - 1 'but skipping every year divisible by 100, but not 400
  49.         NEXT
  50.         s## = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  51.         TimeStamp## = -(s## + 24 * 60 * 60 - t##)
  52.         EXIT FUNCTION
  53.     ELSE
  54.         y = y - 1970
  55.     END IF
  56.  
  57.     FOR i = 1 TO m 'for this year,
  58.         SELECT CASE i 'Add the number of days for each previous month passed
  59.             CASE 1: d = d 'January doestn't have any carry over days.
  60.             CASE 2, 4, 6, 8, 9, 11: d = d + 31
  61.             CASE 3 'Feb might be a leap year
  62.                 IF (y MOD 4) = 2 THEN 'if this year is divisible by 4 (starting in 1972)
  63.                     d = d + 29 'its a leap year
  64.                     IF (y MOD 100) = 30 AND (y MOD 400) <> 30 THEN 'unless..
  65.                         d = d - 1 'the year is divisible by 100, and not divisible by 400
  66.                     END IF
  67.                 ELSE 'year not divisible by 4, no worries
  68.                     d = d + 28
  69.                 END IF
  70.             CASE 5, 7, 10, 12: d = d + 30
  71.         END SELECT
  72.     NEXT
  73.     d = (d - 1) + 365 * y 'current month days passed + 365 days per each standard year
  74.     FOR i = 2 TO y - 1 STEP 4 'from 1972 onwards, skipping the current year (which we handled previously in the FOR loopp)
  75.         d = d + 1 'add an extra day every leap year
  76.         IF (i MOD 100) = 30 AND (i MOD 400) <> 30 THEN d = d - 1 'but skiping every year divisible by 100, but not 400
  77.     NEXT
  78.     s## = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  79.     TimeStamp## = (s## + t##)
  80.  
  81. FUNCTION ExtendedTimer##
  82.     'Simplified version of the TimeStamp routine, streamlined to only give positive values based on the current timer.
  83.     'Note:  Only good until the year 2100, as we don't do all the fancy calculations for leap years.
  84.     'A timer should work quickly and efficiently in the background; and the less we do, the less lag we might insert
  85.     'into a program.
  86.  
  87.     DIM m AS INTEGER, d AS INTEGER, y AS INTEGER
  88.     DIM s AS _FLOAT, day AS STRING
  89.     day = DATE$
  90.     m = VAL(LEFT$(day, 2))
  91.     d = VAL(MID$(day, 4, 2))
  92.     y = VAL(RIGHT$(day, 4)) - 1970
  93.     SELECT CASE m 'Add the number of days for each previous month passed
  94.         CASE 2: d = d + 31
  95.         CASE 3: d = d + 59
  96.         CASE 4: d = d + 90
  97.         CASE 5: d = d + 120
  98.         CASE 6: d = d + 151
  99.         CASE 7: d = d + 181
  100.         CASE 8: d = d + 212
  101.         CASE 9: d = d + 243
  102.         CASE 10: d = d + 273
  103.         CASE 11: d = d + 304
  104.         CASE 12: d = d + 334
  105.     END SELECT
  106.     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
  107.     d = (d - 1) + 365 * y 'current month days passed + 365 days per each standard year
  108.     d = d + (y + 2) \ 4 'add in days for leap years passed
  109.     s = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  110.     ExtendedTimer## = (s + TIMER)
  111.  
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 #39 on: December 27, 2019, 04:06:53 am »
Code: QB64: [Select]
  1. FUNCTION ExtendedTimer##
  2.     'modified extendedtimer to store the old day's count, and not have to recalculate it every time the routine is called.
  3.  
  4.     STATIC olds AS _FLOAT, old_day AS _FLOAT
  5.     DIM m AS INTEGER, d AS INTEGER, y AS INTEGER
  6.     DIM s AS _FLOAT, day AS STRING
  7.     IF olds = 0 THEN 'calculate the day the first time the extended timer runs
  8.         day = DATE$
  9.         m = VAL(LEFT$(day, 2))
  10.         d = VAL(MID$(day, 4, 2))
  11.         y = VAL(RIGHT$(day, 4)) - 1970
  12.         SELECT CASE m 'Add the number of days for each previous month passed
  13.             CASE 2: d = d + 31
  14.             CASE 3: d = d + 59
  15.             CASE 4: d = d + 90
  16.             CASE 5: d = d + 120
  17.             CASE 6: d = d + 151
  18.             CASE 7: d = d + 181
  19.             CASE 8: d = d + 212
  20.             CASE 9: d = d + 243
  21.             CASE 10: d = d + 273
  22.             CASE 11: d = d + 304
  23.             CASE 12: d = d + 334
  24.         END SELECT
  25.         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
  26.         d = (d - 1) + 365 * y 'current month days passed + 365 days per each standard year
  27.         d = d + (y + 2) \ 4 'add in days for leap years passed
  28.         s = d * 24 * 60 * 60 'Seconds are days * 24 hours * 60 minutes * 60 seconds
  29.         old_day = s
  30.     END IF
  31.     IF TIMER < oldt THEN 'we went from 23:59:59 (a second before midnight) to 0:0:0 (midnight)
  32.         old_day = s + 83400 'add another worth of seconds to our counter
  33.     END IF
  34.     oldt = TIMER
  35.     olds = old_day + oldt
  36.     ExtendedTimer## = olds

More efficient version of ExtendedTimer.  This stores our day values and only updates them when necessary.  We really don't need to figure out how many seconds are in today over and over endlessly -- just count them once, and when the clock swaps back to 0:0:0, add 24*60*60 seconds to the count.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline EricE

  • Forum Regular
  • Posts: 114
    • View Profile
Re: Function Timestamp
« Reply #40 on: March 27, 2020, 09:24:21 pm »
Is the offset due to the local time zone included in the calculation?

Offline EricE

  • Forum Regular
  • Posts: 114
    • View Profile
Re: Function Timestamp
« Reply #41 on: March 27, 2020, 09:34:33 pm »
Hi [banned user],
I have started a new thread requesting that the next release of QB64 have a new function which returns the local Time Zone information.
This would be a very useful addition to the QB64 compiler.
EricE

Offline zaadstra

  • Newbie
  • Posts: 78
    • View Profile
Re: Function Timestamp
« Reply #42 on: September 29, 2021, 04:37:05 pm »
Hi SMcNeill,

I've been running this program (in the green box), and I'm puzzled with the timezone.  My TZ offset now is +02:00 but the program only matches the website when I enter: CONST MyTimeZone## = -2 * 3600

The ExtendedTimer output also is wrong and can't be offset so it seems.

I may have found old obsolete abandoned code ... just wondering ...  I'm building a compare for these date/times: 2019:04:29 13:23:50 (UTC) and 2019:04:29 15:23:49+02:00 (local time notation) ... so still a long way to go. Normally it would just be subtracting one or two hours (DST) but things get awkward when changing days, month or even year....

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Function Timestamp
« Reply #43 on: September 29, 2021, 07:13:30 pm »
Hey Steve, I ran your routine prior to 1970 and got: tihS, just as advertised!

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline zaadstra

  • Newbie
  • Posts: 78
    • View Profile
Re: Function Timestamp
« Reply #44 on: October 03, 2021, 09:54:14 am »
As I ran into an issue with the code (see above), and I wanted to dig a bit deeper to understand things,  I created my own routines. Here are my 2 cents ;-)

I need it to do some tricks with Exiftool so the format is based on that.  It is easy to modify.
There is a routine to convert a normal timestamp to UNIX epoch time, and a routine to convert the other way around.
Time to UNIX is based on the POSIX calculation formula, and UNIX to time is based on an academic paper on Julian dates.

I've put a small loop around it to show current time so you can compare it with the website.  You need to change the timezone tz= to your region.

Code: QB64: [Select]
  1.  
  2. _TITLE "Timestamp and Epoch conversion routines"
  3. ' Configured as Current Time demo loop
  4.  
  5.    CLS
  6.    ' Based on Exiftool  2019:02:05 17:50:30   2019:02:05 18:50:30 +01:00
  7.    tm$ = "2021:10:02 21:00:00"
  8.    'tm$ = "1970:01:02 00:00:00"
  9.    'tm$ = "2319:02:05 17:50:30"
  10.    tm$ = GetNow$
  11.    tz = 2 ' Timezone
  12.  
  13.    IF tz < 0 THEN tzx = ABS(tz): t$ = "-" ELSE tzx = tz: t$ = "+"
  14.    tz$ = " " + t$ + RIGHT$(STR$(100 + tzx), 2) + ":00" ' sorry for the half-hour timezones!
  15.  
  16.    ' date time tm$ to UNIX time
  17.    PRINT: PRINT "Timestamp now   : "; tm$; tz$: PRINT
  18.  
  19.    tm_epoch&& = Epoch&&(tm$) - tz * 3600 ' check: https://www.epochconverter.com/
  20.    PRINT "UNIX epoch time :"; tm_epoch&&
  21.    PRINT
  22.  
  23.    ' UNIX time to date time
  24.    datetime$ = TimeStamp$(tm_epoch&&)
  25.    PRINT "DateTime UTC    : "; datetime$
  26.    datetime$ = TimeStamp$(tm_epoch&& + tz * 3600)
  27.    PRINT "DateTime local  : "; datetime$; tz$
  28.  
  29.    SLEEP 1
  30.  
  31.  
  32.  
  33. FUNCTION Epoch&& (datetime$)
  34.    ' call with date and time argument, format:  "2021:08:09 14:32:14"
  35.    DIM AS _INTEGER64 tm_sec, tm_min, tm_hour, tm_day, tm_month, tm_year, tm_yday
  36.    tm_sec = VAL(MID$(datetime$, 18, 2))
  37.    tm_min = VAL(MID$(datetime$, 15, 2))
  38.    tm_hour = VAL(MID$(datetime$, 12, 2))
  39.    tm_day = VAL(MID$(datetime$, 9, 2))
  40.    tm_month = VAL(MID$(datetime$, 6, 2))
  41.    tm_year = VAL(MID$(datetime$, 1, 4)) - 1900
  42.    tm_yday = INT(275 * tm_month / 9) - (INT((tm_month + 9) / 12) * (1 + INT((tm_year - 4 * INT(tm_year / 4) + 2) / 3))) + tm_day - 30
  43.    Epoch&& = tm_sec + tm_min * 60 + tm_hour * 3600 + (tm_yday - 1) * 86400 + (tm_year - 70) * 31536000 + INT((tm_year - 69) / 4) * 86400 - INT((tm_year - 1) / 100) * 86400 + INT((tm_year + 299) / 400) * 86400
  44.  
  45. FUNCTION TimeStamp$ (tm_epoch&&)
  46.    ' call with argument: UNIX epoch time
  47.    Z& = INT((tm_epoch&& / 86400) + 2440587.5 - 1721118.5)
  48.    R# = (tm_epoch&& / 86400) + 2440587.5 - 1721118.5 - Z&
  49.    G = Z& - .25
  50.    A = INT(G / 36524.25)
  51.    B = A - INT(A / 4)
  52.    year = INT((B + G) / 365.25)
  53.    C& = B + Z& - INT(365.25 * year)
  54.    month = FIX((5 * C& + 456) / 153)
  55.    day = INT(C& - FIX((153 * month - 457) / 5) + R#)
  56.    IF month > 12 THEN year = year + 1: month = month - 12
  57.    tod = R# * 86400
  58.    hh = tod \ 3600
  59.    mm = (tod - hh * 3600) \ 60
  60.    ss = tod MOD 60
  61.    TimeStamp$ = RIGHT$(STR$(10000 + year), 4) + ":" + RIGHT$(STR$(100 + month), 2) + ":" + RIGHT$(STR$(100 + day), 2) + " " + RIGHT$(STR$(100 + hh), 2) + ":" + RIGHT$(STR$(100 + mm), 2) + ":" + RIGHT$(STR$(100 + ss), 2)
  62.  
  63. FUNCTION GetNow$
  64.    GetNow$ = MID$(DATE$, 7, 4) + ":" + MID$(DATE$, 1, 2) + ":" + MID$(DATE$, 4, 2) + " " + TIME$

You may vote for this as 'best answer', LOL !

P.S. thanks to all forummers helping me with insight on variables :-)
« Last Edit: October 03, 2021, 10:05:49 am by zaadstra »