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