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
mktime64(const struct TM * input_date)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 */
timelocal64(const struct TM * date)511 Time64_T timelocal64(const struct TM *date) {
512 return mktime64(date);
513 }
514
515
gmtime64_r(const Time64_T * in_time,struct TM * p)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
localtime64_r(const Time64_T * time,struct TM * local_tm)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
valid_tm_wday(const struct TM * date)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
valid_tm_mon(const struct TM * date)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
asctime64_r(const struct TM * date,char * result)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
755 /* Docs state this function does not support years beyond 9999. */
756 if (1900 + date->tm_year > 9999) {
757 return NULL;
758 }
759
760 /*
761 * The IBM docs for this function state that the result buffer can be
762 * assumed to be at least 26 bytes wide. The docs also state that this is
763 * only valid for years <= 9999, so we know this format string will not
764 * print more than that many characters.
765 *
766 * http://www-01.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.bpxbd00/asctimer.htm
767 */
768 snprintf(result, 26, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
769 wday_name[date->tm_wday],
770 mon_name[date->tm_mon],
771 date->tm_mday, date->tm_hour,
772 date->tm_min, date->tm_sec,
773 1900 + date->tm_year);
774
775 return result;
776 }
777
778
ctime64_r(const Time64_T * time,char * result)779 char *ctime64_r( const Time64_T* time, char* result ) {
780 struct TM date;
781
782 localtime64_r( time, &date );
783 return asctime64_r( &date, result );
784 }
785
786
787 /* Non-thread safe versions of the above */
localtime64(const Time64_T * time)788 struct TM *localtime64(const Time64_T *time) {
789 return localtime64_r(time, &Static_Return_Date);
790 }
791
gmtime64(const Time64_T * time)792 struct TM *gmtime64(const Time64_T *time) {
793 return gmtime64_r(time, &Static_Return_Date);
794 }
795
asctime64(const struct TM * date)796 char *asctime64( const struct TM* date ) {
797 return asctime64_r( date, Static_Return_String );
798 }
799
ctime64(const Time64_T * time)800 char *ctime64( const Time64_T* time ) {
801 return asctime64(localtime64(time));
802 }
803