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 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 */ 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) 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 */ 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 */ 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 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 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 */ 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 */ 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 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 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 */ 511 Time64_T timelocal64(const struct TM *date) { 512 return mktime64(date); 513 } 514 515 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 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 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 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 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 sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", 755 wday_name[date->tm_wday], 756 mon_name[date->tm_mon], 757 date->tm_mday, date->tm_hour, 758 date->tm_min, date->tm_sec, 759 1900 + date->tm_year); 760 761 return result; 762 } 763 764 765 char *ctime64_r( const Time64_T* time, char* result ) { 766 struct TM date; 767 768 localtime64_r( time, &date ); 769 return asctime64_r( &date, result ); 770 } 771 772 773 /* Non-thread safe versions of the above */ 774 struct TM *localtime64(const Time64_T *time) { 775 return localtime64_r(time, &Static_Return_Date); 776 } 777 778 struct TM *gmtime64(const Time64_T *time) { 779 return gmtime64_r(time, &Static_Return_Date); 780 } 781 782 char *asctime64( const struct TM* date ) { 783 return asctime64_r( date, Static_Return_String ); 784 } 785 786 char *ctime64( const Time64_T* time ) { 787 return asctime64(localtime64(time)); 788 } 789