From 97c145919d8c702003b9cd8bac0e01083d3ab9a1 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Thu, 28 Nov 2019 21:31:06 +0000 Subject: [PATCH] ITS#9120 change reference year from 1970 to 0000 Now handles +/-8740 years centered around year 0000 (1BCE) --- include/lutil.h | 5 +- libraries/liblutil/utils.c | 94 +++++++++++++++++++++++--------------- 2 files changed, 61 insertions(+), 38 deletions(-) diff --git a/include/lutil.h b/include/lutil.h index 3273496c1d..5fdc405b7d 100644 --- a/include/lutil.h +++ b/include/lutil.h @@ -169,8 +169,9 @@ typedef struct lutil_tm { } lutil_tm; typedef struct lutil_timet { - unsigned int tt_sec; /* seconds since 1900 */ - int tt_gsec; /* seconds since 1900, high 7 bits */ + unsigned int tt_sec; /* seconds since 0000 */ + int tt_gsec; /* seconds since 0000, high 7 bits, sign-flipped */ + /* sign flipped to sort properly as unsigned ints */ unsigned int tt_usec; /* microseconds */ } lutil_timet; diff --git a/libraries/liblutil/utils.c b/libraries/liblutil/utils.c index ab0fe4e5d2..24e2337509 100644 --- a/libraries/liblutil/utils.c +++ b/libraries/liblutil/utils.c @@ -151,75 +151,92 @@ size_t lutil_localtime( char *s, size_t smax, const struct tm *tm, long delta ) return ret + 4; } +/* Proleptic Gregorian Calendar, 1BCE = year 0 */ + int lutil_tm2time( struct lutil_tm *tm, struct lutil_timet *tt ) { static int moffset[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; - int sec; + int sec, year; + long tmp; tt->tt_usec = tm->tm_usec; - /* special case 0000/01/01+00:00:00 is returned as zero */ - if ( tm->tm_year == -1900 && tm->tm_mon == 0 && tm->tm_mday == 1 && - tm->tm_hour == 0 && tm->tm_min == 0 && tm->tm_sec == 0 ) { - tt->tt_sec = 0; - tt->tt_gsec = 0; - return 0; - } - /* tm->tm_year is years since 1900 */ - /* calculate days from years since 1970 (epoch) */ - tt->tt_sec = tm->tm_year - 70; - tt->tt_sec *= 365L; + /* calculate days from 0000 */ + year = tm->tm_year + 1900; + tmp = year * 365; - /* count leap days in preceding years */ - tt->tt_sec += ((tm->tm_year -69) >> 2); + + /* add in leap days */ + sec = (year - 1) / 4; + tmp += sec; + sec /= 25; + tmp -= sec; + sec /= 4; + tmp += sec; + /* Year 0000 was a leap year */ + if (year > 0) + tmp++; /* calculate days from months */ - tt->tt_sec += moffset[tm->tm_mon]; + tmp += moffset[tm->tm_mon]; - /* add in this year's leap day, if any */ - if (((tm->tm_year & 3) == 0) && (tm->tm_mon > 1)) { - tt->tt_sec ++; - } + /* add in this year's leap day, if any */ + if (tm->tm_mon > 1) { + sec = (year % 4) ? 0 : (year % 100) ? 1 : (year % 400) ? 0 : 1; + tmp += sec; + } - /* add in days in this month */ - tt->tt_sec += (tm->tm_mday - 1); + /* add in days in this month */ + tmp += (tm->tm_mday - 1); /* this function can handle a range of about 17408 years... */ /* 86400 seconds in a day, divided by 128 = 675 */ - tt->tt_sec *= 675; + tmp *= 675; /* move high 7 bits into tt_gsec */ - tt->tt_gsec = tt->tt_sec >> 25; - tt->tt_sec -= tt->tt_gsec << 25; + tt->tt_gsec = tmp >> 25; + tmp -= tt->tt_gsec << 25; - /* get hours */ - sec = tm->tm_hour; + /* toggle sign bit, keep positive greater than negative */ + tt->tt_gsec &= 0x7f; + tt->tt_gsec ^= 0x40; - /* convert to minutes */ - sec *= 60L; - sec += tm->tm_min; + /* get hours */ + sec = tm->tm_hour; - /* convert to seconds */ - sec *= 60L; - sec += tm->tm_sec; + /* convert to minutes */ + sec *= 60L; + sec += tm->tm_min; + + /* convert to seconds */ + sec *= 60L; + sec += tm->tm_sec; /* add remaining seconds */ - tt->tt_sec <<= 7; - tt->tt_sec += sec; + tmp <<= 7; + tmp += sec; + tt->tt_sec = tmp; /* return success */ - return 0; + return 0; } int lutil_parsetime( char *atm, struct lutil_tm *tm ) { while (atm && tm) { - char *ptr = atm; + char *ptr; unsigned i, fracs; + int neg = 0; + + if (*atm == '-') { + neg = 1; + atm++; + } + ptr = atm; /* Is the stamp reasonably long? */ for (i=0; isdigit((unsigned char) atm[i]); i++); @@ -234,6 +251,8 @@ int lutil_parsetime( char *atm, struct lutil_tm *tm ) tm->tm_year *= 10; tm->tm_year += *ptr++ - '0'; tm->tm_year *= 10; tm->tm_year += *ptr++ - '0'; tm->tm_year *= 10; tm->tm_year += *ptr++ - '0'; + if (neg) + tm->tm_year = -tm->tm_year; tm->tm_year -= 1900; /* month 01-12 to 0-11 */ tm->tm_mon = *ptr++ - '0'; @@ -273,7 +292,10 @@ int lutil_parsetime( char *atm, struct lutil_tm *tm ) for (i = fracs; i<6; i++) tm->tm_usec *= 10; } + } else { + tm->tm_usec = 0; } + tm->tm_usub = 0; /* Must be UTC */ if (*ptr != 'Z') break;