Seconds since 1st January 1961 to date/time

Anything QL Software or Programming Related.
User avatar
Peter
Font of All Knowledge
Posts: 2419
Joined: Sat Jan 22, 2011 8:47 am

Seconds since 1st January 1961 to date/time

Post by Peter »

Hi,

does anybody have an algorithm to convert seconds since 1st January 1961 to year, month, day, hour, minute, second?
Maybe as a C or BASIC program, not using the system vector or DATE$ routine?
I know I could learn this from Minerva or SMSQ/E assembler sourcecode, but I'm getting older...

Thanks, Peter


User avatar
Andrew
QL Wafer Drive
Posts: 1032
Joined: Tue Jul 17, 2018 9:10 pm

Re: Seconds since 1st January 1961 to date/time

Post by Andrew »

This article might be of interest: https://howardhinnant.github.io/date_algorithms.html


Derek_Stewart
Font of All Knowledge
Posts: 4669
Joined: Mon Dec 20, 2010 11:40 am
Location: Sunny Runcorn, Cheshire, UK

Re: Seconds since 1st January 1961 to date/time

Post by Derek_Stewart »

Hi,

According to the online Superbasic manual:

The function DATE returns the current date and time as the number of seconds since midnight on 1st January 1961.

For example, PRINT DATE$(DATE) is exactly the same as PRINT DATE$. The NewDate version of this command is exactly the same as Minerva's implementation.

Now this does not give the algorithm, but Andrew Pennell, give a nice description of the the SDATE functions in the QDOS Companion Chapter 7


Regards,

Derek
User avatar
pjw
QL Wafer Drive
Posts: 1608
Joined: Fri Jul 11, 2014 8:44 am
Location: Norway
Contact:

Re: Seconds since 1st January 1961 to date/time

Post by pjw »

Peter wrote: Mon Dec 09, 2024 10:55 pm <>
does anybody have an algorithm to convert seconds since 1st January 1961 to year, month, day, hour, minute, second?
Maybe as a C or BASIC program, not using the system vector or DATE$ routine?
<>
And you cant use an assembler subroutine like the one that completes the suggestion below?

Code: Select all

ut_cal
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Convert date in seconds (offset from 1961 Jan 01 00:00:00) into
*         year, month, day, weekday, hours, minutes, seconds
*
* Entry: d1.l = time in seconds since 00:00:00 on 1st jan in year dot
* Exit : d4.w = day of week (0..6) / month (0..11)
*        d1.w = day of month (1..31)
*        d6.l = hrs.w  / year.w
*        d3.l = secs.w / mins.w
...
Because if thats ok I can show you the rest of Laurence Reeves' excellent, compact code. Or perhaps you can use a ready-to-eat S*BASIC keyword?


Per
I love long walks, especially when they are taken by people who annoy me.
- Fred Allen
User avatar
Peter
Font of All Knowledge
Posts: 2419
Joined: Sat Jan 22, 2011 8:47 am

Re: Seconds since 1st January 1961 to date/time

Post by Peter »

Thanks Per, but trying to avoid assembler code was why I posted here.

Another idea would be code that converts Unix epoch timestamps to date/time.
Unix epoch starts at 1st January 1970, so it can be calculated from QL clock by adding 283996800.
It's just that such code is a little complicated, dealing with issues that are not important for QL usage.

Still I can use some Unix-ish stuff, if nobody here has something at hand accidentally.


User avatar
Pr0f
QL Wafer Drive
Posts: 1552
Joined: Thu Oct 12, 2017 9:54 am

Re: Seconds since 1st January 1961 to date/time

Post by Pr0f »

I remember writing a date routine for ctime to readable date using PL/1 - but I've long since lost that source code.

The basic premise is that every 4 years is a leap year, except every 100 years, except every 400 years - the year 2000 is a leap year.

Once you have that, it's just a matter of picking a reference start date - either 1961 or 1970 seem to be hot favourites (1970 is used for unix ctime)

The 1st of Jan 1961 is a Sunday

The 1st of Jan 1970 is a Thursday, so your week days would be modulus 7 from that reference day


User avatar
tofro
Font of All Knowledge
Posts: 3080
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: Seconds since 1st January 1961 to date/time

Post by tofro »

No ready code, but I've derived most of my date code I once needed from these algorithms:

http://howardhinnant.github.io/date_algorithms.html

The code they present is C++ templates, but doesn't really use C++specifics, so should be easy to convert to C. There's convert everything into everything else and all explained in detail. The algorithms handle only dates - to get the time, convert back to time stamp, subtract from original value to get the number of seconds into the day. Rest should be straightforward.

The elegant idea these algorithms use is they ignore leap days completely by slicing the time stamp into 400-year "eras" (these have a constant length) and start the year in March, only later adding the days since 1.1.


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
User avatar
janbredenbeek
Super Gold Card
Posts: 668
Joined: Wed Jan 21, 2015 4:54 pm
Location: Hilversum, The Netherlands
Contact:

Re: Seconds since 1st January 1961 to date/time

Post by janbredenbeek »

Pr0f wrote: Tue Dec 10, 2024 9:23 am I remember writing a date routine for ctime to readable date using PL/1 - but I've long since lost that source code.
The basic premise is that every 4 years is a leap year, except every 100 years, except every 400 years - the year 2000 is a leap year.
Once you have that, it's just a matter of picking a reference start date - either 1961 or 1970 seem to be hot favourites (1970 is used for unix ctime)
It seems to me that TT has deliberately chosen 1961 as starting point, just to avoid the 'gregorian leap year' issue. Given that the QDOS date is a 32-bit integer, it will wrap somewhere in 2097, just before 2100 which will be an exception to the 4-year rule. (if you consider it a signed 32-bit integer, it will wrap already in 2029. I wonder which programs will break by that time ;) ).

If you're interested in a fast and small assembler routine to do the job, you might want to take a look at https://github.com/janbredenbeek/QBOX/b ... BOXLIB.ASM from line 457 onwards - it's self-contained and doesn't need particular system calls.


User avatar
dilwyn
Mr QL
Posts: 3057
Joined: Wed Dec 01, 2010 10:39 pm

Re: Seconds since 1st January 1961 to date/time

Post by dilwyn »

This won't be directly what Peter wants, but it shows a calculation method for times between two dates.

I wrote it back in 2014. It's a small BASIC program written using Easyptr (menu included - program loads it from folder 'datecalc' or change that in line 150). As it was 10 years ago I don't remember much about it I'm afraid. I may have used bits of someone else's code, which might be why it was never published.

It does three things:

Calculate days between two dates
Calculate Easter dates (UK)
Calculate which day of the week a given date occurred on

No documentation, only the REMs in the program, sorry. When it asks you to enter a date, enter day first, then month, then year.

Even if of no direct use to Peter, anyone is welcome to use it (or debug it if need be!). I remember it's based partly on someone else's code, adapted by me. As it was unfinished and not published, consider as possibly incomplete or not fully debugged.
Attachments
datecalc.zip
DateCalc
(3.76 KiB) Downloaded 65 times


User avatar
dilwyn
Mr QL
Posts: 3057
Joined: Wed Dec 01, 2010 10:39 pm

Re: Seconds since 1st January 1961 to date/time

Post by dilwyn »

As a bit of a diversion, I asked Google's Gemini to write me a short program to calculate times between two dates. Much to my surprise, it wrote a simple one in ZX Spectrum BASIC, easy to convert to QL SuperBASIC.

It's first offering didn't take leap years or 400 year steps into account. I asked it to adjust for that and it did. Here's its offering (not run it to check but at first glance looks good). This one is the ZX Spectrum BASIC version.

Code: Select all

10 INPUT "Enter first date (DD/MM/YYYY): ", D1$, M1$, Y1$
20 INPUT "Enter second date (DD/MM/YYYY): ", D2$, M2$, Y2$

30 D1 = VAL(D1$)
40 M1 = VAL(M1$)
50 Y1 = VAL(Y1$)
60 D2 = VAL(D2$)
70 M2 = VAL(M2$)
80 Y2 = VAL(Y2$)

90 IF Y2 < Y1 THEN PRINT "Second year must be greater than first year" : GOTO 10
100 IF Y2 = Y1 AND M2 < M1 THEN PRINT "Second month must be greater than first month" : GOTO 10
110 IF Y2 = Y1 AND M2 = M1 AND D2 < D1 THEN PRINT "Second day must be greater than first day" : GOTO 10

120 DEF FNLEAP(Y) = (Y MOD 4 = 0 AND Y MOD 100 <> 0) OR Y MOD 400 = 0

130 YD = (Y2 - Y1) * 365
140 FOR I = Y1 + 1 TO Y2 - 1
150   IF FNLEAP(I) THEN YD = YD + 1
160 NEXT I

170 MD = (M2 - M1) * 30
180 IF M2 > M1 THEN
190   FOR J = M1 + 1 TO M2 - 1
200     IF J = 2 AND FNLEAP(Y2) THEN MD = MD + 1 ELSE MD = MD + 30
210   NEXT J
220 END IF

230 DD = D2 - D1
240 IF M2 = M1 AND D2 > D1 THEN DD = DD - 1 ' Adjust for end-of-month

250 TD = YD + MD + DD

260 PRINT "The difference between the two dates is "; TD; " days"
Next step was to ask it to convert it to QL SuperBASIC, which it did, like this. Again, not had time to run it to test, but looks good. No doubt further refinement possible with carefully phrased instruction. For example, a quick look at it tells me to move the DEF FN to the end of the program so as not to break up the inline code, and do something about the VAL keyword, and remove the FN from the function name when it calls the function (line 140). Wonder what it would have looked like had I asked it outright to create the program in QL BASIC originally rather than convert from Spectrum BASIC?

Code: Select all

10 DIM D1(3), D2(3)
20 INPUT "Enter first date (DD/MM/YYYY): ", D1$(1), D1$(2), D1$(3)
30 INPUT "Enter second date (DD/MM/YYYY): ", D2$(1), D2$(2), D2$(3)

40 FOR I = 1 TO 3
50   D1(I) = VAL(D1$(I))
60   D2(I) = VAL(D2$(I))
70 NEXT I

80 IF D2(3) < D1(3) THEN PRINT "Second year must be greater than first year" : GOTO 20
90 IF D2(3) = D1(3) AND D2(2) < D1(2) THEN PRINT "Second month must be greater than first month" : GOTO 20
100 IF D2(3) = D1(3) AND D2(2) = D1(2) AND D2(1) < D1(1) THEN PRINT "Second day must be greater than first day" : GOTO 20

110 DEF FNLEAP(Y) = (Y MOD 4 = 0 AND Y MOD 100 <> 0) OR Y MOD 400 = 0

120 YD = (D2(3) - D1(3)) * 365
130 FOR I = D1(3) + 1 TO D2(3) - 1
140   IF FNLEAP(I) THEN YD = YD + 1
150 NEXT I

160 MD = (D2(2) - D1(2)) * 30
170 IF D2(2) > D1(2) THEN
180   FOR J = D1(2) + 1 TO D2(2) - 1
190     IF J = 2 AND FNLEAP(D2(3)) THEN MD = MD + 1 ELSE MD = MD + 30
200   NEXT J
210 END IF

220 DD = D2(1) - D1(1)
230 IF D2(2) = D1(2) AND D2(1) > D1(1) THEN DD = DD - 1 ' Adjust for end-of-month

240 TD = YD + MD + DD

250 PRINT "The difference between the two dates is "; TD; " days"
Some warning info it gave me with the program:
Key Considerations for Sinclair QL SuperBASIC:

String Handling: SuperBASIC supports string manipulation, so the input and parsing of dates remain similar to the original BASIC code.
Array Usage: We use arrays D1 and D2 to store the day, month, and year components of each date, making the code more organized and readable.
Function Definition: The DEF FNLEAP(Y) function is defined to determine leap years, as in the original BASIC code.
Looping and Conditional Statements: The FOR...NEXT and IF...THEN...ELSE statements are used to control the flow of the program and perform calculations.
Output: The PRINT statement is used to display the final result.
By following these guidelines, you can effectively convert the original BASIC code into SuperBASIC, taking advantage of its features to create a well-structured and efficient program.
Do I get credit for the first QL SuperBASIC listing written by AI? :ugeek:

Seriously, though, it might be worth Peter asking Gemini to compose some code for him, specifying his exact requirements. In doing this, I found that the first efforts weren't quite what I wanted, but asking it to clarify and refine the listing did produce meaningful results.


Post Reply