Annotation of embedaddon/ntp/libntp/prettydate.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * prettydate - convert a time stamp to something readable
        !             3:  */
        !             4: #include <stdio.h>
        !             5: 
        !             6: #include "ntp_fp.h"
        !             7: #include "ntp_unixtime.h"      /* includes <sys/time.h> */
        !             8: #include "lib_strbuf.h"
        !             9: #include "ntp_stdlib.h"
        !            10: #include "ntp_assert.h"
        !            11: 
        !            12: static char *common_prettydate(l_fp *, int);
        !            13: 
        !            14: const char *months[] = {
        !            15:   "Jan", "Feb", "Mar", "Apr", "May", "Jun",
        !            16:   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
        !            17: };
        !            18: 
        !            19: static const char *days[] = {
        !            20:   "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
        !            21: };
        !            22: 
        !            23: /* Helper function to handle possible wraparound of the ntp epoch.
        !            24: 
        !            25:    Works by periodic extension of the ntp time stamp in the NTP epoch.  If the
        !            26:    'time_t' is 32 bit, use solar cycle warping to get the value in a suitable
        !            27:    range. Also uses solar cycle warping to work around really buggy
        !            28:    implementations of 'gmtime()' / 'localtime()' that cannot work with a
        !            29:    negative time value, that is, times before 1970-01-01. (MSVCRT...)
        !            30: 
        !            31:    Apart from that we're assuming that the localtime/gmtime library functions
        !            32:    have been updated so that they work...
        !            33: */
        !            34: 
        !            35: 
        !            36: /* solar cycle in secs, unsigned secs and years. And the cycle limits.
        !            37: **
        !            38: ** And an explanation. The julian calendar repeats ever 28 years, because it's
        !            39: ** the LCM of 7 and 4, the week and leap year cycles. This is called a 'solar
        !            40: ** cycle'. The gregorian calendar does the same as long as no centennial year
        !            41: ** (divisible by 100, but not 400) goes in the way. So between 1901 and 2099
        !            42: ** (inclusive) we can warp time stamps by 28 years to make them suitable for
        !            43: ** localtime() and gmtime() if we have trouble. Of course this will play
        !            44: ** hubbubb with the DST zone switches, so we should do it only if necessary;
        !            45: ** but as we NEED a proper conversion to dates via gmtime() we should try to
        !            46: ** cope with as many idiosyncrasies as possible.
        !            47: */
        !            48: #define SOLAR_CYCLE_SECS   0x34AADC80UL        /* 7*1461*86400*/
        !            49: #define SOLAR_CYCLE_YEARS  28
        !            50: #define MINFOLD -3
        !            51: #define MAXFOLD  3
        !            52: 
        !            53: struct tm *
        !            54: ntp2unix_tm(
        !            55:        u_long ntp, int local
        !            56:        )
        !            57: {
        !            58:        struct tm *tm;
        !            59:        int32      folds = 0;
        !            60:        time_t     t     = time(NULL);
        !            61:        u_int32    dwlo  = (int32)t; /* might expand for SIZEOF_TIME_T < 4 */
        !            62: #if ( SIZEOF_TIME_T > 4 )
        !            63:        int32      dwhi  = (int32)(t >> 16 >> 16);/* double shift: avoid warnings */
        !            64: #else
        !            65:        /*
        !            66:         * Get the correct sign extension in the high part. 
        !            67:         * (now >> 32) may not work correctly on every 32 bit 
        !            68:         * system, e.g. it yields garbage under Win32/VC6.
        !            69:         */
        !            70:     int32              dwhi = (int32)(t >> 31);
        !            71: #endif
        !            72:        
        !            73:        /* Shift NTP to UN*X epoch, then unfold around currrent time. It's
        !            74:         * important to use a 32 bit max signed value -- LONG_MAX is 64 bit on
        !            75:         * a 64-bit system, and it will give wrong results.
        !            76:         */
        !            77:        M_ADD(dwhi, dwlo, 0, ((1UL << 31)-1)); /* 32-bit max signed */
        !            78:        if ((ntp -= JAN_1970) > dwlo)
        !            79:                --dwhi;
        !            80:        dwlo = ntp;
        !            81:  
        !            82: #   if SIZEOF_TIME_T < 4
        !            83: #      error sizeof(time_t) < 4 -- this will not work!
        !            84: #   elif SIZEOF_TIME_T == 4
        !            85: 
        !            86:        /*
        !            87:        ** If the result will not fit into a 'time_t' we have to warp solar
        !            88:        ** cycles. That's implemented by looped addition / subtraction with
        !            89:        ** M_ADD and M_SUB to avoid implicit 64 bit operations, especially
        !            90:        ** division. As he number of warps is rather limited there's no big
        !            91:        ** performance loss here.
        !            92:        **
        !            93:        ** note: unless the high word doesn't match the sign-extended low word,
        !            94:        ** the combination will not fit into time_t. That's what we use for
        !            95:        ** loop control here...
        !            96:        */
        !            97:        while (dwhi != ((int32)dwlo >> 31)) {
        !            98:                if (dwhi < 0 && --folds >= MINFOLD)
        !            99:                        M_ADD(dwhi, dwlo, 0, SOLAR_CYCLE_SECS);
        !           100:                else if (dwhi >= 0 && ++folds <= MAXFOLD)
        !           101:                        M_SUB(dwhi, dwlo, 0, SOLAR_CYCLE_SECS);
        !           102:                else
        !           103:                        return NULL;
        !           104:        }
        !           105: 
        !           106: #   else
        !           107: 
        !           108:        /* everything fine -- no reduction needed for the next thousand years */
        !           109: 
        !           110: #   endif
        !           111: 
        !           112:        /* combine hi/lo to make time stamp */
        !           113:        t = ((time_t)dwhi << 16 << 16) | dwlo;  /* double shift: avoid warnings */
        !           114: 
        !           115: #   ifdef _MSC_VER     /* make this an autoconf option? */
        !           116: 
        !           117:        /*
        !           118:        ** The MSDN says that the (Microsoft) Windoze versions of 'gmtime()'
        !           119:        ** and 'localtime()' will bark on time stamps < 0. Better to fix it
        !           120:        ** immediately.
        !           121:        */
        !           122:        while (t < 0) { 
        !           123:                if (--folds < MINFOLD)
        !           124:                        return NULL;
        !           125:                t += SOLAR_CYCLE_SECS;
        !           126:        }
        !           127:                        
        !           128: #   endif /* Microsoft specific */
        !           129: 
        !           130:        /* 't' should be a suitable value by now. Just go ahead. */
        !           131:        while ( (tm = (*(local ? localtime : gmtime))(&t)) == 0)
        !           132:                /* seems there are some other pathological implementations of
        !           133:                ** 'gmtime()' and 'localtime()' somewhere out there. No matter
        !           134:                ** if we have 32-bit or 64-bit 'time_t', try to fix this by
        !           135:                ** solar cycle warping again...
        !           136:                */
        !           137:                if (t < 0) {
        !           138:                        if (--folds < MINFOLD)
        !           139:                                return NULL;
        !           140:                        t += SOLAR_CYCLE_SECS;
        !           141:                } else {
        !           142:                        if ((++folds > MAXFOLD) || ((t -= SOLAR_CYCLE_SECS) < 0))
        !           143:                                return NULL; /* That's truely pathological! */
        !           144:                }
        !           145:        /* 'tm' surely not NULL here... */
        !           146:        NTP_INSIST(tm != NULL);
        !           147:        if (folds != 0) {
        !           148:                tm->tm_year += folds * SOLAR_CYCLE_YEARS;
        !           149:                if (tm->tm_year <= 0 || tm->tm_year >= 200)
        !           150:                        return NULL;    /* left warp range... can't help here! */
        !           151:        }
        !           152:        return tm;
        !           153: }
        !           154: 
        !           155: 
        !           156: static char *
        !           157: common_prettydate(
        !           158:        l_fp *ts,
        !           159:        int local
        !           160:        )
        !           161: {
        !           162:        char *bp;
        !           163:        struct tm *tm;
        !           164:        u_long sec;
        !           165:        u_long msec;
        !           166: 
        !           167:        LIB_GETBUF(bp);
        !           168:        
        !           169:        sec = ts->l_ui;
        !           170:        msec = ts->l_uf / 4294967;      /* fract / (2 ** 32 / 1000) */
        !           171: 
        !           172:        tm = ntp2unix_tm(sec, local);
        !           173:        if (!tm)
        !           174:                snprintf(bp, LIB_BUFLENGTH,
        !           175:                         "%08lx.%08lx  --- --- -- ---- --:--:--",
        !           176:                         (u_long)ts->l_ui, (u_long)ts->l_uf);
        !           177:        else
        !           178:                snprintf(bp, LIB_BUFLENGTH,
        !           179:                         "%08lx.%08lx  %s, %s %2d %4d %2d:%02d:%02d.%03lu",
        !           180:                         (u_long)ts->l_ui, (u_long)ts->l_uf,
        !           181:                         days[tm->tm_wday], months[tm->tm_mon],
        !           182:                         tm->tm_mday, 1900 + tm->tm_year, tm->tm_hour,
        !           183:                         tm->tm_min, tm->tm_sec, msec);
        !           184:        
        !           185:        return bp;
        !           186: }
        !           187: 
        !           188: 
        !           189: char *
        !           190: prettydate(
        !           191:        l_fp *ts
        !           192:        )
        !           193: {
        !           194:        return common_prettydate(ts, 1);
        !           195: }
        !           196: 
        !           197: 
        !           198: char *
        !           199: gmprettydate(
        !           200:        l_fp *ts
        !           201:        )
        !           202: {
        !           203:        return common_prettydate(ts, 0);
        !           204: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>