Link to home
Start Free TrialLog in
Avatar of sailwind
sailwind

asked on

Convert 32bit UTC timestamp to yr, mo, da, sec, etc.

Hi there everyone.

I am trying to convert a 32 bit UTC timestamp, which represents the number of seconds from the year 1900, into the current year, month, , date, day, hr, min, se.  Does anyone have the algorithm to calculate this and can help explain how the algorithm works? Thanks.
Avatar of ozo
ozo
Flag of United States of America image

#include <time.h>
struct tm *gmtime (const time_t *timer);
Avatar of sailwind
sailwind

ASKER


Ozo, thanks for your interest again. I used your answer last time and it worked fine. This time around though, I would like to see the actual algorithm and understand exactly how the year, date, month, etc is calculated.

I tried looking at some of the standard C routines, but couldn't make heads or tail of the advanced stuff it was doing. Can anyone propose an efficient yet easy to understand algorithm for converting the timestamp to smaller time units? Thanks.
If the time is divided in a single 32 bit number i.e. if it is bitfield then let me know how the fields are divided in bits?

I mean,
hours ===   __ to __ bits
min   ===   __ to __ bits
sec   ===   __ to __ bits
This converts seconds to date & time.  I think it can be readily interpreted.

#include<stdio.h>


void datum(unsigned long secs,long *year,long *month,long *day,long *hour,
 long *min, long *sec)
 {
      long tdays;
      int tyear;
      int year4;       \
      int leap = 0;

      tdays = secs / ((unsigned long) 24 * 3600);

      if (tdays < 1461)
      {
            tyear = tdays / 365;
            tdays = tdays - 365 * tyear;
            leap = 0;
      }
      else
      {
            year4 = (tdays - 1460) / 1461 + 1;
            tdays = (tdays -1460) % 1461;
            if (tdays > 366)
            {
               tyear = year4 * 4 + (tdays - 366)/365+1;
               tdays = (tdays - 366) % 365;
               leap = 0;
            }
            else
            {
               tyear = year4 * 4;
               leap = 1;
            }
      }

      if (tdays < 31)
      {
            *month = 1;
            *day = tdays;
      }
      else
      {
      if(tdays < 59 + leap)
      {
            *month = 2;
            *day = tdays - 31;
      }
      else
      {
      if(tdays < 90 + leap)
      {
            *month = 3;
            *day  = tdays - 59 - leap;
      }
      else
      {
      if(tdays < 120 + leap)

      {
            *month = 4;
            *day = tdays - 90 - leap;
      }
      else
      {
      if(tdays < 151 + leap)
      {
            *month = 5;
            *day = tdays - 120 - leap;
      }
      else
      {
      if(tdays < 181 + leap)
      {
            *month = 6;
            *day = tdays - 151 - leap;
      }
      else
      {
      if(tdays < 212 + leap)
      {
            *month = 7;
            *day = tdays - 181 - leap;
      }
      else
      {
      if(tdays < 243 + leap)
      {
            *month = 8;
            *day = tdays - 212 - leap;
      }
      else
      {
      if(tdays < 273 + leap)
      {
            *month = 9;
            *day = tdays - 243 - leap;
      }
      else
      {
      if(tdays < 304 + leap)
      {
            *month = 10;
            *day = tdays - 273 - leap;
      }
      else
      {
      if(tdays < 334 + leap)
      {
            *month = 11;
            *day = tdays - 304 - leap;
      }
      else
      {
      if(tdays < 365 + leap)
      {
            *month = 12;
            *day = tdays - 365 - leap;
      }
      }}}}}}}}}}}
      ++(*day);

      *sec = secs % 60;
      *min = (secs / 60) % 60;
      *hour = (secs / 3600) % 24;

      *year = tyear;
}


main()
{
      unsigned long s,y,m,d,h,min,sec;


      do{
      printf("\nSeconds ");
      scanf("%li",&s);

      datum(s,&y,&m,&d,&h,&min,&sec);
      printf("\n %li:%li:%li  %li/%li/%li\n",h,min,sec,m,d,y);
      } while(s!=0);
}

sumant, the timestamp is not a bit field. Rather, the 32bit timestamp is the number of seconds from the year 1900 to now. So you cannot just extract the time from the bitfield, but rather have to convert the seconds.
Deighton, thanks for your suggestion, I believe it works. However, does anyone know of a smaller, more efficient algorithm? Ozo offered one last time which was about 18 lines, but I have a hard time understanding what it was doing.

I'm looking for an efficient algorithm, and someone to explain to me how that algorithm works. Ozo, if you could explain to me step by step how that algorithm worked it would be great too.

Sorry, opening up question to more suggestions
ozo's formula was based on all sorts of mathematical trickery to implicitly adjust for leap years etc.

I've got a hard time explaining it too.

My algorithm is not inefficient, not as efficient as the ozo formula, but i developed it to be more readily understandable.


Well, here's an implementation that I think is both pretty understandable and acceptably efficient, and it even works with Windows ;)

#define seconds_per_minute      (60L)
#define seconds_per_hour            (60L * seconds_per_minute)
#define seconds_per_day                  (24L * seconds_per_hour)

 const short __month_to_days[2][13] =
      {
      {0,31,59,90, 120, 151, 181, 212, 243, 273, 304,334, 365 },
      {0,31,60,91, 121, 152, 182, 213, 244, 274, 305,335, 366 }
      };

/*
 *      leap_year - return nonzero if year is a leap year, zero otherwise (year 0 = 1900)
 */

int __leap_year(int year);
int __leap_year(int year)
{
      return(__mod(year, 4) == 0 && (__mod(year, 100) != 0 || __mod(year, 400) == 100));
}

/*
 *      __time2tm - convert seconds since midnight, 1/1/1900 (or 1970 on Win32),
 *      to broken-down time
 */

#if (__dest_os == __win32_os  || __dest_os == __wince_os)
static void __time2tm(time_t inTime, struct tm * tm)
#else
static void __time2tm(time_t time, struct tm * tm)
#endif
{
      unsigned long      years, months, days, seconds;
      int                                    is_leap_year;

      #if (__dest_os == __win32_os  || __dest_os == __wince_os)
            /* Since Win32 time_t is relative to 1970 rather than 1900.
             * This must be of type unsigned long rather than a signed
             * time_t to prevent overflow */
            unsigned long time = inTime + ((365 * 70UL) + 17) * 24 * 60 * 60;
      #endif

      if (!tm)
            return;

      tm->tm_isdst = -1;

      days    = time / seconds_per_day;
      seconds = time % seconds_per_day;

      tm->tm_wday = (days + 1) % 7;
                  /* January 1, 1900 was a Monday */

      years = 0;

      for (;;)
      {
            unsigned long      days_this_year = __leap_year(years) ? 366 : 365;

            if (days < days_this_year)
                  break;

            days  -= days_this_year;
            years += 1;
      }

      tm->tm_year = years;
      tm->tm_yday = days;

      months = 0;

      is_leap_year = __leap_year(years);

      for (;;)
      {
            unsigned long days_thru_this_month = __month_to_days[is_leap_year][months+1];

            if (days < days_thru_this_month)
            {
                  days -= __month_to_days[is_leap_year][months];
                  break;
            }

            ++months;
      }

      tm->tm_mon  = months;
      tm->tm_mday = days + 1;

      tm->tm_hour = seconds / seconds_per_hour;

      seconds %= seconds_per_hour;

      tm->tm_min = seconds / seconds_per_minute;
      tm->tm_sec = seconds % seconds_per_minute;
}
ASKER CERTIFIED SOLUTION
Avatar of Alex Curylo
Alex Curylo

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial