Path: blob/main/crypto/krb5/src/util/support/gmt_mktime.c
34889 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* This code placed in the public domain by Mark W. Eichin */23#include "autoconf.h"4#include <stdio.h>56#ifdef HAVE_SYS_TYPES_H7#include <sys/types.h>8#endif9#ifdef HAVE_SYS_TIME_H10#include <sys/time.h>11#endif12#include <time.h>1314#include "k5-gmt_mktime.h"1516#if !HAVE_TIMEGM || TEST_LEAP17static time_t gmt_mktime(struct tm *t);18#endif1920/*21* Use the nonstandard timegm() (if available) to convert broken-down22* UTC times into time_t values. Use our custom gmt_mktime() if23* timegm() is not available.24*25* We use gmtime() (or gmtime_r()) when encoding ASN.1 GeneralizedTime26* values. On systems where a "right" (leap-second-aware) time zone27* is configured, gmtime() adjusts for the presence of accumulated28* leap seconds in the input time_t value. POSIX requires that time_t29* values omit leap seconds; systems configured to include leap30* seconds in their time_t values are non-conforming and will have31* difficulties exchanging timestamp information with other systems.32*33* We use krb5int_gmt_mktime() for decoding ASN.1 GeneralizedTime34* values. If timegm() is not available, krb5int_gmt_mktime() won't35* be the inverse of gmtime() on a system that counts leap seconds. A36* system configured with a "right" time zone probably has timegm()37* available; without it, an application would have no reliable way of38* converting broken-down UTC times into time_t values.39*/40time_t41krb5int_gmt_mktime(struct tm *t)42{43#if HAVE_TIMEGM44return timegm(t);45#else46return gmt_mktime(t);47#endif48}4950#if !HAVE_TIMEGM || TEST_LEAP5152/* take a struct tm, return seconds from GMT epoch */53/* like mktime, this ignores tm_wday and tm_yday. */54/* unlike mktime, this does not set them... it only passes a return value. */5556static const int days_in_month[12] = {570, /* jan 31 */5831, /* feb 28 */5959, /* mar 31 */6090, /* apr 30 */61120, /* may 31 */62151, /* jun 30 */63181, /* jul 31 */64212, /* aug 31 */65243, /* sep 30 */66273, /* oct 31 */67304, /* nov 30 */68334 /* dec 31 */69};7071#define hasleapday(year) (year%400?(year%100?(year%4?0:1):0):1)7273static time_t74gmt_mktime(struct tm *t)75{76uint32_t accum;7778#define assert_time(cnd) if(!(cnd)) return (time_t) -17980/*81* For 32-bit unsigned time values starting on 1/1/1970, the range is:82* time 0x00000000 -> Thu Jan 1 00:00:00 197083* time 0xffffffff -> Sun Feb 7 06:28:15 210684*85* We can't encode all dates in 2106, and we're not doing overflow checking86* for such cases.87*/88assert_time(t->tm_year>=70);89assert_time(t->tm_year<=206);9091assert_time(t->tm_mon>=0);92assert_time(t->tm_mon<=11);93assert_time(t->tm_mday>=1);94assert_time(t->tm_mday<=31);95assert_time(t->tm_hour>=0);96assert_time(t->tm_hour<=23);97assert_time(t->tm_min>=0);98assert_time(t->tm_min<=59);99assert_time(t->tm_sec>=0);100assert_time(t->tm_sec<=62);101102#undef assert_time103104105accum = t->tm_year - 70;106accum *= 365; /* 365 days/normal year */107108/* add in leap day for all previous years */109if (t->tm_year >= 70)110accum += (t->tm_year - 69) / 4;111else112accum -= (72 - t->tm_year) / 4;113/* add in leap day for this year */114if(t->tm_mon >= 2) /* march or later */115if(hasleapday((t->tm_year + 1900))) accum += 1;116117accum += days_in_month[t->tm_mon];118accum += t->tm_mday-1; /* days of month are the only 1-based field */119accum *= 24; /* 24 hour/day */120accum += t->tm_hour;121accum *= 60; /* 60 minute/hour */122accum += t->tm_min;123accum *= 60; /* 60 seconds/minute */124accum += t->tm_sec;125126return accum;127}128#endif /* !HAVE_TIMEGM || TEST_LEAP */129130#ifdef TEST_LEAP131int132main (int argc, char *argv[])133{134int yr;135time_t t;136struct tm tm = {137.tm_mon = 0, .tm_mday = 1,138.tm_hour = 0, .tm_min = 0, .tm_sec = 0,139};140for (yr = 60; yr <= 104; yr++)141{142printf ("1/1/%d%c -> ", 1900 + yr, hasleapday((1900+yr)) ? '*' : ' ');143tm.tm_year = yr;144t = gmt_mktime (&tm);145if (t == (time_t) -1)146printf ("-1\n");147else148{149long u;150if (t % (24 * 60 * 60))151printf ("(not integral multiple of days) ");152u = t / (24 * 60 * 60);153printf ("%3ld*365%+ld\t0x%08lx\n",154(long) (u / 365), (long) (u % 365),155(long) t);156}157}158t = 0x80000000, printf ("time 0x%lx -> %s", t, ctime (&t));159t = 0x7fffffff, printf ("time 0x%lx -> %s", t, ctime (&t));160return 0;161}162#endif163164165