• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
is_exception_century(Year year)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 */
timegm64(const struct TM * date)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)
check_tm(struct TM * tm)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 */
cycle_offset(Year year)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 */
safe_year(const Year year)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 
copy_tm_to_TM(const struct tm * src,struct TM * dest)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 
copy_TM_to_tm(const struct TM * src,struct tm * dest)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 */
fake_localtime_r(const time_t * clock,struct tm * result)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 */
fake_gmtime_r(const time_t * clock,struct tm * result)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 
seconds_between_years(Year left_year,Year right_year)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 /* This implementation violates mktime specification, according to which
487    tm_yday, tm_wday, and tm_isdst fields should be updated. This function
488    leaves input_date unmodified. Given that there were no bug reports, fixing
489    it might cause more troubles than just leaving it as it is.
490  */
mktime64(const struct TM * input_date)491 Time64_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 */
timelocal64(const struct TM * date)516 Time64_T timelocal64(const struct TM *date) {
517     return mktime64(date);
518 }
519 
520 
gmtime64_r(const Time64_T * in_time,struct TM * p)521 struct 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 
localtime64_r(const Time64_T * time,struct TM * local_tm)648 struct 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 
valid_tm_wday(const struct TM * date)738 static 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 
valid_tm_mon(const struct TM * date)745 static 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 
asctime64_r(const struct TM * date,char * result)753 char *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 
760     /* Docs state this function does not support years beyond 9999. */
761     if (1900 + date->tm_year > 9999) {
762         return NULL;
763     }
764 
765     /*
766      * The IBM docs for this function state that the result buffer can be
767      * assumed to be at least 26 bytes wide. The docs also state that this is
768      * only valid for years <= 9999, so we know this format string will not
769      * print more than that many characters.
770      *
771      * http://www-01.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.bpxbd00/asctimer.htm
772      */
773     snprintf(result, 26, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
774         wday_name[date->tm_wday],
775         mon_name[date->tm_mon],
776         date->tm_mday, date->tm_hour,
777         date->tm_min, date->tm_sec,
778         1900 + date->tm_year);
779 
780     return result;
781 }
782 
783 
ctime64_r(const Time64_T * time,char * result)784 char *ctime64_r( const Time64_T* time, char* result ) {
785     struct TM date;
786 
787     localtime64_r( time, &date );
788     return asctime64_r( &date, result );
789 }
790 
791 
792 /* Non-thread safe versions of the above */
localtime64(const Time64_T * time)793 struct TM *localtime64(const Time64_T *time) {
794     return localtime64_r(time, &Static_Return_Date);
795 }
796 
gmtime64(const Time64_T * time)797 struct TM *gmtime64(const Time64_T *time) {
798     return gmtime64_r(time, &Static_Return_Date);
799 }
800 
asctime64(const struct TM * date)801 char *asctime64( const struct TM* date ) {
802     return asctime64_r( date, Static_Return_String );
803 }
804 
ctime64(const Time64_T * time)805 char *ctime64( const Time64_T* time ) {
806     return asctime64(localtime64(time));
807 }
808