time64.c revision 920d831ba15a4d9707e70e834df1be12613413c3
1/*
2
3Copyright (c) 2007-2008  Michael G Schwern
4
5This software originally derived from Paul Sheer's pivotal_gmtime_r.c.
6
7The MIT License:
8
9Permission is hereby granted, free of charge, to any person obtaining a copy
10of this software and associated documentation files (the "Software"), to deal
11in the Software without restriction, including without limitation the rights
12to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13copies of the Software, and to permit persons to whom the Software is
14furnished to do so, subject to the following conditions:
15
16The above copyright notice and this permission notice shall be included in
17all copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25THE SOFTWARE.
26
27*/
28
29/* See http://code.google.com/p/y2038 for this code's origin */
30
31#if defined(__LP64__)
32#error This cruft should be LP32 only!
33#endif
34
35/*
36
37Programmers who have available to them 64-bit time values as a 'long
38long' type can use localtime64_r() and gmtime64_r() which correctly
39converts the time even on 32-bit systems. Whether you have 64-bit time
40values will depend on the operating system.
41
42localtime64_r() is a 64-bit equivalent of localtime_r().
43
44gmtime64_r() is a 64-bit equivalent of gmtime_r().
45
46*/
47
48#include <assert.h>
49#include <stdlib.h>
50#include <stdio.h>
51#include <string.h>
52#include <time.h>
53#include <errno.h>
54#include "time64.h"
55
56/* BIONIC_BEGIN */
57/* the following are here to avoid exposing time64_config.h and
58 * other types in our public time64.h header
59 */
60#include "time64_config.h"
61
62/* Not everyone has gm/localtime_r(), provide a replacement */
63#ifdef HAS_LOCALTIME_R
64# define LOCALTIME_R(clock, result) localtime_r(clock, result)
65#else
66# define LOCALTIME_R(clock, result) fake_localtime_r(clock, result)
67#endif
68#ifdef HAS_GMTIME_R
69# define GMTIME_R(clock, result) gmtime_r(clock, result)
70#else
71# define GMTIME_R(clock, result) fake_gmtime_r(clock, result)
72#endif
73
74typedef int64_t  Int64;
75typedef time64_t Time64_T;
76typedef int64_t  Year;
77#define  TM      tm
78/* BIONIC_END */
79
80/* Spec says except for stftime() and the _r() functions, these
81   all return static memory.  Stabbings! */
82static struct TM   Static_Return_Date;
83static char        Static_Return_String[35];
84
85static const int days_in_month[2][12] = {
86    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
87    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
88};
89
90static const int julian_days_by_month[2][12] = {
91    {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
92    {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
93};
94
95static char const wday_name[7][3] = {
96    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
97};
98
99static char const mon_name[12][3] = {
100    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
101    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
102};
103
104static const int length_of_year[2] = { 365, 366 };
105
106/* Some numbers relating to the gregorian cycle */
107static const Year     years_in_gregorian_cycle   = 400;
108#define               days_in_gregorian_cycle      ((365 * 400) + 100 - 4 + 1)
109static const Time64_T seconds_in_gregorian_cycle = days_in_gregorian_cycle * 60LL * 60LL * 24LL;
110
111/* Year range we can trust the time funcitons with */
112#define MAX_SAFE_YEAR 2037
113#define MIN_SAFE_YEAR 1971
114
115/* 28 year Julian calendar cycle */
116#define SOLAR_CYCLE_LENGTH 28
117
118/* Year cycle from MAX_SAFE_YEAR down. */
119static const int safe_years_high[SOLAR_CYCLE_LENGTH] = {
120    2016, 2017, 2018, 2019,
121    2020, 2021, 2022, 2023,
122    2024, 2025, 2026, 2027,
123    2028, 2029, 2030, 2031,
124    2032, 2033, 2034, 2035,
125    2036, 2037, 2010, 2011,
126    2012, 2013, 2014, 2015
127};
128
129/* Year cycle from MIN_SAFE_YEAR up */
130static const int safe_years_low[SOLAR_CYCLE_LENGTH] = {
131    1996, 1997, 1998, 1971,
132    1972, 1973, 1974, 1975,
133    1976, 1977, 1978, 1979,
134    1980, 1981, 1982, 1983,
135    1984, 1985, 1986, 1987,
136    1988, 1989, 1990, 1991,
137    1992, 1993, 1994, 1995,
138};
139
140/* Let's assume people are going to be looking for dates in the future.
141   Let's provide some cheats so you can skip ahead.
142   This has a 4x speed boost when near 2008.
143*/
144/* Number of days since epoch on Jan 1st, 2008 GMT */
145#define CHEAT_DAYS  (1199145600 / 24 / 60 / 60)
146#define CHEAT_YEARS 108
147
148#define IS_LEAP(n)      ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0)
149#define WRAP(a,b,m)     ((a) = ((a) <  0  ) ? ((b)--, (a) + (m)) : (a))
150
151#ifdef USE_SYSTEM_LOCALTIME
152#    define SHOULD_USE_SYSTEM_LOCALTIME(a)  (       \
153    (a) <= SYSTEM_LOCALTIME_MAX &&              \
154    (a) >= SYSTEM_LOCALTIME_MIN                 \
155)
156#else
157#    define SHOULD_USE_SYSTEM_LOCALTIME(a)      (0)
158#endif
159
160#ifdef USE_SYSTEM_GMTIME
161#    define SHOULD_USE_SYSTEM_GMTIME(a)     (       \
162    (a) <= SYSTEM_GMTIME_MAX    &&              \
163    (a) >= SYSTEM_GMTIME_MIN                    \
164)
165#else
166#    define SHOULD_USE_SYSTEM_GMTIME(a)         (0)
167#endif
168
169/* Multi varadic macros are a C99 thing, alas */
170#ifdef TIME_64_DEBUG
171#    define TRACE(format) (fprintf(stderr, format))
172#    define TRACE1(format, var1)    (fprintf(stderr, format, var1))
173#    define TRACE2(format, var1, var2)    (fprintf(stderr, format, var1, var2))
174#    define TRACE3(format, var1, var2, var3)    (fprintf(stderr, format, var1, var2, var3))
175#else
176#    define TRACE(format) ((void)0)
177#    define TRACE1(format, var1) ((void)0)
178#    define TRACE2(format, var1, var2) ((void)0)
179#    define TRACE3(format, var1, var2, var3) ((void)0)
180#endif
181
182
183static int is_exception_century(Year year)
184{
185    int is_exception = ((year % 100 == 0) && !(year % 400 == 0));
186    TRACE1("# is_exception_century: %s\n", is_exception ? "yes" : "no");
187
188    return(is_exception);
189}
190
191
192/* timegm() is not in the C or POSIX spec, but it is such a useful
193   extension I would be remiss in leaving it out.  Also I need it
194   for localtime64()
195*/
196Time64_T timegm64(const struct TM *date) {
197    Time64_T days    = 0;
198    Time64_T seconds = 0;
199    Year     year;
200    Year     orig_year = (Year)date->tm_year;
201    int      cycles  = 0;
202
203    if( orig_year > 100 ) {
204        cycles = (orig_year - 100) / 400;
205        orig_year -= cycles * 400;
206        days      += (Time64_T)cycles * days_in_gregorian_cycle;
207    }
208    else if( orig_year < -300 ) {
209        cycles = (orig_year - 100) / 400;
210        orig_year -= cycles * 400;
211        days      += (Time64_T)cycles * days_in_gregorian_cycle;
212    }
213    TRACE3("# timegm/ cycles: %d, days: %lld, orig_year: %lld\n", cycles, days, orig_year);
214
215    if( orig_year > 70 ) {
216        year = 70;
217        while( year < orig_year ) {
218            days += length_of_year[IS_LEAP(year)];
219            year++;
220        }
221    }
222    else if ( orig_year < 70 ) {
223        year = 69;
224        do {
225            days -= length_of_year[IS_LEAP(year)];
226            year--;
227        } while( year >= orig_year );
228    }
229
230
231    days += julian_days_by_month[IS_LEAP(orig_year)][date->tm_mon];
232    days += date->tm_mday - 1;
233
234    seconds = days * 60 * 60 * 24;
235
236    seconds += date->tm_hour * 60 * 60;
237    seconds += date->tm_min * 60;
238    seconds += date->tm_sec;
239
240    return(seconds);
241}
242
243
244#if !defined(NDEBUG)
245static int check_tm(struct TM *tm)
246{
247    /* Don't forget leap seconds */
248    assert(tm->tm_sec >= 0);
249    assert(tm->tm_sec <= 61);
250
251    assert(tm->tm_min >= 0);
252    assert(tm->tm_min <= 59);
253
254    assert(tm->tm_hour >= 0);
255    assert(tm->tm_hour <= 23);
256
257    assert(tm->tm_mday >= 1);
258    assert(tm->tm_mday <= days_in_month[IS_LEAP(tm->tm_year)][tm->tm_mon]);
259
260    assert(tm->tm_mon  >= 0);
261    assert(tm->tm_mon  <= 11);
262
263    assert(tm->tm_wday >= 0);
264    assert(tm->tm_wday <= 6);
265
266    assert(tm->tm_yday >= 0);
267    assert(tm->tm_yday <= length_of_year[IS_LEAP(tm->tm_year)]);
268
269#ifdef HAS_TM_TM_GMTOFF
270    assert(tm->tm_gmtoff >= -24 * 60 * 60);
271    assert(tm->tm_gmtoff <=  24 * 60 * 60);
272#endif
273
274    return 1;
275}
276#endif
277
278
279/* The exceptional centuries without leap years cause the cycle to
280   shift by 16
281*/
282static Year cycle_offset(Year year)
283{
284    const Year start_year = 2000;
285    Year year_diff  = year - start_year;
286    Year exceptions;
287
288    if( year > start_year )
289        year_diff--;
290
291    exceptions  = year_diff / 100;
292    exceptions -= year_diff / 400;
293
294    TRACE3("# year: %lld, exceptions: %lld, year_diff: %lld\n",
295          year, exceptions, year_diff);
296
297    return exceptions * 16;
298}
299
300/* For a given year after 2038, pick the latest possible matching
301   year in the 28 year calendar cycle.
302
303   A matching year...
304   1) Starts on the same day of the week.
305   2) Has the same leap year status.
306
307   This is so the calendars match up.
308
309   Also the previous year must match.  When doing Jan 1st you might
310   wind up on Dec 31st the previous year when doing a -UTC time zone.
311
312   Finally, the next year must have the same start day of week.  This
313   is for Dec 31st with a +UTC time zone.
314   It doesn't need the same leap year status since we only care about
315   January 1st.
316*/
317static int safe_year(const Year year)
318{
319    int safe_year = 0;
320    Year year_cycle;
321
322    if( year >= MIN_SAFE_YEAR && year <= MAX_SAFE_YEAR ) {
323        return (int)year;
324    }
325
326    year_cycle = year + cycle_offset(year);
327
328    /* safe_years_low is off from safe_years_high by 8 years */
329    if( year < MIN_SAFE_YEAR )
330        year_cycle -= 8;
331
332    /* Change non-leap xx00 years to an equivalent */
333    if( is_exception_century(year) )
334        year_cycle += 11;
335
336    /* Also xx01 years, since the previous year will be wrong */
337    if( is_exception_century(year - 1) )
338        year_cycle += 17;
339
340    year_cycle %= SOLAR_CYCLE_LENGTH;
341    if( year_cycle < 0 )
342        year_cycle = SOLAR_CYCLE_LENGTH + year_cycle;
343
344    assert( year_cycle >= 0 );
345    assert( year_cycle < SOLAR_CYCLE_LENGTH );
346    if( year < MIN_SAFE_YEAR )
347        safe_year = safe_years_low[year_cycle];
348    else if( year > MAX_SAFE_YEAR )
349        safe_year = safe_years_high[year_cycle];
350    else
351        assert(0);
352
353    TRACE3("# year: %lld, year_cycle: %lld, safe_year: %d\n",
354          year, year_cycle, safe_year);
355
356    assert(safe_year <= MAX_SAFE_YEAR && safe_year >= MIN_SAFE_YEAR);
357
358    return safe_year;
359}
360
361
362static void copy_tm_to_TM(const struct tm *src, struct TM *dest) {
363    if( src == NULL ) {
364        memset(dest, 0, sizeof(*dest));
365    }
366    else {
367#       ifdef USE_TM64
368            dest->tm_sec        = src->tm_sec;
369            dest->tm_min        = src->tm_min;
370            dest->tm_hour       = src->tm_hour;
371            dest->tm_mday       = src->tm_mday;
372            dest->tm_mon        = src->tm_mon;
373            dest->tm_year       = (Year)src->tm_year;
374            dest->tm_wday       = src->tm_wday;
375            dest->tm_yday       = src->tm_yday;
376            dest->tm_isdst      = src->tm_isdst;
377
378#           ifdef HAS_TM_TM_GMTOFF
379                dest->tm_gmtoff  = src->tm_gmtoff;
380#           endif
381
382#           ifdef HAS_TM_TM_ZONE
383                dest->tm_zone  = src->tm_zone;
384#           endif
385
386#       else
387            /* They're the same type */
388            memcpy(dest, src, sizeof(*dest));
389#       endif
390    }
391}
392
393
394static void copy_TM_to_tm(const struct TM *src, struct tm *dest) {
395    if( src == NULL ) {
396        memset(dest, 0, sizeof(*dest));
397    }
398    else {
399#       ifdef USE_TM64
400            dest->tm_sec        = src->tm_sec;
401            dest->tm_min        = src->tm_min;
402            dest->tm_hour       = src->tm_hour;
403            dest->tm_mday       = src->tm_mday;
404            dest->tm_mon        = src->tm_mon;
405            dest->tm_year       = (int)src->tm_year;
406            dest->tm_wday       = src->tm_wday;
407            dest->tm_yday       = src->tm_yday;
408            dest->tm_isdst      = src->tm_isdst;
409
410#           ifdef HAS_TM_TM_GMTOFF
411                dest->tm_gmtoff  = src->tm_gmtoff;
412#           endif
413
414#           ifdef HAS_TM_TM_ZONE
415                dest->tm_zone  = src->tm_zone;
416#           endif
417
418#       else
419            /* They're the same type */
420            memcpy(dest, src, sizeof(*dest));
421#       endif
422    }
423}
424
425
426/* Simulate localtime_r() to the best of our ability */
427struct tm * fake_localtime_r(const time_t *clock, struct tm *result) {
428    const struct tm *static_result = localtime(clock);
429
430    assert(result != NULL);
431
432    if( static_result == NULL ) {
433        memset(result, 0, sizeof(*result));
434        return NULL;
435    }
436    else {
437        memcpy(result, static_result, sizeof(*result));
438        return result;
439    }
440}
441
442
443
444/* Simulate gmtime_r() to the best of our ability */
445struct tm * fake_gmtime_r(const time_t *clock, struct tm *result) {
446    const struct tm *static_result = gmtime(clock);
447
448    assert(result != NULL);
449
450    if( static_result == NULL ) {
451        memset(result, 0, sizeof(*result));
452        return NULL;
453    }
454    else {
455        memcpy(result, static_result, sizeof(*result));
456        return result;
457    }
458}
459
460
461static Time64_T seconds_between_years(Year left_year, Year right_year) {
462    int increment = (left_year > right_year) ? 1 : -1;
463    Time64_T seconds = 0;
464    int cycles;
465
466    if( left_year > 2400 ) {
467        cycles = (left_year - 2400) / 400;
468        left_year -= cycles * 400;
469        seconds   += cycles * seconds_in_gregorian_cycle;
470    }
471    else if( left_year < 1600 ) {
472        cycles = (left_year - 1600) / 400;
473        left_year += cycles * 400;
474        seconds   += cycles * seconds_in_gregorian_cycle;
475    }
476
477    while( left_year != right_year ) {
478        seconds += length_of_year[IS_LEAP(right_year - 1900)] * 60 * 60 * 24;
479        right_year += increment;
480    }
481
482    return seconds * increment;
483}
484
485
486Time64_T mktime64(const struct TM *input_date) {
487    struct tm safe_date;
488    struct TM date;
489    Time64_T  time;
490    Year      year = input_date->tm_year + 1900;
491
492    if( MIN_SAFE_YEAR <= year && year <= MAX_SAFE_YEAR ) {
493        copy_TM_to_tm(input_date, &safe_date);
494        return (Time64_T)mktime(&safe_date);
495    }
496
497    /* Have to make the year safe in date else it won't fit in safe_date */
498    date = *input_date;
499    date.tm_year = safe_year(year) - 1900;
500    copy_TM_to_tm(&date, &safe_date);
501
502    time = (Time64_T)mktime(&safe_date);
503
504    time += seconds_between_years(year, (Year)(safe_date.tm_year + 1900));
505
506    return time;
507}
508
509
510/* Because I think mktime() is a crappy name */
511Time64_T timelocal64(const struct TM *date) {
512    return mktime64(date);
513}
514
515
516struct TM *gmtime64_r (const Time64_T *in_time, struct TM *p)
517{
518    int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday;
519    Time64_T v_tm_tday;
520    int leap;
521    Time64_T m;
522    Time64_T time = *in_time;
523    Year year = 70;
524    int cycles = 0;
525
526    assert(p != NULL);
527
528    /* Use the system gmtime() if time_t is small enough */
529    if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) {
530        time_t safe_time = *in_time;
531        struct tm safe_date;
532        GMTIME_R(&safe_time, &safe_date);
533
534        copy_tm_to_TM(&safe_date, p);
535        assert(check_tm(p));
536
537        return p;
538    }
539
540#ifdef HAS_TM_TM_GMTOFF
541    p->tm_gmtoff = 0;
542#endif
543    p->tm_isdst  = 0;
544
545#ifdef HAS_TM_TM_ZONE
546    p->tm_zone   = "UTC";
547#endif
548
549    v_tm_sec =  (int)(time % 60);
550    time /= 60;
551    v_tm_min =  (int)(time % 60);
552    time /= 60;
553    v_tm_hour = (int)(time % 24);
554    time /= 24;
555    v_tm_tday = time;
556
557    WRAP (v_tm_sec, v_tm_min, 60);
558    WRAP (v_tm_min, v_tm_hour, 60);
559    WRAP (v_tm_hour, v_tm_tday, 24);
560
561    v_tm_wday = (int)((v_tm_tday + 4) % 7);
562    if (v_tm_wday < 0)
563        v_tm_wday += 7;
564    m = v_tm_tday;
565
566    if (m >= CHEAT_DAYS) {
567        year = CHEAT_YEARS;
568        m -= CHEAT_DAYS;
569    }
570
571    if (m >= 0) {
572        /* Gregorian cycles, this is huge optimization for distant times */
573        cycles = (int)(m / (Time64_T) days_in_gregorian_cycle);
574        if( cycles ) {
575            m -= (cycles * (Time64_T) days_in_gregorian_cycle);
576            year += (cycles * years_in_gregorian_cycle);
577        }
578
579        /* Years */
580        leap = IS_LEAP (year);
581        while (m >= (Time64_T) length_of_year[leap]) {
582            m -= (Time64_T) length_of_year[leap];
583            year++;
584            leap = IS_LEAP (year);
585        }
586
587        /* Months */
588        v_tm_mon = 0;
589        while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) {
590            m -= (Time64_T) days_in_month[leap][v_tm_mon];
591            v_tm_mon++;
592        }
593    } else {
594        year--;
595
596        /* Gregorian cycles */
597        cycles = (int)((m / (Time64_T) days_in_gregorian_cycle) + 1);
598        if( cycles ) {
599            m -= (cycles * (Time64_T) days_in_gregorian_cycle);
600            year += (cycles * years_in_gregorian_cycle);
601        }
602
603        /* Years */
604        leap = IS_LEAP (year);
605        while (m < (Time64_T) -length_of_year[leap]) {
606            m += (Time64_T) length_of_year[leap];
607            year--;
608            leap = IS_LEAP (year);
609        }
610
611        /* Months */
612        v_tm_mon = 11;
613        while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) {
614            m += (Time64_T) days_in_month[leap][v_tm_mon];
615            v_tm_mon--;
616        }
617        m += (Time64_T) days_in_month[leap][v_tm_mon];
618    }
619
620    p->tm_year = year;
621    if( p->tm_year != year ) {
622#ifdef EOVERFLOW
623        errno = EOVERFLOW;
624#endif
625        return NULL;
626    }
627
628    /* At this point m is less than a year so casting to an int is safe */
629    p->tm_mday = (int) m + 1;
630    p->tm_yday = julian_days_by_month[leap][v_tm_mon] + (int)m;
631    p->tm_sec  = v_tm_sec;
632    p->tm_min  = v_tm_min;
633    p->tm_hour = v_tm_hour;
634    p->tm_mon  = v_tm_mon;
635    p->tm_wday = v_tm_wday;
636
637    assert(check_tm(p));
638
639    return p;
640}
641
642
643struct TM *localtime64_r (const Time64_T *time, struct TM *local_tm)
644{
645    time_t safe_time;
646    struct tm safe_date;
647    struct TM gm_tm;
648    Year orig_year;
649    int month_diff;
650
651    assert(local_tm != NULL);
652
653    /* Use the system localtime() if time_t is small enough */
654    if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) {
655        safe_time = *time;
656
657        TRACE1("Using system localtime for %lld\n", *time);
658
659        LOCALTIME_R(&safe_time, &safe_date);
660
661        copy_tm_to_TM(&safe_date, local_tm);
662        assert(check_tm(local_tm));
663
664        return local_tm;
665    }
666
667    if( gmtime64_r(time, &gm_tm) == NULL ) {
668        TRACE1("gmtime64_r returned null for %lld\n", *time);
669        return NULL;
670    }
671
672    orig_year = gm_tm.tm_year;
673
674    if (gm_tm.tm_year > (2037 - 1900) ||
675        gm_tm.tm_year < (1970 - 1900)
676       )
677    {
678        TRACE1("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year);
679        gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year + 1900)) - 1900;
680    }
681
682    safe_time = timegm64(&gm_tm);
683    if( LOCALTIME_R(&safe_time, &safe_date) == NULL ) {
684        TRACE1("localtime_r(%d) returned NULL\n", (int)safe_time);
685        return NULL;
686    }
687
688    copy_tm_to_TM(&safe_date, local_tm);
689
690    local_tm->tm_year = orig_year;
691    if( local_tm->tm_year != orig_year ) {
692        TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n",
693              (Year)local_tm->tm_year, (Year)orig_year);
694
695#ifdef EOVERFLOW
696        errno = EOVERFLOW;
697#endif
698        return NULL;
699    }
700
701
702    month_diff = local_tm->tm_mon - gm_tm.tm_mon;
703
704    /*  When localtime is Dec 31st previous year and
705        gmtime is Jan 1st next year.
706    */
707    if( month_diff == 11 ) {
708        local_tm->tm_year--;
709    }
710
711    /*  When localtime is Jan 1st, next year and
712        gmtime is Dec 31st, previous year.
713    */
714    if( month_diff == -11 ) {
715        local_tm->tm_year++;
716    }
717
718    /* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st
719       in a non-leap xx00.  There is one point in the cycle
720       we can't account for which the safe xx00 year is a leap
721       year.  So we need to correct for Dec 31st comming out as
722       the 366th day of the year.
723    */
724    if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 )
725        local_tm->tm_yday--;
726
727    assert(check_tm(local_tm));
728
729    return local_tm;
730}
731
732
733static int valid_tm_wday( const struct TM* date ) {
734    if( 0 <= date->tm_wday && date->tm_wday <= 6 )
735        return 1;
736    else
737        return 0;
738}
739
740static int valid_tm_mon( const struct TM* date ) {
741    if( 0 <= date->tm_mon && date->tm_mon <= 11 )
742        return 1;
743    else
744        return 0;
745}
746
747
748char *asctime64_r( const struct TM* date, char *result ) {
749    /* I figure everything else can be displayed, even hour 25, but if
750       these are out of range we walk off the name arrays */
751    if( !valid_tm_wday(date) || !valid_tm_mon(date) )
752        return NULL;
753
754    sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
755        wday_name[date->tm_wday],
756        mon_name[date->tm_mon],
757        date->tm_mday, date->tm_hour,
758        date->tm_min, date->tm_sec,
759        1900 + date->tm_year);
760
761    return result;
762}
763
764
765char *ctime64_r( const Time64_T* time, char* result ) {
766    struct TM date;
767
768    localtime64_r( time, &date );
769    return asctime64_r( &date, result );
770}
771
772
773/* Non-thread safe versions of the above */
774struct TM *localtime64(const Time64_T *time) {
775    return localtime64_r(time, &Static_Return_Date);
776}
777
778struct TM *gmtime64(const Time64_T *time) {
779    return gmtime64_r(time, &Static_Return_Date);
780}
781
782char *asctime64( const struct TM* date ) {
783    return asctime64_r( date, Static_Return_String );
784}
785
786char *ctime64( const Time64_T* time ) {
787    return asctime64(localtime64(time));
788}
789