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