]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#9120 change reference year from 1970 to 0000
authorHoward Chu <hyc@openldap.org>
Thu, 28 Nov 2019 21:31:06 +0000 (21:31 +0000)
committerHoward Chu <hyc@openldap.org>
Thu, 28 Nov 2019 21:33:48 +0000 (21:33 +0000)
Now handles +/-8740 years centered around year 0000 (1BCE)

include/lutil.h
libraries/liblutil/utils.c

index 3273496c1d5ebf78a8ece0f6fdd37200bee68448..5fdc405b7d19f73081c53266a87216e18db279b6 100644 (file)
@@ -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;
 
index ab0fe4e5d216829806edbdbf9d40e3cb4c776d4d..24e2337509a4215f80f17fcc8d42ef2ca7143443 100644 (file)
@@ -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; 
-
-       /* count leap days in preceding years */ 
-       tt->tt_sec += ((tm->tm_year -69) >> 2); 
+       /* calculate days from 0000 */
+       year = tm->tm_year + 1900;
+       tmp = year * 365;
+
+
+       /* 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;