_Portable Custom Time Structures_ by Donald Bryson Listing One /* Example of time functions and structures */ #include #include void main( void ) { struct tm *UTCtime; struct tm *LocalTime; long ltime; time( <ime ); /* _tzset() sets the following global variables that have been set in the environment: (1) _daylight Nonzero value if a daylight-saving-time zone is specified in the TZ setting; otherwise, 0 (2) _timezone Difference in seconds between UTC and local time (3) _tzname[0] String value of the three-letter time-zone name from the TZ environmental variable (4) _tzname[1] String value of the daylight-saving-time zone, or an empty string if the daylight-saving-time zone is omitted from the TZ environmental variable */ _tzset(); printf("Daylight Savings Flag: %d\n", _daylight); printf("Offset in seconds from UTC and local time: %d\n", _timezone); printf("Regular Time Zone: %s\n", (char *)_tzname[0]); printf("Daylight Savings Time Zone: %s\n", (char *)_tzname[1]); /* An example of incorrectly populating two tm structs */ /* Both gmtime() and locatime() use the same global static buffer */ /* for conversion. Calling the two functions and then trying */ /* to use the two pointers will result in an incorrect answer. */ /* The buffer contains UTC time after gmtime() is called, but */ /* it contains local time after locatime() is called. */ UTCtime = gmtime( <ime ); LocalTime = localtime( <ime ); /* a \n is not needed in the following printf statement. It is added to the string by asctime() */ printf( "Incorrect Universal Coordinated Time: %s", asctime(UTCtime) ); printf( "Correct Local Time: %s", asctime(LocalTime) ); /* This is one way of getting the both functions together */ UTCtime = gmtime( <ime ); /* Note: we use the tm we got */ printf( "Universal Coordinated Time is %s", asctime(UTCtime) ); /* Now we get the other tm */ LocalTime = localtime( <ime ); printf( "Local Time is %s", asctime(UTCtime) ); /* Note: You can also copy the global buffer to your own buffers with with each call. Use that method if you need to compare different tm structures. */ return; } Listing Two /* Example of custom time structures and functions */ #include #include /* ****************************************************************** */ /* This table is used to determine the number of days in the month given the month and the flag for leap year as determined by ItIsLeapYear()*/ static unsigned char daytable[2][13] = { {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; /* ****************************************************************** */ /* The Custom time structure tt */ typedef struct { unsigned int year; /* year stored as a full year, i.e., 19XX */ unsigned char month; /* month - Jan=1, Dec=12*/ unsigned char day; /* day 1-31 */ unsigned char hour; /* hour 0-59*/ unsigned char minute; /* minute 0-59 */ unsigned weekday; /* Day of week 0=Sunday, 6=Saturday */ int yearday; /* Day of year Jan 1 = 1 */ } tt; /* The following tm structure is only included for comparison */ #ifdef ONLYFORDISPLAY struct tm { /* Standard Section of the tm structure */ int tm_sec; /* Seconds - [0,59] */ int tm_min; /* Minutes - [0,59] */ int tm_hour; /* Hours - [0,23] */ int tm_mday; /* Day of the month - [1,31] */ int tm_mon; /* Months - [0,11] */ int tm_year; /* Years since 1900 */ int tm_wday; /* Days of week - [0,6] */ int tm_yday; /* Days since January 1 - [0,365] */ int tm_isdst; /* Daylight savings time flag */ }; #endif /* ****************************************************************** */ /* Employee Schedule Structures */ typedef struct { char In1hr; /* Hour of 1st Clock-in */ char In1mn; /* Minute of 1st Clock-in */ char Out1hr; /* Hour of 1st Clock-out */ char Out1mn; /* Minute of 1st Clock-out */ char In2hr; /* Hour of 2nd Clock-in */ char In2mn; /* Minute of 2nd Clock-in */ char Out2hr; /* Hour of 2nd Clock-out */ char Out2mn; /* Minute of 2nd Clock-out */ } DaySchedule; typedef struct { char StatusFlag; long ScheduleNum; char ScheduleName[21]; DaySchedule WorkDay[7]; } WeekSchedule; /* ****************************************************************** */ /* Determine if the given year is a leap year */ /* The year parameter must be a full year i.e. 1997 */ int ItIsLeapYear(int year) { if ( ( year % 4 == 0 ) && ( year % 100 != 0 ) || ( year % 400 == 0) ) return 1; else return 0; } /* ****************************************************************** */ /* Determine the number of days per year */ unsigned int days_yr(int year) { if (ItIsLeapYear(year)) return 366; else return 365; } /* ****************************************************************** */ /* Convert from the custom time structure, tt, into the standard time_t variable */ time_t tt_Convert(tt *ttPtr) { int unsigned t_year; int month; int leap; /* Flag to indicate if leap year is in effect */ time_t DaysInPartial; /* Number of Days in the Partial Year */ time_t EpochDate; struct tm *tmPtr; EpochDate = 0L; DaysInPartial = 0L; month = 0; /* Check the validity of the tt parameter */ leap = ItIsLeapYear(ttPtr->year); if ( ttPtr->yearday == -1 ) { return -1L; } if ( ttPtr->month > 12 ) { return -1L; } /* Use leap year in case current year is a leap year */ if ( ttPtr->day > daytable[leap][ttPtr->month] ) { return -1L; } if ( ttPtr->hour > 24 ) { return -1L; } if ( ttPtr->minute > 60 ) { return -1L; } /* Set the temporary year to the epoch year */ t_year = 1970; /* Calculate the number of days in each full year since 1970 */ while ( t_year < ttPtr->year ) { DaysInPartial += (long)days_yr(t_year); ++t_year; } /* Calculate the number of days in each month in current year */ while ( month < ttPtr->month ) { DaysInPartial += daytable[leap][month]; month++; } /* Add the number of full days this month */ DaysInPartial += (long)(ttPtr->day - 1); /* Now we know the number of days since Jan. 1, 1970 */ /* Convert the number of days to seconds */ EpochDate = (time_t)((time_t)DaysInPartial * (time_t)( 60L * 60L *24L) ); /* Convert the hours to seconds and add to total */ EpochDate += (time_t)((time_t)ttPtr->hour * 60L * 60L); /* Convert the minutes to seconds and add to total */ EpochDate += (time_t)((time_t)ttPtr->minute * 60L); /* Determine if Daylight Savings Time is in Effect */ /* And adjust for the time zone and daylight savings time */ /* Note: I included these lines only in the *NIX versions, but I am leaving it here for demonstration purposes only for MS-DOS programmers. */ tmPtr = localtime(&EpochDate); if ( tmPtr->tm_isdst > 0 ) { EpochDate -= ( 60L * 60L ); } EpochDate += timezone; /* Like the standard mktime() normalize the year day and week day. */ tmPtr = localtime(&EpochDate); ttPtr->yearday = tmPtr->tm_yday + 1; ttPtr->weekday = tmPtr->tm_wday; return EpochDate; } /* convert from a time_t variable to a tt variable */ tt *Timet_to_tt(time_t tlong, tt *ttPtr) { struct tm *t; t = localtime(&tlong); /* Code will break in 2070 */ if ( t->tm_year > 70 ) { ttPtr->year = t->tm_year + 1900; } else { ttPtr->year = t->tm_year + 2000; } ttPtr->month = t->tm_mon + 1; ttPtr->day = t->tm_mday; ttPtr->hour = t->tm_hour; ttPtr->minute = t->tm_min; ttPtr->yearday = t->tm_yday + 1; ttPtr->weekday = t->tm_wday; return ttPtr; } void main(void) { tt OurNewStruct; struct tm *UTCtime; struct tm *LocalTime; long ltime; /* Set the global time zone variables */ _tzset(); time( <ime ); printf("The time_t for the current time: %ld\n", ltime); /* Obtain Both local time and UTC */ UTCtime = gmtime( <ime ); printf( "Universal Coordinated Time is %s", asctime(UTCtime) ); LocalTime = localtime( <ime ); printf( "Local Time is using tm %s", asctime(UTCtime) ); /* Populate the custom structure tt from time_t */ Timet_to_tt(ltime, &OurNewStruct); printf("tt converted from current time_t: %d/%d/%d %d:%d\n", OurNewStruct.month, OurNewStruct.day, OurNewStruct.year, OurNewStruct.hour, OurNewStruct.minute); /* Convert from tt to time_t */ ltime = tt_Convert(&OurNewStruct); LocalTime = localtime(<ime); printf("The time_t variable calculated tt: %ld\n", ltime); printf("Local Time from tm calculated from time_t calculated from tt: %s", asctime(LocalTime) ); } Listing Three REM MS-DOS BATCH for playing with Listing 1 and Listing 2 rem The syntax to setting the TZ environment variable is rem set TZ=tzn[+ | -]hh[:mm[:ss] ][dzn] set TZ=EDT4EST LISTING1 LISTING2