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