Converting a UNIX time / date stamp to human readable form.

Go To Last Post
39 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I need to store a time/date stamp (accurate to 1 second) in four bytes. So I am attenmpting to use a UNIX style time & date stamp. (I.e. the date stamp is x seconds from an epoch date). The problem is I'm struggling with an alogtithm to convert a time stamp from UNIX format to a humand readable format.

A google search revals lots of applications, programmes and applets to do this but no algortiths explaining how it is done.

Can ayone here offer a suitable algorithm?

Ben
-Using IAR (& ocasionally CodeVision)
0.7734
1101111011000000110111101101

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

http://en.wikipedia.org/wiki/Uni...

UNIX (or POSIX) time increases by 86400 each day, and is a count of the seconds elapsed since January 1st, 1970. Special considerations need to be taken to align leap years accordingly...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've posted an algorithm before now. I'm guessing a search for "unixtime" should hit it.

EDIT: it did - see rtc.c in that thread:

https://www.avrfreaks.net/index.p...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cheers

Ben
-Using IAR (& ocasionally CodeVision)
0.7734
1101111011000000110111101101

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1
/* Set the tm_t fields for the local time. */
struct tm *gmtime(timep) const time_t *timep;
{
	static struct tm tmbuf;
	register struct tm *tp = &tmbuf;
	time_t time = *timep;
	register long day, mins, secs, year, leap;
	day = time/(24L*60*60);
	secs = time % (24L*60*60);
	tp->tm_sec = secs % 60;
	mins = secs / 60;
	tp->tm_hour = mins / 60;
	tp->tm_min = mins % 60;
	tp->tm_wday = (day + 4) % 7;
	year = (((day * 4) + 2)/1461);
	tp->tm_year = year + 70;
	leap = !(tp->tm_year & 3);
	day -= ((year * 1461) + 1) / 4;
	tp->tm_yday = day;
	day += (day > 58 + leap) ? ((leap) ? 1 : 2) : 0;
	tp->tm_mon = ((day * 12) + 6)/367;
	tp->tm_mday = day + 1 - ((tp->tm_mon * 367) + 5)/12;
	tp->tm_isdst = 0;
	return (tp);
}

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How soon before someone mentions Zeller's Congruence I wonder? ;-)

http://en.wikipedia.org/wiki/Zel...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Maybe it's me but....

I can't get my head around the two lines that calculate the month and the day of the month. Can anyone kindly explain how they work?

tp->tm_mon = ((day * 12) + 6)/367;
tp->tm_mday = day + 1 - ((tp->tm_mon * 367) + 5)/12; 

Ben
-Using IAR (& ocasionally CodeVision)
0.7734
1101111011000000110111101101

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I originally wrote time routines for 6502 assembler. It was important to have integer arithmetic. (for me anyway)

I probably just used the same algorithm when writing a Unix compatible function. (I dug it out from some very old code)

There are many ways of handling the odd month length / leap years etc. I think you will find that my way works, but does not look very elegant. It relies on the day always being less than 367. So any integer divisions will give the correct result.

Read your Unix docs for explanation of tm_t structure.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

David,

Your way works - I just can't get my head around it ;-)

Cheers

Ben
-Using IAR (& ocasionally CodeVision)
0.7734
1101111011000000110111101101

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ben - Cliff gave you the hint - Zeller's Congruence. Zeller pondered this very problem and came up with a solution - centuries ago. So now you know!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't understand, Zeller's Congruence solves only the problem with the day of week, not what the OP wants. Or am I wrong?

axos88

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

yes, but it explains the two lines of code he had in question.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:

	leap = !(tp->tm_year & 3);
	day += (day > 58 + leap) ? ((leap) ? 1 : 2) : 0;
}

I can anyone explain me what this two lines do (mathematicly)?
Maybe an example?

I've been trying to understand it, but no success :(

Thanks in advance

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It looks like something like this is doing the same thing.

if ((tp->tm_year % 4) == 0)
   leap = 1;
else
   leap = 0;

if (day > 58 + leap) {
   if (leap != 0)
      day = day + 1;
   else
      day = day + 2;
}

I think the last part is equal to

if (day > 58 + leap) {
   day = day + 2 - leap;
}

Untested.

Edit: Corrected last line.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

snigelen wrote:
It looks like something like this is doing the same thing.

Thank You, it works.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
   // true for divisible by 4
   leap = !(tp->tm_year & 3);
   // make february seem to 'have' 30 days 
   day += (day > 58 + leap) ? ((leap) ? 1 : 2) : 0;
}

The algorithm works but is a little untidy.
It worked very well in 6502 assembler.

I have never understood why February should be such an unusual number of days. A more intuitive sequence would be Jan = 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, Dec = 30/31 days.

Hey-ho, I am not the Pope.

Do the Chinese, Japanese, Indians, Arabs have a more logical system of dates?
Let's face it, you always need occasional leap days. Whether they occur in February or December is a matter for debate.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just a note that the algorithms above works because they rely on an exception to the exception for what is a leap year, and that the Unixdate interval for valid dates is 1970 to 2038.

The leap year rules are:
- If a year is divisible by 4 it is a leap year.
- Exception: If a year is divisible by 100 it is not a leap year (e.g. year 1900).
- Exception to the exception: If a year is divisible by 400 it is a leap year (e.g. year 2000).

So, if you for some reason "transpose" these algorithms to e.g. years 1870 to 1938 they will not work.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1. I will be dead before 1 March 2100.
2. I had not heard of Unix or Unix time when I wrote the 6502. In fact I used 'days since 1 Jan 1968' for the purpose of storing calving and insemination dates.

There is nothing inherently wrong with Unix time_t. I just guess that will change from an int32_t to an int64_t sometime before we get to 2038.

I will not get to 2038.

I doubt that anyone needed seconds resolution in medieval times. The standard Unix 'cal' program will even give you the correct English calendar for September 1757.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

We're drifting OT now...

Quote:
I doubt that anyone needed seconds resolution in medieval times.

No, but contemporary software might want to handle some point in medieval time with resolution of seconds. E.g. astronomical software.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Modern PCs have little problem with uint64_t or even high resolution floating point.

I guess that the medieval astronomer simply does everything relative to 1 Jan 1970 but with a higher resolution time_t. They can cater for leap seconds too.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, this is an old thread. Since I was looking for a similar function, I found this thread.

I noticed that the original algorithm only works up to 2038-01-19 03:14:07 (Unix-Time 0x7FFFFFFF), due to the signed data type.

If the constant (24UL*60*60) is used rather than (24L*60*60), it works right until 2100-02-28, inclusively.
I did the test on the PC with 32bit integers. So, with the avr-gcc (with 16 bit int) the "UL" is needed in all cases.

In the beginning was the Word, and the Word was with God, and the Word was God.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is a unix time function in the latest release of avr-libc. Announcement here:

https://www.avrfreaks.net/index.p...

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, Jim, for that hint.

Yes, there was a release at 2014-08-12. In http://www.nongnu.org/avr-libc/N... it says:

Quote:
*** Changes in avr-libc-1.8.1:

Quote:
* Other changes:

Quote:

- Add time.h package, C standard functions such as mktime() and localtime,
along with 'ephemera' such as solar declination, time of sun rise and set.

In https://github.com/vancegroup-mi... I can see the functions.

And the algorithm in https://github.com/vancegroup-mi... looks much nicer than the one posted above. I will check it out...

In the beginning was the Word, and the Word was with God, and the Word was God.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I noticed that the original algorithm only works up to 2038-01-19 ...

Are you doing celestial navigation?

Do you need to be compatible with other utilities, or not?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Are you doing celestial navigation?

No, but there is the probability that my code will be used 2038 (and we are / I am still alive).

In the beginning was the Word, and the Word was with God, and the Word was God.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's always been the case that unixtime is due to rollover in 2038. Programmers since the 70's have accepted this. There was a lot of hoopla at the end of 1999 about the "millennium bug" was going to be disastrous but in the end it was all a bit of a damp squib however there really are going to be BIG problems when we get there. To combat this some systems are now treating the count as uint32_t rather than int32_t which extends the deadline so perhaps this is the best approach for now.

Luckily for us 2038 is still currently so far away that the likelihood of any electronics designed now still being operational then is close to 0. (were not now using 5.25" floppy disks or EGA video cards as we were in 1990 which is - 24 years while 2038 is +24 years)

But as I say switch to uint32_t and let your grandchildren's grandchildren worry about it instead: if there are 2G seconds between 1970 and 2038 (68 years) then the extra bit (4G seconds) will extend to 2038+68 which is 2106.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

(were not now using 5.25" floppy disks or EGA video cards

Youre not?!? Really? Are you suggesting that I've fallen behind and should upgrade my hardware?? :wink:

Quote:
there is the probability that my code will be used 2038 (and we are / I am still alive).

If it's a question about being alive or not at unixtime wrap - then I'm likely won't have any problems with the exthausion of the unixtime format.

In the unlikely event I am still alive then I will be so happy about it that I would not care sh*t about the unixtime wrap.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wait! Are you suggesting my 8 inch floppy hardware is inferior to your 5.25? I don't understand!

Kidding aside, conversion to/from UNIX time is easily accomplished by adding/subtracting the constant UNIX_OFFSET. Similarly for NTP_OFFSET.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Conversion from the unix time to NTP time is not an issue.
The most complicated task from converting from unix time to YMD/hms is the calculation of the year Y from the day number after 1970, caring for leap years correctly.
You can do it easily with a loop, but that's not good style, if there's a closed formula.

In the beginning was the Word, and the Word was with God, and the Word was God.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In the meantime I found some intersting alorithms at http://www.merlyn.demon.co.uk/da... , though I do not understand exactly where the magic numbers come from.

function afterEGRalgF(CMJD) { var g=0, J_, Y_, T_, M_, D_, D, M, Y
  J = CMJD + 2400001
  // Alg F : To convert a Julian day number, J, to a date D/M/Y
  g  = ( (3*( ((4*J + B)/146097|0 ))/4 )|0) + G // omit for Julian
  J_ = J + j + g
  Y_ = ((r*J_ + v)/p)|0
  T_ = ( ((r*J_ + v) % p)/r)|0
  M_ = ((u*T_ + w)/s)|0
  D_ = ( ((u*T_ + w) % s)/u)|0
  D  = D_ + 1
  M  = ((M_ + m - 1) % n) + 1
  Y  = Y_ - y + (( (n + m - 1 - M)/n)|0)
  return [Y, M, D]
}

function optEGRalgF(CMJD) {
  var g=0, J, t, D, M, Y
  J = CMJD + 2400001
  // Alg F : To convert a Julian day number, J, to a date D/M/Y
  g = ( (3*( ((4*J + 274277)/146097|0 ))/4 )|0) - 38 // not Julian
  J += 1401 + g ; t = 4*J+3
  Y = (t/1461)|0
  t = (t%1461) >> 2
  M = ((t*5 + 461)/153)|0
  D = ( ((t*5 + 2) % 153)/5)|0
  if (M>12) { Y++ ; M -= 12 }
  return [Y-4716, M, D+1]
}

Some values are easy to understand, for exampe 146097, which are the days in 400 years.

In the beginning was the Word, and the Word was with God, and the Word was God.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is this book by Jean Meeus, "Astronomical Algorithms", that has a chapter devoted to conversion of dates and times.

I believe that an open source project implementing the algorithms is here: http://astroalgorithms.sourcefor... .

[WHAT?!? Not CAPTCHA'd??!]

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

In the meantime I found some intersting alorithms at

"Interesting" they may be but what is the point in unintelligible, obfuscated code? I much prefer functions that look like this:

http://svn.savannah.nongnu.org/v...

and this

http://svn.savannah.nongnu.org/v...

That's proper C that tells the reader pretty much exactly what is going on.

Those are the functions now available in AVR-LibC 1.8.1

[Hey, I wasn't captcha'd either - is it "off"?]

EDIT: Captcha is clearly turned off - just deleted 60+ in Off Topic!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am familiar with Meeus, who is widely quoted in many scholarly tomes. The 'ephemera' code in AVR-Libc drew a lot of inspiration from Meeus. (As a matter of fact, now that it has been brought up, I really should disclose that in the docs).

Meeus algorithms are designed to produce 'correct' results over a range of centuries. As a practical matter, they are not directly suitable for general use in the AVR environment, due to the usual constraints of available resources.

However, for purpose built applications (i.e. not library code), much of Meeus can be applied, as long as care is taken to compensate for the limitations of 32 bit floating point code available.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@clawson: thank you for the link relating to the gmtime_r.c implemenation.

This is a very good commented function without any magic numbers.

Due to the ldiv calls it should also perform very fast.

 

I only wonder why for

 days = *timer / 86400UL;
 fract = *timer % 86400UL;

no ldiv call is used, especially if *timer (=unixtime) is a signed value.

In the beginning was the Word, and the Word was with God, and the Word was God.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skotti wrote:

@clawson: thank you for the link relating to the gmtime_r.c implemenation.

This is a very good commented function without any magic numbers.

Due to the ldiv calls it should also perform very fast.

 

I only wonder why for

 days = *timer / 86400UL;
 fract = *timer % 86400UL;

no ldiv call is used, especially if *timer (=unixtime) is a signed value.


From avr-libc/time.h:
<br />
/**<br />
time_t represents seconds elapsed from Midnight, Jan 1 2000 UTC (the Y2K 'epoch').<br />
Its range allows this implementation to represent time up to Tue Feb 7 06:28:15 2136 UTC.<br />
*/<br />
typedef uint32_t time_t;

(Apologies if this has odd formatting. The new forum doesn't seem to work very well on Android.)

Last Edited: Wed. Sep 10, 2014 - 02:11 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good day, excuse me for reviving this thread, I found David's implementation very good, but I am having problems with it. Probably I miss something and will be very glad if you point me to the mistake.

I seek to convert NTP reported time to readable format. NTP gives time since Jan 1, 1900, thus converting to Unix Jan 1, 1970 epoch is just subtract NTP time with 2,208,988,800.

I have time, DB12763E, which is 3,675,420,222 and corresponds to June 20, 2016 14:03:42. Moving epoch to 1970 I get 1,466,431,422. Using David's algorithm I get proper time, weekday (1), year (46), yday (171), but

	day += (day > 58 + leap) ? ((leap) ? 1 : 2) : 0;
	tp->tm_mon = ((day * 12) + 6)/367;

I get month 5 (May). [leap is 1 in calculations].

What I do wrong? Thank you!

 

EDIT: OMG after re-reading this post I thought that January should be 0??? Then June is 5...

EDIT1: Correct, please excuse me for bothering. I have read definition of tm structure... And also learned something new that Sunday is 0 :)

Last Edited: Mon. Jun 20, 2016 - 06:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Eugeny_ wrote:
And also learned something new that Sunday is 0 :)

Indeed, many [most?  all?] RTC chips such as DS1305 family have "day" as 1-7.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can use whatever convention you like.   I am sure that Florida measures Sundays in 64ths of an inch.

 

Just as long as you are consistent.    The function uses Unix structs.   Whether you like it or not,  you follow Unix rules.

Likewise,   an RTC chip might use different structs.    Lee mentioned the Maxim/Dallas chips.   From memory,   NXP are are 0-6 for the day of the week.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I like that it works. Changing rules is not a problem writing conversions and format connectors.