Home | History | Annotate | Download | only in bionic
      1 /*
      2 
      3 Copyright (c) 2007-2008  Michael G Schwern
      4 
      5 This software originally derived from Paul Sheer's pivotal_gmtime_r.c.
      6 
      7 The MIT License:
      8 
      9 Permission is hereby granted, free of charge, to any person obtaining a copy
     10 of this software and associated documentation files (the "Software"), to deal
     11 in the Software without restriction, including without limitation the rights
     12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     13 copies of the Software, and to permit persons to whom the Software is
     14 furnished to do so, subject to the following conditions:
     15 
     16 The above copyright notice and this permission notice shall be included in
     17 all copies or substantial portions of the Software.
     18 
     19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     25 THE 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 
     37 Programmers who have available to them 64-bit time values as a 'long
     38 long' type can use localtime64_r() and gmtime64_r() which correctly
     39 converts the time even on 32-bit systems. Whether you have 64-bit time
     40 values will depend on the operating system.
     41 
     42 localtime64_r() is a 64-bit equivalent of localtime_r().
     43 
     44 gmtime64_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 
     74 typedef int64_t  Int64;
     75 typedef time64_t Time64_T;
     76 typedef 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! */
     82 static struct TM   Static_Return_Date;
     83 static char        Static_Return_String[35];
     84 
     85 static 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 
     90 static 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 
     95 static char const wday_name[7][3] = {
     96     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
     97 };
     98 
     99 static char const mon_name[12][3] = {
    100     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    101     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    102 };
    103 
    104 static const int length_of_year[2] = { 365, 366 };
    105 
    106 /* Some numbers relating to the gregorian cycle */
    107 static const Year     years_in_gregorian_cycle   = 400;
    108 #define               days_in_gregorian_cycle      ((365 * 400) + 100 - 4 + 1)
    109 static 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. */
    119 static 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 */
    130 static 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 
    183 static 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 */
    196 Time64_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)
    245 static 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 */
    282 static 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 */
    317 static 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 
    362 static 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 
    394 static 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 */
    427 struct 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 */
    445 struct 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 
    461 static 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 
    486 Time64_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 */
    511 Time64_T timelocal64(const struct TM *date) {
    512     return mktime64(date);
    513 }
    514 
    515 
    516 struct 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 
    643 struct 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 
    733 static 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 
    740 static 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 
    748 char *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 
    765 char *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 */
    774 struct TM *localtime64(const Time64_T *time) {
    775     return localtime64_r(time, &Static_Return_Date);
    776 }
    777 
    778 struct TM *gmtime64(const Time64_T *time) {
    779     return gmtime64_r(time, &Static_Return_Date);
    780 }
    781 
    782 char *asctime64( const struct TM* date ) {
    783     return asctime64_r( date, Static_Return_String );
    784 }
    785 
    786 char *ctime64( const Time64_T* time ) {
    787     return asctime64(localtime64(time));
    788 }
    789