Shop OBEX P1 Docs P2 Docs Learn Events
My seconds to date function in C ( easy port to spin) for years 2000 to 2136 — Parallax Forums

My seconds to date function in C ( easy port to spin) for years 2000 to 2136

tonyp12tonyp12 Posts: 1,951
edited 2017-04-05 21:56 in Propeller 1
rtc struct is 6 bytes with the names: .year .month .day .hour .minute . second
Use this website to calculate seconds, we are in DST now so there is a option to add timezone conversion.
https://www.timeanddate.com/date/durationresult.html?m1=1&d1=1&y1=2000&m2=4&d2=5&y2=2135&h1=0&i1=0&s1=0&h2=16&i2=42&s2=45

Systems with signed 32bit unix from 1970 will soon get Y2K38 problems, this one uses unsigned from 2000, so a embedded system using it will get plenty of life.
I did not look at any other code examples as I did not want to get "bad" influences.
Mine is fast and uses no multiplications/division and no fractions, pasm have cmpsub so that would be even faster.
const unsigned long secinamonth[13] = {0,0,0,31*86400,30*86400,31*86400,30*86400,31*86400,31*86400,30*86400,31*86400,30*86400,31*86400};    

void seconds_to_rtc(unsigned long sec)                  // unsigned 32bit input 
{
  char temp = 0;                                        // start at year 2000
  while (sec >= 126230400 && temp <100){ sec -= 126230400; temp +=4;} // four year blocks until 2100
  if (temp == 100){while (sec >= 31536000 && temp <104){ sec -= 31536000; temp +=1;}}//no leapyears
  while (sec >= 126230400){ sec -= 126230400; temp +=4;}// four year blocks again
  if (sec >= 31622400){ sec -= 31622400; temp +=1;      // first year after a 4yr block is a leapyear                           
    while (sec >= 31536000){ sec -= 31536000; temp +=1;}// then it can only be two years left 
  } 
  rtc.year = temp; temp = 1;                             // let it go past 99 to max 136 for year 2136
 
  if (sec >= 86400*31){                                  // January
    sec -= 86400*31; temp +=1;                          
    unsigned long tempfeb = 86400*28;
    if (rtc.year !=100 && !(rtc.year & 3)) tempfeb = 86400*29;// 2100 is no leap year
    if (sec >= tempfeb){ 
      sec -= tempfeb; temp +=1;                          // do the other months now
      while (sec >= secinamonth[temp]){ sec -= secinamonth[temp]; temp +=1;}
    }
  }
  rtc.month = temp; temp = 1;
 
  while (sec >= 86400){ sec -= 86400; temp +=1;}         // days
  rtc.day = temp; temp = 0;
  while (sec >= 3600){ sec -= 3600; temp +=1;}           // hour
  rtc.hour = temp; temp = 0;
  while (sec >= 60){ sec -= 60; temp +=1;}               // minute
  rtc.minute = temp; 
  rtc.second = sec;                                      // seconds
}

Comments

  • Now all you need to do is add the Day of Week...
  • tonyp12tonyp12 Posts: 1,951
    edited 2017-04-06 01:54
    Maybe do that as separate routine, should not be to hard as functionally it's just modulo (7*86400).
    Jan 1 2000 was a Saturday,
  • Just to be pedantic -- your routine converts "solar" seconds to date, not SI (or standard UTC) seconds. In other words, you're ignoring leap seconds. Which is probably unavoidable for an embedded system!

    (Sorry, you hit on one of my pet peeves -- lots of people think Unix time_t and similar timestamps count "seconds since the epoch", but in fact they actually count 86400*"days since the epoch", and the two are not the same -- not all days have 86400 seconds.)
  • tonyp12, ... in 120 years, your going to have a lot of people cursing you :-)
  • tonyp12tonyp12 Posts: 1,951
    edited 2017-04-06 15:42
    Feb7 2136 there will be cursing :), but is anything build this decade still around then? pcb probably turned in to sand, capacitors have dried up.

    You can also do local time:
    rtc.zone = 9.5*3600; // signed long Adelaide Australia.
    //rtc.zone = -5*3600; // signed long New York USA.
    seconds_to_rtc(seconds + rtc.zone);
    
    And it's up to user to adjust rt.c.zone ±3600 on the dst-dates.

    seconds could be a #define to directly pointing to counter register,
    or a function that merge a much faster running counter like 8192Hz rightshifted >>13 and adding to it a software counter that rollover IRQ have been adding upper values to.

    So what is best is to have a que message system, that you call to insert a time and event flag and it sorts them. (eg. a rtc alarm)
    As you could then insert the 2am/3am DST dates as a event.
    Though there are no CCR register and IRQ's for counters on a P1 and waitcnt only taps in on system counter.

    leap seconds, leap smear is probably safer, Google did that last time on Dec31 2016 over a 10hr timespan.
    You should use a timeserver that gives you seconds from 2000 that don't include leap seconds as actually seconds counted.
    Or base your system time on GPS time, it was zero at ( Jan6 ?) 1980 and since it is not perturbed by leap seconds GPS is now ahead of UTC by 18 seconds.

    Are leap-smears still seconds added to the seconds-between-two-dates?
    quote:
    The irregularity and unpredictability of UTC leap seconds is problematic for several areas, especially computing.
    For example, to compute the elapsed time in seconds between two given UTC past dates requires the consultation of a table of leap seconds, which needs to be updated whenever a new leap second is announced.
    Moreover, it is not possible to compute accurate time intervals for UTC dates that are more than about six months in the future.

    Two timescales that do not follow leap seconds are already available, International Atomic Time (TAI) and Global Positioning System (GPS) time
    Instead of inserting a leap second at the end of the day, Google servers implement a leap smear, extending seconds slightly over a time window prior to the leap second.
    Amazon has announced it would follow a similar, but slightly different, pattern for the introduction of the June 30, 2015 leap second,leading to another case of the proliferation of timescales.
  • tonyp12tonyp12 Posts: 1,951
    edited 2017-04-06 18:55
    Following ZIGBEE way of doing is probably best, It synchronize itself with NTP UTC, so that should include leap seconds.
    But if not syncing up on Jan1 or July1 (two dates most likely to just had a leap second) and it runs of a hardware rtc it will be a second off until next sync.

    And where does it store leapsecond lookup table?, so how does ZigBee handle leapseconds when its trying to calculate dates?, I guess that is up to programmer?
    ---32bit and unsigned unless noted---
    Time			0x0000 R  The current time, in UTC 2000 (seconds since Jan 1, 2000).
    Time Status		0x0001 R  Bitmap representing the the synchronization state of this time server. 
    Time Zone		0x0002 RW User-settable, signed offset from UTC (in seconds) representing the local time zone. (signed)
    Daylight Savings Start	0x0003 RW User-settable UTC of when daylight savings time starts for the current year. 
    Daylight Savings End	0x0004 RW User-settable UTC of when daylight savings time ends for the current year.
    Daylight Savings Shift	0x0005 RW User-settable, offset from standard time (in seconds) while daylight savings is in effect. (signed)
    Standard Time		0x0006 R  The standard time of the local device (seconds since Jan 1, 2000), after adjusting for the time zone.
    Local Time		0x0007 R  The local time of the local device (seconds since Jan 1, 2000), after adjusting for the time zone and daylight savings
    
  • Time zones are mean...



    ...and DST even more...
  • tonyp12tonyp12 Posts: 1,951
    edited 2017-11-29 22:34
    leap second is really mean as it's so random, who cares that sunrise is off by a second?

    Why not
    1: Just always add two leap seconds to the leapday Feb29, in the last 26 years the average have been 2.17yrs between a added leap second.
    OR
    2: Use a leap-minute every 100 years, on Jan-1 2100 everyone gets an extra minute of sleep.

    In November 2015 it was again decided to continue using leap seconds, pending further study and consideration at the next conference in 2023.
  • tonyp12tonyp12 Posts: 1,951
    edited 2017-12-01 03:12
    Here is the other way, date to seconds, structs is just plain 6 bytes year,month....
    This one I use runtime multiplications, as all ARM have single cycle multi.
    const uint32_t secinamonth[13] = {0,31*86400,28*86400,31*86400,30*86400,31*86400,30*86400,31*86400,31*86400,30*86400,31*86400,30*86400,31*86400}; 
    
    uint32_t rtcc_to_seconds(struct rtccstruct *rtcc)                     // input of a filled-in struct returns seconds
    {
      char temp = rtcc->year;
      uint32_t seconds = temp * 31536000;                                 // seconds in a normal year
      if (temp && --temp < 100) seconds += 86400;                         // add the 2000 leapday but offset that 2100 is not a leapyear
      seconds += (temp>>2) * 86400;                                       // add the every full 4th year leapdays
      temp = rtcc->month;
      const uint32_t *pnt = &secinamonth[1];                              // set pointer to Jan
      if (temp > 2 && rtcc->year !=100 && !(rtcc->year & 3)) seconds += 86400;   // pre-add leapday
      while (--temp) seconds += *pnt++;                                   // add all months of the year
      seconds += (rtcc->day - 1) * 86400;                                 // there is no day 0 so sub1
      return seconds + rtcc->hour*3600 + rtcc->minute*60 + rtcc->second;  // that is it
    }
    

Sign In or Register to comment.