blob: 73f24ab165ddb439afc2588a48e39b71fe3aa6f5 [file] [log] [blame]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001/*
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
Elliott Hughes8d77bce2014-04-22 13:55:58 -070031#if defined(__LP64__)
32#error This cruft should be LP32 only!
33#endif
34
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080035/*
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
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800140/* 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
183static 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*/
196Time64_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
Elliott Hughes066eb0b2014-07-01 10:48:23 -0700244#if !defined(NDEBUG)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800245static 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);
Elliott Hughes8d77bce2014-04-22 13:55:58 -0700265
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800266 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}
Elliott Hughes066eb0b2014-07-01 10:48:23 -0700276#endif
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800277
278
279/* The exceptional centuries without leap years cause the cycle to
280 shift by 16
281*/
282static 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*/
317static 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
Jim Huang8b2707a2010-10-15 02:15:54 +0800362static void copy_tm_to_TM(const struct tm *src, struct TM *dest) {
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800363 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
Jim Huang8b2707a2010-10-15 02:15:54 +0800394static void copy_TM_to_tm(const struct TM *src, struct tm *dest) {
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800395 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 */
427struct 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 */
445struct 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
461static 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
Almaz Mingaleev5411aff2022-02-11 12:27:12 +0000486/* 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 */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800491Time64_T mktime64(const struct TM *input_date) {
492 struct tm safe_date;
493 struct TM date;
494 Time64_T time;
495 Year year = input_date->tm_year + 1900;
496
497 if( MIN_SAFE_YEAR <= year && year <= MAX_SAFE_YEAR ) {
498 copy_TM_to_tm(input_date, &safe_date);
499 return (Time64_T)mktime(&safe_date);
500 }
501
502 /* Have to make the year safe in date else it won't fit in safe_date */
503 date = *input_date;
504 date.tm_year = safe_year(year) - 1900;
505 copy_TM_to_tm(&date, &safe_date);
506
507 time = (Time64_T)mktime(&safe_date);
508
509 time += seconds_between_years(year, (Year)(safe_date.tm_year + 1900));
510
511 return time;
512}
513
514
515/* Because I think mktime() is a crappy name */
516Time64_T timelocal64(const struct TM *date) {
517 return mktime64(date);
518}
519
520
521struct TM *gmtime64_r (const Time64_T *in_time, struct TM *p)
522{
523 int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday;
524 Time64_T v_tm_tday;
525 int leap;
526 Time64_T m;
527 Time64_T time = *in_time;
528 Year year = 70;
529 int cycles = 0;
530
531 assert(p != NULL);
532
533 /* Use the system gmtime() if time_t is small enough */
534 if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) {
535 time_t safe_time = *in_time;
536 struct tm safe_date;
537 GMTIME_R(&safe_time, &safe_date);
538
539 copy_tm_to_TM(&safe_date, p);
540 assert(check_tm(p));
541
542 return p;
543 }
544
545#ifdef HAS_TM_TM_GMTOFF
546 p->tm_gmtoff = 0;
547#endif
548 p->tm_isdst = 0;
549
550#ifdef HAS_TM_TM_ZONE
551 p->tm_zone = "UTC";
552#endif
553
554 v_tm_sec = (int)(time % 60);
555 time /= 60;
556 v_tm_min = (int)(time % 60);
557 time /= 60;
558 v_tm_hour = (int)(time % 24);
559 time /= 24;
560 v_tm_tday = time;
561
562 WRAP (v_tm_sec, v_tm_min, 60);
563 WRAP (v_tm_min, v_tm_hour, 60);
564 WRAP (v_tm_hour, v_tm_tday, 24);
565
566 v_tm_wday = (int)((v_tm_tday + 4) % 7);
567 if (v_tm_wday < 0)
568 v_tm_wday += 7;
569 m = v_tm_tday;
570
571 if (m >= CHEAT_DAYS) {
572 year = CHEAT_YEARS;
573 m -= CHEAT_DAYS;
574 }
575
576 if (m >= 0) {
577 /* Gregorian cycles, this is huge optimization for distant times */
578 cycles = (int)(m / (Time64_T) days_in_gregorian_cycle);
579 if( cycles ) {
580 m -= (cycles * (Time64_T) days_in_gregorian_cycle);
581 year += (cycles * years_in_gregorian_cycle);
582 }
583
584 /* Years */
585 leap = IS_LEAP (year);
586 while (m >= (Time64_T) length_of_year[leap]) {
587 m -= (Time64_T) length_of_year[leap];
588 year++;
589 leap = IS_LEAP (year);
590 }
591
592 /* Months */
593 v_tm_mon = 0;
594 while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) {
595 m -= (Time64_T) days_in_month[leap][v_tm_mon];
596 v_tm_mon++;
597 }
598 } else {
599 year--;
600
601 /* Gregorian cycles */
602 cycles = (int)((m / (Time64_T) days_in_gregorian_cycle) + 1);
603 if( cycles ) {
604 m -= (cycles * (Time64_T) days_in_gregorian_cycle);
605 year += (cycles * years_in_gregorian_cycle);
606 }
607
608 /* Years */
609 leap = IS_LEAP (year);
610 while (m < (Time64_T) -length_of_year[leap]) {
611 m += (Time64_T) length_of_year[leap];
612 year--;
613 leap = IS_LEAP (year);
614 }
615
616 /* Months */
617 v_tm_mon = 11;
618 while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) {
619 m += (Time64_T) days_in_month[leap][v_tm_mon];
620 v_tm_mon--;
621 }
622 m += (Time64_T) days_in_month[leap][v_tm_mon];
623 }
624
625 p->tm_year = year;
626 if( p->tm_year != year ) {
627#ifdef EOVERFLOW
628 errno = EOVERFLOW;
629#endif
630 return NULL;
631 }
632
633 /* At this point m is less than a year so casting to an int is safe */
634 p->tm_mday = (int) m + 1;
635 p->tm_yday = julian_days_by_month[leap][v_tm_mon] + (int)m;
636 p->tm_sec = v_tm_sec;
637 p->tm_min = v_tm_min;
638 p->tm_hour = v_tm_hour;
639 p->tm_mon = v_tm_mon;
640 p->tm_wday = v_tm_wday;
641
642 assert(check_tm(p));
643
644 return p;
645}
646
647
648struct TM *localtime64_r (const Time64_T *time, struct TM *local_tm)
649{
650 time_t safe_time;
651 struct tm safe_date;
652 struct TM gm_tm;
653 Year orig_year;
654 int month_diff;
655
656 assert(local_tm != NULL);
657
658 /* Use the system localtime() if time_t is small enough */
659 if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) {
660 safe_time = *time;
661
662 TRACE1("Using system localtime for %lld\n", *time);
663
664 LOCALTIME_R(&safe_time, &safe_date);
665
666 copy_tm_to_TM(&safe_date, local_tm);
667 assert(check_tm(local_tm));
668
669 return local_tm;
670 }
671
672 if( gmtime64_r(time, &gm_tm) == NULL ) {
673 TRACE1("gmtime64_r returned null for %lld\n", *time);
674 return NULL;
675 }
676
677 orig_year = gm_tm.tm_year;
678
679 if (gm_tm.tm_year > (2037 - 1900) ||
680 gm_tm.tm_year < (1970 - 1900)
681 )
682 {
683 TRACE1("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year);
684 gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year + 1900)) - 1900;
685 }
686
687 safe_time = timegm64(&gm_tm);
688 if( LOCALTIME_R(&safe_time, &safe_date) == NULL ) {
689 TRACE1("localtime_r(%d) returned NULL\n", (int)safe_time);
690 return NULL;
691 }
692
693 copy_tm_to_TM(&safe_date, local_tm);
694
695 local_tm->tm_year = orig_year;
696 if( local_tm->tm_year != orig_year ) {
697 TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n",
698 (Year)local_tm->tm_year, (Year)orig_year);
699
700#ifdef EOVERFLOW
701 errno = EOVERFLOW;
702#endif
703 return NULL;
704 }
705
706
707 month_diff = local_tm->tm_mon - gm_tm.tm_mon;
708
709 /* When localtime is Dec 31st previous year and
710 gmtime is Jan 1st next year.
711 */
712 if( month_diff == 11 ) {
713 local_tm->tm_year--;
714 }
715
716 /* When localtime is Jan 1st, next year and
717 gmtime is Dec 31st, previous year.
718 */
719 if( month_diff == -11 ) {
720 local_tm->tm_year++;
721 }
722
723 /* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st
724 in a non-leap xx00. There is one point in the cycle
725 we can't account for which the safe xx00 year is a leap
726 year. So we need to correct for Dec 31st comming out as
727 the 366th day of the year.
728 */
729 if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 )
730 local_tm->tm_yday--;
731
732 assert(check_tm(local_tm));
733
734 return local_tm;
735}
736
737
Jim Huang8b2707a2010-10-15 02:15:54 +0800738static int valid_tm_wday( const struct TM* date ) {
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800739 if( 0 <= date->tm_wday && date->tm_wday <= 6 )
740 return 1;
741 else
742 return 0;
743}
744
Jim Huang8b2707a2010-10-15 02:15:54 +0800745static int valid_tm_mon( const struct TM* date ) {
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800746 if( 0 <= date->tm_mon && date->tm_mon <= 11 )
747 return 1;
748 else
749 return 0;
750}
751
752
753char *asctime64_r( const struct TM* date, char *result ) {
754 /* I figure everything else can be displayed, even hour 25, but if
755 these are out of range we walk off the name arrays */
Dan Albertb0fd5562014-10-07 11:07:53 -0700756 if (!valid_tm_wday(date) || !valid_tm_mon(date)) {
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800757 return NULL;
Dan Albertb0fd5562014-10-07 11:07:53 -0700758 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800759
Dan Albertb0fd5562014-10-07 11:07:53 -0700760 /* 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",
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800774 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
784char *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 */
793struct TM *localtime64(const Time64_T *time) {
794 return localtime64_r(time, &Static_Return_Date);
795}
796
797struct TM *gmtime64(const Time64_T *time) {
798 return gmtime64_r(time, &Static_Return_Date);
799}
800
801char *asctime64( const struct TM* date ) {
802 return asctime64_r( date, Static_Return_String );
803}
804
805char *ctime64( const Time64_T* time ) {
806 return asctime64(localtime64(time));
807}