/*============================================================================= * drt/sys/ktime.c * DRT primitive date and time functions for the 'drt_time_t' type. * * Notes * Date calculations are more easily performed assuming a nice 400-year * epoch, which is why TIME_ZDOFFSET is subtracted from the drt_time_t * values before further calculations are performed. This converts the * time in the "proper epoch" into a time in the "adjusted epoch". * * Any date can be used as the zero date (ZD) of the epoch, but the * TIME_ZDOFFSET macro must be properly defined so that subtracting its * value (which is the number of days difference between it and the ZD) * from the ZD will result in a date of the form: * YYYY-01-01 00:00:00.0000000 Z * where (YYYY - 1) is a year divisible by 400, and the TIME_Z400YR macro * is set equal to YYYY. The year YYYY is the center date of the adjusted * epoch. The 'TIME_ZDYEAR' macro must therefore be defined as equal to * the year number for the ZD. * * The 'TIME_ZWKDAY' macro is equal to the day of the week of the first * day of the adjusted 400-year epoch, which is Monday for all years of * the form CY01, where CY00 is a multiple of 400. * * See also * ktime.h * * History * 0.01, 1999-03-09, David R Tribble. * First cut. * * 0.02, 1999-03-22, David R Tribble. * Converted from C++ to C. * Added drt_time_gmtime(). * * 0.03, 1999-04-24, David R Tribble. * Working drt_time_gmtime(). * * 0.04, 1999-04-28, David R Tribble. * Working drt_time_make(). * * 0.05, 1999-05-01, David R Tribble. * Working test cases. * * 0.06, 1999-08-12, David R Tribble. * Added drt_time_adjtz(). * * Copyright ©1999, by David R. Tribble, all rights reserved. * See "drt/sys/copyr.txt" for more information. *----------------------------------------------------------------------------*/ /* Identification */ static const char id[] = "@(#)drt/sys/ktime.c 0.06"; /* System includes */ #include #define drt_std_assert_h 1 #include #define drt_std_limits_h 1 #include #define drt_std_string_h 1 #if TEST #include #define drt_std_stdio_h 1 #endif #if DEBUG #include #define drt_std_stdio_h 1 #endif /* Special includes */ #include "kdefs.h" /* Local includes */ #include "kdebug.h" #include "ktimec.h" #include "ktime.h" /* Local macros */ #if TEST #if DEBUG #define T(e) (e) #else #define T(e) (0) #endif #else #define T(e) (0) #endif /*----------------------------------------------------------------------------- * Local constants *----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- * drt_time_mdays[] * Contains the accumulated number of days for the first day of each * month. The array is based on Jan-01 being the very first day of the * adjusted 400-year epoch. * * Caveats * The TIME_ZDOFFSET macro must reflect the fact that Jan-01 is the first * day of the adjusted 400-year epoch. *----------------------------------------------------------------------------*/ const short drt_time_mdays[] = { /* 0 Jan */ 0, /* 0 */ /* 1 Feb */ 31, /* 31 */ /* 2 Mar */ 31+28, /* 59 */ /* 3 Apr */ 31+28+31, /* 90 */ /* 4 May */ 31+28+31+30, /* 120 */ /* 5 Jun */ 31+28+31+30+31, /* 151 */ /* 6 Jul */ 31+28+31+30+31+30, /* 181 */ /* 7 Aug */ 31+28+31+30+31+30+31, /* 212 */ /* 8 Sep */ 31+28+31+30+31+30+31+31, /* 243 */ /* 9 Oct */ 31+28+31+30+31+30+31+31+30, /* 273 */ /* 10 Nov */ 31+28+31+30+31+30+31+31+30+31, /* 304 */ /* 11 Dec */ 31+28+31+30+31+30+31+31+30+31+30, /* 334 */ /* 12 - */ 31+28+31+30+31+30+31+31+30+31+30+32, /* 366 */ }; #if TEST static const char wday_names[][3+1] = { "-7?", "-6?", "-5?", "-4?", "-3?", "-2?", "-1?", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "+7?", "+8?", "+9?" }; static const char (*const wday)[3+1] = &wday_names[+7]; #endif /* TEST */ /*----------------------------------------------------------------------------- * Public constants *----------------------------------------------------------------------------*/ #if DRT_TIME_VS != 100 #error DRT_TIME_VS has changed #endif const int drt_time_vs = DRT_TIME_VS; /* Typedef version number */ const int DRT_TIME_ZYEAR = TIME_ZDYEAR; /* Zero year of the epoch */ const drt_time_t DRT_TIME_MIN = TIME_MIN; /* BC9999-01-01 00:00:00.000 Z */ const drt_time_t DRT_TIME_MAX = TIME_MAX; /* AD9999-12-31 23:59:59.999 Z */ const drt_time_t DRT_TIME_UNKNOWN = TIME_UNKNOWN; /* Unknown date */ const drt_time_t DRT_TIME_NEVER = TIME_NEVER; /* Never date */ const long int DRT_TIME_TICKS_PER_SEC = TICKS_PER_SEC; /* Number of ticks in a second */ const drt_int64_t DRT_TIME_TICKS_PER_DAY = TICKS_PER_DAY; /* Number of ticks in a day */ const drt_int64_t DRT_TIME_TICKS_PER_YEAR = TICKS_PER_YEAR; /* Number of ticks in a year */ const drt_time_t DRT_TIME_BC4713 = MAKE_TICKS(-4712, 0, 12,0,0,0); /* BC4713-01-01 12:00:00.000 Z */ const drt_time_t DRT_TIME_BC0001 = MAKE_TICKS(+0000, 0, 0,0,0,0); /* BC0001-01-01 00:00:00.000 Z */ const drt_time_t DRT_TIME_AD0001 = MAKE_TICKS(+0001, 0, 0,0,0,0); /* AD0001-01-01 00:00:00.000 Z */ const drt_time_t DRT_TIME_AD1601 = MAKE_TICKS(+1601, 0, 0,0,0,0); /* AD1601-01-01 00:00:00.000 Z */ const drt_time_t DRT_TIME_AD1865 = MAKE_TICKS(+1865, 0, 0,0,0,0); /* AD1865-01-01 00:00:00.000 Z */ const drt_time_t DRT_TIME_AD1858 = MAKE_TICKS(+1858, 304-1+17, 0,0,0,0); /* AD1858-11-17 00:00:00.000 Z */ const drt_time_t DRT_TIME_AD1900 = MAKE_TICKS(+1900, 0, 0,0,0,0); /* AD1900-01-01 00:00:00.000 Z */ const drt_time_t DRT_TIME_AD1970 = MAKE_TICKS(+1970, 0, 0,0,0,0); /* AD1970-01-01 00:00:00.000 Z */ const drt_time_t DRT_TIME_AD1972 = MAKE_TICKS(+1972, 0, 0,0,0,0); /* AD1972-01-01 00:00:00.000 Z */ const drt_time_t DRT_TIME_AD1980 = MAKE_TICKS(+1980, 0, 0,0,0,0); /* AD1980-01-01 00:00:00.000 Z */ const drt_time_t DRT_TIME_AD2000 = MAKE_TICKS(+2000, 0, 0,0,0,0); /* AD2000-01-01 00:00:00.000 Z */ const drt_time_t DRT_TIME_AD2001 = MAKE_TICKS(+2001, 0, 0,0,0,0); /* AD2001-01-01 00:00:00.000 Z */ /*----------------------------------------------------------------------------- * Public functions *----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- * drt_time_gmtime() * Convert time value 't' into its broken-down form, placing the results * into struct 'th'. * * Returns * True if successful, otherwise false on error. *----------------------------------------------------------------------------*/ bool drt_time_gmtime(struct drt_tm *th, drt_time_t t) { #if DRT_TIME_VS != 100 #error DRT_TIME_VS has changed #endif #if DRT_TM_VS != 101 #error DRT_TM_VS has changed #endif long int yr; long int t2; int yd; int d; /* Check args */ if (th == null) return (false); /* Reset some struct members */ #if TEST memset(th, 0, sizeof(*th)); th->tm_vers = DRT_TM_VS; #else drt_tm_zero(th); #endif T(printf("> %+020" LLONG_FMT "d:", t)); /* Check for special cases */ if (t == TIME_UNKNOWN) { /* Time value is 'unknown' */ th->tm_year = TIME_YR_UNKNOWN; T(printf(" UNKNOWN\n")); return (true); } if (t == TIME_NEVER) { /* Time value is 'never' */ th->tm_year = TIME_YR_NEVER; T(printf(" NEVER\n")); return (true); } if (t < TIME_MIN or t > TIME_MAX) { /* Time value is outside the epoch, and thus invalid */ th->tm_year = TIME_YR_UNKNOWN; T(printf(" INVALID\n")); return (false); } /* Convert ticks 't' into broken-down time */ /* Adjust 't' to be within a 400-year cycle (ZD%400) */ t -= TIME_ZDOFFSET*TICKS_PER_DAY; /* Adjusted epoch */ T(printf(" adj:%+020" LLONG_FMT "d\n", t)); /* Adjust 't' to be within 400 years of the ZD */ if (t < 0) { /* Add a multiple of 400 years to make 't' positive */ yr = t/TICKS_PER_400YR - 1; } else { /* Adjust 't' to be within 400 years of the ZD */ yr = t/TICKS_PER_400YR; } t -= yr*TICKS_PER_400YR; /* Ticks within 400-yr cycle */ yr *= 400; /* First year of 400-yr cycle */ T(printf("> 400 x %ld: yr=%04ld, sub %+" LLONG_FMT "d, " "t=%+" LLONG_FMT "d\n", yr/400, yr, yr/400*TICKS_PER_400YR, t)); /* Determine the nanoseconds */ t2 = t%TICKS_PER_SEC; /* Ticks since the second */ t /= TICKS_PER_SEC; /* Seconds since ZD%400 (exact) */ th->tm_nsec = t2*NSEC_PER_TICKS; T(printf("> %+" LLONG_FMT "d.%07ld sec-since-zd400(%04ld)\n", t, t2, yr)); /* Determine the time (HH:MM:SS) */ t2 = t%SECS_PER_DAY; /* Seconds since midnight */ th->tm_hour = t2/3600; t2 %= 3600; th->tm_min = t2/60; t2 %= 60; th->tm_sec = t2; T(printf("> %+03d.%+03d.%+03d.+%010d\n", th->tm_hour, th->tm_min, th->tm_sec, th->tm_nsec)); /* Determine the day of the week */ t2 = t/SECS_PER_DAY; /* Days since ZD%400 */ th->tm_wday = (t2+TIME_ZWKDAY)%7; /* Determine the year and day of the year */ { long int p; long int t3; T(printf("> 400yr x %ld, ", yr/400)); /* Determine the 100-year period within the 400-year cycle */ t3 = t2; p = t3/DAYS_PER_100YR; /* 100yr periods since ZD%400 */ if (p >= 400/100) { T(printf("[>100]")); p = 400/100 - 1; } yr += p*100; t3 -= p * DAYS_PER_100YR; /* Days since ZD%100 */ T(printf("100yr x %ld, ", p)); /* Determine the 4-year period within the 100-year cycle */ p = t3/DAYS_PER_4YR; /* 4yr periods since ZD%100 */ yr += p*4; t3 -= p * DAYS_PER_4YR; /* Days since ZD%4 */ T(printf("4yr x %ld, ", p)); /* Determine the 1-year period within the 4-year cycle */ p = t3/DAYS_PER_YEAR; /* 1yr periods since ZD%4 */ if (p >= 4/1) { T(printf("[>4]")); p = 4/1 - 1; } yr += p; t3 -= p * DAYS_PER_YEAR; /* Days since Jan-01 */ /* Determine the day within the year */ yd = t3; T(printf("1yr x %ld", p)); T(printf(" => year=%ld+%ld(%ld), yday=%d\n", yr, (long)TIME_ZDYEAR, yr+TIME_ZDYEAR, yd)); yr += TIME_ZDYEAR; /* Year number (exact) */ th->tm_year = yr; th->tm_yday = yd; } /* Determine the month and day of the month */ { bool lept; /* Determine leap days for the year */ if (yr%4 == 0 and yr%100 != 0 or yr%400 == 0) T(printf("[leapyear] ")); lept = false; if (yd >= 31+28 /*drt_time_mdays[2]*/ and (yr%4 == 0 and (yr%100 != 0 or yr%400 == 0))) { T(printf("[add leapday]\n")); yd--; /* Adjust for a leap day in this year */ lept = true; } else T(printf("[no leapday]\n")); #if TEST /* Sanity check */ if (yd < 0 or yd > 365) { printf("*** yday out of range: %d\n", yd); return (false); } fflush(stdout); #endif /* Determine the month */ d = yd/31; /* First guess */ if (d < 11 and yd >= drt_time_mdays[d+1]) d++; /* Corrected guess */ th->tm_mon = d; T(printf("> mon=%d+1, year=%04d, yday=%03d(%03d)\n", d, th->tm_year, th->tm_yday, yd)); /* Determine the day of the month */ d = yd - drt_time_mdays[d] + 1; /* Fix up special case of Feb-29 */ if (yd == 31+28-1 and lept) { T(printf("> [corrected leapday, %d -> %d]\n", d, d+1)); d++; } th->tm_mday = d; } return (true); } /*----------------------------------------------------------------------------- * drt_time_make() * Convert the broken-down time in struct 'tm' into its equivalent time * value, storing the results in 'th'. * * Returns * True if successful and 'th' is set to the converted time value, * otherwise false on error and 'th' is set to an 'unknown' time value. * * Caveats * The 'tm_vers' member of struct 't' must be set properly, otherwise this * function returns false. *----------------------------------------------------------------------------*/ bool drt_time_make(drt_time_t *th, struct drt_tm *t) { #if DRT_TIME_VS != 100 #error DRT_TIME_VS has changed #endif #if DRT_TM_VS != 101 #error DRT_TM_VS has changed #endif /* Check args */ if (th == null) return (false); T(printf("\n")); if (t == null or t->tm_vers != DRT_TM_VS) { /* Improper time value */ T(printf("] tm null or bad vers\n")); *th = TIME_UNKNOWN; return (false); } /* Normalize the members of 't' */ if (not drt_tm_normalize(t)) { /* Can't normalize the time */ T(printf("] Can't normalize tm\n")); *th = TIME_NEVER; return (false); } /* Check for special cases */ if (t->tm_year == TIME_YR_UNKNOWN) { /* Date is 'unknown' */ T(printf("] Unknown\n")); *th = TIME_UNKNOWN; return (true); } if (t->tm_year == TIME_YR_NEVER) { /* Date is 'never' */ T(printf("] Never\n")); *th = TIME_NEVER; return (true); } /* Convert normalized broken-down time 't' into ticks */ { drt_time_t v; long yr; v = 0; yr = t->tm_year; if (yr < TIME_Z400YR) { long yoff; /* Handle dates prior to ZD%400 */ yoff = ((TIME_Z400YR - yr)/400 + 1); v -= yoff * DAYS_PER_400YR; yr += yoff*400; T(printf("] adj %+ld x 400 = %+ld years (%+ld days)\n", -yoff, -yoff*400, -yoff*DAYS_PER_400YR)); } yr -= TIME_Z400YR; v += yr/400 * DAYS_PER_400YR + yr%400/100 * DAYS_PER_100YR + yr%100/4 * DAYS_PER_4YR + yr%4 * DAYS_PER_YEAR + t->tm_yday; /* Days */ T(printf("] days %+20" LLONG_FMT "d\n", v)); v += TIME_ZDOFFSET; T(printf("] zdays %+20" LLONG_FMT "d\n", v)); v = v * SECS_PER_DAY + (long)t->tm_hour * 60*60 + (long)t->tm_min * 60 + (long)t->tm_sec; /* Seconds */ T(printf("] secs %+20" LLONG_FMT "d\n", v)); v = v * TICKS_PER_SEC + (long)t->tm_nsec / NSEC_PER_TICKS; /* Ticks */ T(printf("] ticks %+20" LLONG_FMT "d\n", v)); /* Set the time (ticks) value */ *th = v; } return (true); } /*----------------------------------------------------------------------------- * drt_time_is_valid() * Determines if time value 'th' is valid, i.e., contains a value within * the normal range of dates within the epoch. * * Returns * True if time 'th' is valid, otherwise false. *----------------------------------------------------------------------------*/ bool drt_time_is_valid(drt_time_t th) { #if DRT_TIME_VS != 100 #error DRT_TIME_VS has changed #endif /* Check for special cases */ if (th >= TIME_MIN and th <= TIME_MAX) return (true); /* Is within the epoch range */ if (th == TIME_UNKNOWN) return (true); /* Is 'unknown' */ if (th == TIME_NEVER) return (true); /* Is 'never' */ return (false); } /*----------------------------------------------------------------------------- * drt_time_is_unknown() * Determines if time value 'th' is "unknown". * * Returns * True if time 'th' is unknown, otherwise false. *----------------------------------------------------------------------------*/ bool drt_time_is_unknown(drt_time_t th) { #if DRT_TIME_VS != 100 #error DRT_TIME_VS has changed #endif /* Check for special cases */ return (th == TIME_UNKNOWN); } /*----------------------------------------------------------------------------- * drt_time_is_never() * Determines if time value 'th' is "never". * * Returns * True if time 'th' is never, otherwise false. *----------------------------------------------------------------------------*/ bool drt_time_is_never(drt_time_t th) { #if DRT_TIME_VS != 100 #error DRT_TIME_VS has changed #endif /* Check for special cases */ return (th == TIME_NEVER); } /*----------------------------------------------------------------------------- * drt_time_is_leap() * Determines if year 'yr' is a leap year. * * Returns * True if the year is a leap year, otherwise false. *----------------------------------------------------------------------------*/ bool drt_time_is_leap(long yr) { #if DRT_TIME_VS/100 != 1 #error DRT_TIME_VS has changed #endif #if DEBUG >= 2 DrtTrace dbg("drt_time_is_leap"); #endif /* Check for leap year */ if (yr % 4 != 0) return (false); if (yr % 100 != 0) return (true); if (yr % 400 != 0) return (false); return (true); } /*----------------------------------------------------------------------------- * drt_time_adjtz() * Adjusts times 'th' by adding the timezone offset in 'tz' to it. * * Returns * True if successful and 'th' is set to the converted time value, * otherwise false on error and 'th' is set to an 'unknown' time value. * * Caveats * The 'tz_vers' member of struct 'tz' must be set properly, otherwise * this function returns false. * * Pointer 'tz' can be null, in which case no adjustment to 'th' is made. *----------------------------------------------------------------------------*/ bool drt_time_adjtz(drt_time_t *th, const struct drt_tz *tz) { #if DRT_TIME_VS != 100 #error DRT_TIME_VS has changed #endif #if DRT_TZ_VS != 101 #error DRT_TZ_VS has changed #endif /* Check args */ if (th == null) return (false); if (tz == null) return (true); if (not drt_tz_validate(tz)) return (false); /* Add the timezone offset to the time */ { drt_time_t n; n = (long)(tz->tz_hour * 60 + tz->tz_min) * 60 + tz->tz_sec; n *= TICKS_PER_SEC; *th += n; } return (true); } /*===========================================================================*/ /*===========================================================================*/ #if TEST == 2 static int leapday = 0; /*----------------------------------------------------------------------------- * main() * Test driver. *----------------------------------------------------------------------------*/ int main(void) { int yd; for (yd = 0; yd <= 365; yd++) { int d; int j; #if 0 if (yd%20 == 0) printf("\n%3d. ", yd); #else printf("\n%3d. ", yd); #endif /* Guess the month */ d = yd/31; /* First guess */ #ifdef OLD___ if (yd+leapday >= drt_time_mdays[d+1]) d++; #else if (yd >= drt_time_mdays[d+1]) d++; #endif printf("%2dm", (d+2)%12); #if 1 j = yd - drt_time_mdays[d]; j = yd+leapday - drt_time_mdays[d] + 1; printf(j <= 1 ? " >" : " "); printf("%2d/%02d", (d+2)%12+1, j); #endif /* Check out guess */ if (yd < drt_time_mdays[d]) printf("<"); else if (d > 0 and yd < drt_time_mdays[d-1]) printf("*"); else if (yd > drt_time_mdays[d+1]) printf("."); else printf(" "); } printf("\n"); return (0); } #endif /* TEST == 2 */ /*===========================================================================*/ /*===========================================================================*/ #if TEST == 1 /*----------------------------------------------------------------------------- * Local constants *----------------------------------------------------------------------------*/ static const drt_time_t EMPTY; static const drt_time_t SKIP; static const drt_time_t ZD = /* ZD */ 0; static const drt_time_t ZDSUB1 = /* ZD-1 */ -1; static const drt_time_t ZDADD1 = /* ZD+1 */ +1; static const drt_time_t XMAS = /* BC0003-12-25 02:00:00.000 Z */ MAKE_TICKS(-0002, 365-31+25-1, 2,0,0,0); static const drt_time_t AD2002 = /* AD2002-01-01 00:00:00.000 Z */ MAKE_TICKS(+2002, 1-1, 0,0,0,0); static const drt_time_t AD2100 = /* AD2100-01-01 00:00:00.000 Z */ MAKE_TICKS(+2100, 1-1, 0,0,0,0); static const drt_time_t AD2400 = /* AD2400-12-31 23:59:59.999 Z */ MAKE_TICKS(+2401, 1-1, 0,0,0,0) - 1; static const drt_time_t AD2401 = /* AD2401-01-01 00:00:00.000 Z */ MAKE_TICKS(+2401, 1-1, 0,0,0,0); static const drt_time_t AD2999 = /* AD2100-01-01 00:00:00.000 Z */ MAKE_TICKS(2999, 1-1, 0,0,0,0); static const drt_time_t TM111111 = /* AD2002-02-02 01:01:01:001 Z */ ((365 + 31 + 1)*SECS_PER_DAY + 60*60 + 60 + 1)*(drt_time_t)TICKS_PER_SEC + 10000; static const drt_time_t D01JAN1601 = /* AD1601-01-01 00:00:00.000 Z */ (+1601 - TIME_ZDYEAR) * TICKS_PER_YEAR; static const drt_time_t D01JAN1701 = /* AD1701-01-01 00:00:00.000 Z */ (+1601 - TIME_ZDYEAR) * TICKS_PER_YEAR + (+1701-1601)/100 * TICKS_PER_100YR; static const drt_time_t D01JAN1801 = /* AD1801-01-01 00:00:00.000 Z */ (+1601 - TIME_ZDYEAR) * TICKS_PER_YEAR + (+1801-1601)/100 * TICKS_PER_100YR; static const drt_time_t D01JAN1901 = /* AD1901-01-01 00:00:00.000 Z */ (+1601 - TIME_ZDYEAR) * TICKS_PER_YEAR + (+1901-1601)/100 * TICKS_PER_100YR; static const drt_time_t D01JAN1970 = /* AD1970-01-01 00:00:00.000 Z */ (+1601 - TIME_ZDYEAR) * TICKS_PER_YEAR + (+1970-1601)/100 * TICKS_PER_100YR + (+1970-1901) * 36525/100 * TICKS_PER_DAY; static const drt_time_t D01JAN1980 = /* AD1980-01-01 00:00:00.000 Z */ (+1601 - TIME_ZDYEAR) * TICKS_PER_YEAR + (+1980-1601)/100 * TICKS_PER_100YR + (+1980-1901) * 36525/100 * TICKS_PER_DAY; static const drt_time_t D01JAN1980_A = /* AD1980-01-01 00:00:00.000 Z */ MAKE_TICKS(+1980, 0, 0,0,0,0); static const drt_time_t D01JAN2000 = /* AD2000-01-01 00:00:00.000 Z */ MAKE_TICKS(+2000, 1-1, 0,0,0,0); static const drt_time_t D28FEB2000 = /* AD2000-02-28 00:00:00.000 Z */ MAKE_TICKS(+2000, 31-1+28, 0,0,0,0); static const drt_time_t D29FEB2000 = /* AD2000-02-29 00:00:00.000 Z */ MAKE_TICKS(+2000, 31+29-1, 0,0,0,0); static const drt_time_t D01MAR2000 = /* AD2000-03-01 00:00:00.000 Z */ MAKE_TICKS(+2000, 31+29+1-1, 0,0,0,0); static const drt_time_t D02MAR2000 = /* AD2000-03-02 00:00:00.000 Z */ MAKE_TICKS(+2000, 31+29+2-1, 0,0,0,0); static const drt_time_t D01JAN2001 = /* AD2001-01-01 00:00:00.000 Z */ MAKE_TICKS(+2001, 1-1, 0,0,0,0); static const drt_time_t D01JAN2001N = /* AD2001-01-01 12:00:00.000 Z */ MAKE_TICKS(+2001, 1-1, 12,0,0,0); static const drt_time_t D02JAN2001 = /* AD2001-01-02 00:00:00.000 Z */ MAKE_TICKS(+2001, 2-1, 0,0,0,0); static const drt_time_t D01MAR2001 = /* AD2001-03-01 00:00:00.000 Z */ MAKE_TICKS(+2001, 31+28+1-1, 0,0,0,0); static const drt_time_t D02MAR2001 = /* AD2001-03-02 00:00:00.000 Z */ MAKE_TICKS(+2001, 31+28+2-1, 0,0,0,0); static const drt_time_t D31MAR2001 = /* AD2001-03-31 00:00:00.000 Z */ MAKE_TICKS(+2001, 31+28+31-1, 0,0,0,0); static const drt_time_t D01APR2001 = /* AD2001-04-01 00:00:00.000 Z */ MAKE_TICKS(+2001, 31+28+31+1-1, 0,0,0,0); static const drt_time_t D31DEC2001 = /* AD2001-12-31 00:00:00.000 Z */ MAKE_TICKS(+2001, DAYS_PER_YEAR-1, 0,0,0,0); static const drt_time_t D01JAN2002 = /* AD2002-01-01 00:00:00.000 Z */ MAKE_TICKS(+2002, 0, 0,0,0,0); static const drt_time_t D01JAN2002_B = /* AD2002-01-01 00:00:00.000 Z */ MAKE_TICKS(+2001, DAYS_PER_YEAR, 0,0,0,0); static const drt_time_t D01JAN2003 = /* AD2003-01-01 00:00:00.000 Z */ MAKE_TICKS(+2001, 2*DAYS_PER_YEAR, 0,0,0,0); static const drt_time_t D01JAN2004 = /* AD2004-01-01 00:00:00.000 Z */ MAKE_TICKS(+2001, (2004-2001)*DAYS_PER_YEAR, 0,0,0,0); static const drt_time_t D29FEB2004 = /* AD2004-02-29 00:00:00.000 Z */ MAKE_TICKS(+2004, 31+29-1, 0,0,0,0); static const drt_time_t D01JAN2005 = /* AD2005-01-01 00:00:00.000 Z */ MAKE_TICKS(+2001, (2005-2001)*DAYS_PER_YEAR + (2005-2001)/4, 0,0,0,0); static const drt_time_t D01JAN2099 = /* AD2099-01-01 00:00:00.000 Z */ MAKE_TICKS(2099, 1-1, 0,0,0,0); static const drt_time_t D01JAN2100 = /* AD2100-01-01 00:00:00.000 Z */ MAKE_TICKS(2100, 1-1, 0,0,0,0); static const drt_time_t D01JAN2200 = /* AD2200-01-01 00:00:00.000 Z */ MAKE_TICKS(2200, 1-1, 0,0,0,0); static const drt_time_t D01JAN2300 = /* AD2300-01-01 00:00:00.000 Z */ MAKE_TICKS(2300, 1-1, 0,0,0,0); static const drt_time_t D01JAN2400 = /* AD2400-01-01 00:00:00.000 Z */ MAKE_TICKS(2400, 1-1, 0,0,0,0); static const drt_time_t DMIN = /* BC9999-01-01 00:00:00.000 Z */ MAKE_TICKS(TIME_MINYEAR, 0, 0,0,0,0); static const drt_time_t DMAX = /* AD9999-12-31 23:59:59.999 Z */ MAKE_TICKS(TIME_MAXYEAR+1, 0, 0,0,0,0) - 1; static const drt_time_t DUNKNOWN = /* BC10000-12-31 23:59:59.999 Z */ MAKE_TICKS(TIME_MINYEAR, 0, 0,0,0,0) - 1; static const drt_time_t DNEVER = /* AD10000-01-01 00:00:00.000 Z */ MAKE_TICKS(TIME_MAXYEAR+1, 0, 0,0,0,0); /*----------------------------------------------------------------------------- * Local types and test constants *----------------------------------------------------------------------------*/ struct Item { const drt_time_t * t; /* Ticks */ struct drt_tm ts; /* Time value */ }; static const struct Item dates[] = { /*[0]*/ { &DRT_TIME_UNKNOWN, /* Unknown date */ { DRT_TM_VS, TIME_YR_UNKNOWN, 0, 0, 0, 0, 0, 0, -1, -1 }}, { &DRT_TIME_NEVER, /* Never date */ { DRT_TM_VS, TIME_YR_NEVER, 0, 0, 0, 0, 0, 0, -1, -1 }}, { &DRT_TIME_MIN, /* BC9999-01-01 00:00:00.000 Z */ { DRT_TM_VS, -9998, 1, 1, 0, 0, 0, 0, 1-1, +2 }}, { &DRT_TIME_MAX, /* AD9999-12-31 23:59:59.999 Z */ { DRT_TM_VS, +9999,12,31,23,59,59, 999999900, 365-1, +5 }}, { &ZD, /* ZD */ { DRT_TM_VS, +2001, 1, 1, 0, 0, 0, 0, 1-1, +1 }}, { &ZDSUB1, /* ZD-1 */ { DRT_TM_VS, +2000,12,31,23,59,59, 999999900, 366-1, +0 }}, { &ZDADD1, /* ZD+1 */ { DRT_TM_VS, +2001, 1, 1, 0, 0, 0, 100, 1-1, +1 }}, { &XMAS, /* BC0003-12-25 02:00:00.000 Z */ { DRT_TM_VS, -0002,12,25, 2, 0, 0, 0, -1, +5 }}, { &EMPTY }, { &EMPTY }, /*[10]*/ { &SKIP }, { &DRT_TIME_BC4713, /* BC4713-01-01 12:00:00.000 Z */ { DRT_TM_VS, -4712, 1, 1,12, 0, 0, 0, 1-1, +4 }}, { &DRT_TIME_BC0001, /* BC0001-01-01 00:00:00.000 Z */ { DRT_TM_VS, -0000, 1, 1, 0, 0, 0, 0, 0, +6 }}, { &DRT_TIME_AD0001, /* AD0001-01-01 00:00:00.000 Z */ { DRT_TM_VS, +0001, 1, 1, 0, 0, 0, 0, 0, +1 }}, { &DRT_TIME_AD1601, /* AD1601-01-01 00:00:00.000 Z */ { DRT_TM_VS, +1601, 1, 1, 0, 0, 0, 0, 0, +1 }}, { &DRT_TIME_AD1858, /* AD1858-11-17 00:00:00.000 Z */ { DRT_TM_VS, +1858,11,17, 0, 0, 0, 0, -1, +3 }}, { &DRT_TIME_AD1900, /* AD1900-01-01 00:00:00.000 Z */ { DRT_TM_VS, +1900, 1, 1, 0, 0, 0, 0, 0, +1 }}, { &DRT_TIME_AD1970, /* AD1970-01-01 00:00:00.000 Z */ { DRT_TM_VS, +1970, 1, 1, 0, 0, 0, 0, 0, +4 }}, { &DRT_TIME_AD1972, /* AD1972-01-01 00:00:00.000 Z */ { DRT_TM_VS, +1972, 1, 1, 0, 0, 0, 0, 0, +6 }}, { &DRT_TIME_AD1980, /* AD1980-01-01 00:00:00.000 Z */ { DRT_TM_VS, +1980, 1, 1, 0, 0, 0, 0, 0, +2 }}, /*[20]*/ { &DRT_TIME_AD2000, /* AD2000-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2000, 1, 1, 0, 0, 0, 0, 0, +6 }}, { &DRT_TIME_AD2001, /* AD2001-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2001, 1, 1, 0, 0, 0, 0, 1-1, +1 }}, { &EMPTY }, { &EMPTY }, { &EMPTY }, { &EMPTY }, { &EMPTY }, { &EMPTY }, { &EMPTY }, { &EMPTY }, /*[30]*/ { &SKIP }, { &AD2002, /* AD2002-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2002, 1, 1, 0, 0, 0, 0, 1-1, +2 }}, { &AD2100, /* AD2100-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2100, 1, 1, 0, 0, 0, 0, 1-1, +5 }}, { &AD2400, /* AD2400-12-31 23:59:59.999 Z */ { DRT_TM_VS, +2400,12,31,23,59,59, 999999900, -1, +0 }}, { &AD2401, /* AD2401-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2401, 1, 1, 0, 0, 0, 0, 1-1, +1 }}, { &AD2999, /* AD2999-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2999, 1, 1, 0, 0, 0, 0, 1-1, +2 }}, { &TM111111, /* AD2002-02-02 01:01:01:001 Z */ { DRT_TM_VS, +2002, 2, 2, 1, 1, 1, 1000000, 31+2-1, +6 }}, { &EMPTY }, { &EMPTY }, { &EMPTY }, /*[40]*/ { &SKIP }, { &D01JAN2000, /* AD2000-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2000, 1, 1, 0, 0, 0, 0, 1-1, +6 }}, { &D28FEB2000, /* AD2000-02-28 00:00:00.000 Z */ { DRT_TM_VS, +2000, 2,28, 0, 0, 0, 0, 31+28-1, +1 }}, { &D29FEB2000, /* AD2000-02-29 00:00:00.000 Z */ { DRT_TM_VS, +2000, 2,29, 0, 0, 0, 0, 31+29-1, +2 }}, { &D01MAR2000, /* AD2000-03-01 00:00:00.000 Z */ { DRT_TM_VS, +2000, 3, 1, 0, 0, 0, 0, 31+29+1-1, +3 }}, { &D02MAR2000, /* AD2000-03-01 00:00:00.000 Z */ { DRT_TM_VS, +2000, 3, 2, 0, 0, 0, 0, 31+29+2-1, +4 }}, { &D29FEB2004, /* AD2004-02-29 00:00:00.000 Z */ { DRT_TM_VS, +2004, 2,29, 0, 0, 0, 0, 31+29-1, +0 }}, { &D01JAN2001, /* AD2001-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2001, 1, 1, 0, 0, 0, 0, 1-1, +1 }}, { &D01JAN2001N, /* AD2001-01-01 12:00:00.000 Z */ { DRT_TM_VS, +2001, 1, 1,12, 0, 0, 0, 1-1, +1 }}, { &D02JAN2001, /* AD2001-01-02 00:00:00.000 Z */ { DRT_TM_VS, +2001, 1, 2, 0, 0, 0, 0, 2-1, +2 }}, /*[50]*/ { &D01MAR2001, /* AD2001-03-01 00:00:00.000 Z */ { DRT_TM_VS, +2001, 3, 1, 0, 0, 0, 0, 31+28+1-1, +4 }}, { &D02MAR2001, /* AD2001-03-02 00:00:00.000 Z */ { DRT_TM_VS, +2001, 3, 2, 0, 0, 0, 0, 31+28+2-1, +5 }}, { &D31MAR2001, /* AD2001-03-31 00:00:00.000 Z */ { DRT_TM_VS, +2001, 3,31, 0, 0, 0, 0, 31+28+31-1, +6 }}, { &D01APR2001, /* AD2001-04-01 00:00:00.000 Z */ { DRT_TM_VS, +2001, 4, 1, 0, 0, 0, 0, 31+28+31+1-1, +0 }}, { &D31DEC2001, /* AD2001-12-31 00:00:00.000 Z */ { DRT_TM_VS, +2001,12,31, 0, 0, 0, 0, 365-1, +1 }}, { &D01JAN2002, /* AD2002-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2002, 1, 1, 0, 0, 0, 0, 1-1, +2 }}, { &D01JAN2002_B, /* AD2002-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2002, 1, 1, 0, 0, 0, 0, 1-1, +2 }}, { &D01JAN2003, /* AD2003-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2003, 1, 1, 0, 0, 0, 0, 1-1, +3 }}, { &D01JAN2004, /* AD2004-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2004, 1, 1, 0, 0, 0, 0, 1-1, +4 }}, { &D01JAN2005, /* AD2005-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2005, 1, 1, 0, 0, 0, 0, 1-1, +6 }}, /*[60]*/ { &D01JAN2099, /* AD2099-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2099, 1, 1, 0, 0, 0, 0, 1-1, +4 }}, { &D01JAN2100, /* AD2100-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2100, 1, 1, 0, 0, 0, 0, 1-1, +5 }}, { &D01JAN2200, /* AD2200-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2200, 1, 1, 0, 0, 0, 0, 1-1, +3 }}, { &D01JAN2300, /* AD2300-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2300, 1, 1, 0, 0, 0, 0, 1-1, +1 }}, { &D01JAN2400, /* AD2400-01-01 00:00:00.000 Z */ { DRT_TM_VS, +2400, 1, 1, 0, 0, 0, 0, 1-1, +6 }}, { &D01JAN1601, /* AD1601-01-01 00:00:00.000 Z */ { DRT_TM_VS, +1601, 1, 1, 0, 0, 0, 0, 1-1, +1 }}, { &D01JAN1701, /* AD1701-01-01 00:00:00.000 Z */ { DRT_TM_VS, +1701, 1, 1, 0, 0, 0, 0, 1-1, +6 }}, { &D01JAN1801, /* AD1801-01-01 00:00:00.000 Z */ { DRT_TM_VS, +1801, 1, 1, 0, 0, 0, 0, 1-1, +4 }}, { &D01JAN1901, /* AD1901-01-01 00:00:00.000 Z */ { DRT_TM_VS, +1901, 1, 1, 0, 0, 0, 0, 1-1, +2 }}, { &D01JAN1970, /* AD1970-01-01 00:00:00.000 Z */ { DRT_TM_VS, +1970, 1, 1, 0, 0, 0, 0, 1-1, +4 }}, /*[70]*/ { &D01JAN1980, /* AD1980-01-01 00:00:00.000 Z */ { DRT_TM_VS, +1980, 1, 1, 0, 0, 0, 0, 1-1, +2 }}, { &D01JAN1980_A, /* AD1980-01-01 00:00:00.000 Z */ { DRT_TM_VS, +1980, 1, 1, 0, 0, 0, 0, 1-1, +2 }}, { &EMPTY }, { &EMPTY }, { &EMPTY }, { &EMPTY }, { &EMPTY }, { &EMPTY }, { &EMPTY }, { &EMPTY }, /*[80]*/ { &SKIP }, { &DMIN, /* BC9999-01-01 00:00:00.000 Z */ { DRT_TM_VS, -9998, 1, 1, 0, 0, 0, 0, 1-1, +2 }}, { &DMAX, /* AD9999-12-31 23:59:59.999 Z */ { DRT_TM_VS, +9999,12,31,23,59,59, 999999900, 365-1, +5 }}, { &DUNKNOWN, /* Unknown, BC10000-12-31 23:59:59.999 Z */ { DRT_TM_VS, TIME_YR_UNKNOWN, 0, 0, 0, 0, 0, 0, -1, -1 }}, { &DNEVER, /* Never, AD10000-01-01 00:00:00.000 Z */ { DRT_TM_VS, TIME_YR_NEVER, 0, 0, 0, 0, 0, 0, -1, -1 }}, { &EMPTY }, { &EMPTY }, { &EMPTY }, { &EMPTY }, /*[80]*/ { &SKIP }, { null } }; /*----------------------------------------------------------------------------- * Local fake functions *----------------------------------------------------------------------------*/ bool drt_tm_normalize(struct drt_tm *th) { /* Fake function */ if (th == null) return (false); /* Normalize only the .m_yday member of 'th' */ th->tm_yday = drt_time_mdays[th->tm_mon] + th->tm_mday - 1; if (drt_time_is_leap(th->tm_year) and th->tm_mon >= 2) th->tm_yday++; return (true); } /*----------------------------------------------------------------------------- */ static void printtm(const struct drt_tm *t, bool faked) { if (t->tm_year == TIME_YR_UNKNOWN) printf("UNKNOWN|"); else if (t->tm_year == TIME_YR_NEVER) printf("NEVER |"); else { if (t->tm_year < -9998 or t->tm_year > +9999) printf("INVALID|"); if (t->tm_year <= 0) printf("BC%04d", -t->tm_year+1); else printf("AD%04d", t->tm_year); } printf("%s%02d-%02d", drt_time_is_leap(t->tm_year) ? "L" : "-", t->tm_mon + (faked ? 0 : 1), t->tm_mday ); if (t->tm_yday == -1) printf("(??\?)"); else printf("(%03d)", t->tm_yday); printf(" %02d:%02d:%02d.%09ld", t->tm_hour, t->tm_min, t->tm_sec, t->tm_nsec ); if (t->tm_wday == -1) printf(" ?:"); else printf(" %d:", t->tm_wday); if (t->tm_wday == -1) printf("??\?"); else printf("%s", wday[t->tm_wday]); } static bool sametm(const struct drt_tm *act, const struct drt_tm *exp) { if (act->tm_year != exp->tm_year) return (false); if (act->tm_year == TIME_YR_UNKNOWN) return (true); if (act->tm_year == TIME_YR_NEVER) return (true); if (act->tm_mon != exp->tm_mon-1) return (false); if (act->tm_mday != exp->tm_mday) return (false); if (act->tm_hour != exp->tm_hour) return (false); if (act->tm_min != exp->tm_min) return (false); if (act->tm_sec != exp->tm_sec) return (false); if (act->tm_nsec != exp->tm_nsec) return (false); if (exp->tm_yday != -1 and act->tm_yday != exp->tm_yday) return (false); if (exp->tm_wday != -1 and act->tm_wday != exp->tm_wday) return (false); return (true); } /*----------------------------------------------------------------------------- * main() * Test driver. *----------------------------------------------------------------------------*/ int main(void) { drt_time_t t; drt_time_t t1; int i; /* Print some of the manifest constants */ printf("TIME_ZDYEAR ....... %04d-01-01\n", TIME_ZDYEAR); printf("TIME_ZDOFFSET ..... %+d\n", TIME_ZDOFFSET); printf("TIME_Z400YR ....... %04d+-%03d\n", TIME_Z400YR, TIME_ZDOFFSET); printf("TIME_ZWKDAY ....... %d %s\n", TIME_ZWKDAY, wday[TIME_ZWKDAY]); printf("TIME_UNKNOWN ...... %+" LLONG_FMT "d\n", TIME_UNKNOWN); printf("TIME_NEVER ........ %+" LLONG_FMT "d\n", TIME_NEVER); printf("TIME_MIN .......... %+" LLONG_FMT "d\n", TIME_MIN); printf("TIME_MAX .......... %+" LLONG_FMT "d\n", TIME_MAX); printf("TIME_YR_UNKNOWN ... %+ld\n", (long)TIME_YR_UNKNOWN); printf("TIME_YR_NEVER ..... %+ld\n", (long)TIME_YR_NEVER); printf("SECS_PER_DAY ...... %ld\n", SECS_PER_DAY); printf("SECS_PER_YEAR ..... %ld\n", SECS_PER_YEAR); printf("DAYS_PER_400YR .... %ld\n", DAYS_PER_400YR); printf("NSEC_PER_TICKS .... %ld\n", NSEC_PER_TICKS); printf("TICKS_PER_SEC ..... %ld\n", TICKS_PER_SEC); printf("TICKS_PER_HOUR .... %" LLONG_FMT "d\n", TICKS_PER_HOUR); printf("TICKS_PER_DAY ..... %" LLONG_FMT "d\n", TICKS_PER_DAY); printf("TICKS_PER_YEAR .... %" LLONG_FMT "d\n", TICKS_PER_YEAR); printf("TICKS_PER_400YR ... %" LLONG_FMT "d\n", TICKS_PER_400YR); printf("\n"); fflush(stdout); /* Print some well-known exact dates */ for (i = 0; dates[i].t != null; i++) { drt_time_t t; struct drt_tm tm; if (dates[i].t == &SKIP) { printf("======================================================\n"); continue; } else if (dates[i].t == &EMPTY) continue; t = *dates[i].t; printf("[%2d]: <", i); printtm(&dates[i].ts, true); printf(">\n"); printf(": %+20" LLONG_FMT "d\n", t); drt_time_gmtime(&tm, t); printf(" =<"); printtm(&tm, false); printf(">"); if (sametm(&tm, &dates[i].ts) or not drt_time_is_valid(t)) printf(" ok\n"); else printf(" FAIL ***\n"); fflush(stdout); /* Convert broken-down time back into a numeric ticks time */ { drt_time_t nt; printf(" Converted back into ticks:"); if (drt_time_make(&nt, &tm)) { printf(" %+020" LLONG_FMT "d ", nt); if (nt == t or not drt_time_is_valid(t)) printf("ok\n"); else printf("FAIL ***\n"); } else printf(" ERROR\n"); } printf("\n"); fflush(stdout); } /* Print a series of increasing drt_time_t values */ { drt_time_t inc; inc = 100000000; inc *= 10000; t = 1; t1 = 0; for (i = 0; i < 30; i++) { struct drt_tm tm; drt_time_gmtime(&tm, t); printf("%20" LLONG_FMT "d: ", t); printtm(&tm, false); printf("\n"); fflush(stdout); if (tm.tm_year < 2100 and t1 != 0) t1 = t; t += t*2/3 + inc; } printf("\n"); } /* Print a series of drt_time_t values with increasing increments */ { drt_time_t inc; t = t1; inc = 1; for (i = 0; i < 44; i++) { struct drt_tm tm; drt_time_gmtime(&tm, t); printf("%2u. ", i); printf("%20" LLONG_FMT "d: ", t); printtm(&tm, false); printf("\n"); fflush(stdout); if (i == 3) inc *= 10; if (i == 7) inc = TICKS_PER_SEC; if (i == 11) inc *= 60; if (i == 15) inc *= 60; if (i == 19) inc *= 24; if (i == 23) inc *= 30; if (i == 27) inc = (drt_time_t)TICKS_PER_SEC * SECS_PER_YEAR; if (i == 31) inc *= 100; if (i == 35) inc *= 10; t += inc; } printf("\n"); } /* Done */ return (0); } #endif /* TEST == 1 */ /* End ktime.c */