time64.c revision 066eb0b06d51e7ccbaea92e11de9c747314b03d6
1/* 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 31#if defined(__LP64__) 32#error This cruft should be LP32 only! 33#endif 34 35/* 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 255#if !defined(NDEBUG) 256static 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); 276 277 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} 287#endif 288 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 373static void copy_tm_to_TM(const struct tm *src, struct TM *dest) { 374 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 405static void copy_TM_to_tm(const struct TM *src, struct tm *dest) { 406 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 744static int valid_tm_wday( const struct TM* date ) { 745 if( 0 <= date->tm_wday && date->tm_wday <= 6 ) 746 return 1; 747 else 748 return 0; 749} 750 751static int valid_tm_mon( const struct TM* date ) { 752 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} 800