2

I am trying to implement in C two simple convertors, date/time to time-stamp and vice versa, without any dependencies on time library routines, such as mktime, etc.

The time-stamp is in seconds, and the date/time structure is in the following format:

  • unsigned char year: 0 to 99 (representing the range 2000 to 2099)

  • unsigned char month: 1 to 12

  • unsigned char day: 1 to 31

  • unsigned char hour: 0 to 23

  • unsigned char minute: 0 to 59

  • unsigned char second: 0 to 59

I would like to have a second opinion on the dt2ts convertor (assuming that the input is legal):

unsigned int dt2ts(const dt_t* dt)
{
    static unsigned short days[] = {0,31,59,90,120,151,181,212,243,273,304,334};
    return ((((dt->year*365+dt->year/4)+days[dt->month-1]+dt->day)*24+dt->hour)*60+dt->minute)*60+dt->second;
}

In addition to that, I would appreciate some help completing the ts2dt convertor:

void ts2dt(unsigned int ts,dt_t* dt)
{
    dt->second = ts%60; ts /= 60;
    dt->minute = ts%60; ts /= 60;
    dt->hour   = ts%24; ts /= 24;
    dt->day    = ?????;
    dt->month  = ?????;
    dt->year   = ?????;
}

Thanks

barak manos
  • 29,648
  • 10
  • 62
  • 114
  • Let's hope this is an exercise and you're not just creating a Y2100 bug. :-) Do you care about Daylight Saving Time? You definitely need to account for leap years, when there's a Feb 29. – torek Nov 03 '13 at 18:41
  • 1
    http://pubs.opengroup.org/onlinepubs/000095399/functions/gmtime.html – Mat Nov 03 '13 at 18:42
  • 1
    Why you trying to put all the code in one line? It is not Perl or Python. This "style" really hard to read. – JIghtuse Nov 03 '13 at 18:46
  • Might want to make `const` the array `static unsigned short days[]`. – chux - Reinstate Monica Nov 03 '13 at 18:48
  • To answer all 4 comments above: torek - Daylight Saving Time should not have any effect on the outcome, and my code (above) already accounts for leap years. Mat - I specifically emphasized I would like to refrain using time library routines. JIghtuse and chux - thanks, but I'm not looking for coding advices in this case. – barak manos Nov 03 '13 at 20:06
  • 1
    `(dt->year*365+dt->year/4)+days[dt->month-1]` is not correct in that it does not account for leap day properly. – chux - Reinstate Monica Nov 03 '13 at 20:35
  • chux: "dt->year/4" adds 1 day for every year divisible by 4 (so it adds 1 day for 2004, 2 days for 2008 and so forth). I am not concerned about multiples of 100 or 400 (year 2100 and onwards), in case you were wondering... – barak manos Nov 03 '13 at 21:09
  • Try `dt_t Feb28 = {00,2,28,0,0,0}; dt_t Mar1 = {00,3,1,0,0,0}; printf("%d\n", dt2ts(&Mar1) - dt2ts(&Feb28) );` You get 86400 seconds (1 day), even though its 2 days apart. – chux - Reinstate Monica Nov 04 '13 at 05:50
  • related: [Minimal implementation of gmtime algorithm?](http://stackoverflow.com/q/9745255/4279) – jfs Dec 29 '13 at 21:50
  • especially [`mktm_r.c`](http://stackoverflow.com/questions/9745255/minimal-implementation-of-gmtime-algorithm#comment31240157_9745516) – jfs Dec 29 '13 at 22:02

1 Answers1

2

OP is all ready well handling the hours, minutes, seconds. Just a bit of assist on Y,M,D.

Note: The number of days from Jan 1, 2000 to Dec 31, 2099 needs at least a 16 bit integer. Following should work even if unsigned is 2 bytes.

unsigned DivRem(unsigned Dividend, unsigned Divisor, unsigned *Remainder) {
  unsigned Quotient = Dividend/Divisor;
  *Remainder = Dividend - Quotient*Divisor;
  return Quotient;
}

void Day2000ToYMD(unsigned DaySince2000Jan1, unsigned *Y, unsigned *M, unsigned *D) {
  unsigned OlympiadDay;  // Every 4 years is an Olympiad
  *Y = 4*DivRem(DaySince2000Jan1, 365*4+1, &OlympiadDay);
  *D = 1;
  if (OlympiadDay >= (31+29-1)) {  // deal with Feb 29th and after
    OlympiadDay--;
    if (OlympiadDay == (31+29-1)) {
      (*D)++;
    }
  }
  unsigned YearDay;      // Day of the year 0 to 364
  *Y += DivRem(OlympiadDay, 365, &YearDay);
  static const unsigned short days[] = {0,31,59,90,120,151,181,212,243,273,304,334,365};
  *M = 1;
  while (days[*M] <= YearDay) (*M)++;
  *D += YearDay - days[*M - 1];
}

[Edit] The answer provided tries to keep the concept of the year as Jan 1 to Dec 31. As this answer does not need to handle the leap year years about 100 and 400 years, I've kept with this style.

In general, once those 2 rules are added, the math becomes easier to if one shifts the beginning of the year to March 1 and ending on Feb 28/29. FWIW, this is a more consistent view of the ancient development of the Julian/Gregorian calendar. Thus *Oct*ober is then the 8th month and *Dec*ember is the 10th month.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256