3 Copyright (c) 2007-2008 Michael G Schwern
5 This software originally derived from Paul Sheer's pivotal_gmtime_r.c.
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:
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
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
31 * This thing all things devours:
32 * Birds, beasts, trees, flowers;
33 * Gnaws iron, bites steel;
34 * Grinds hard stones to meal;
35 * Slays king, ruins town,
36 * And beats high mountain down."
38 * Poor Bilbo sat in the dark thinking of all the horrible names of all the
39 * giants and ogres he had ever heard told of in tales, but not one of them had
40 * done all these things. He had a feeling that the answer was quite different
41 * and that he ought to know it, but he could not think of it. He began to get
42 * frightened, and that is bad for thinking. Gollum began to get out of his
43 * boat. He flapped into the water and paddled to the bank; Bilbo could see his
44 * eyes coming towards him. His tongue seemed to stick in his mouth; he wanted
45 * to shout out: "Give me more time! Give me time!" But all that came out with
46 * a sudden squeal was:
50 * Bilbo was saved by pure luck. For that of course was the answer.
52 * [p.84 of _The Hobbit_: "Riddles in the Dark"]
58 Programmers who have available to them 64-bit time values as a 'long
59 long' type can use localtime64_r() and gmtime64_r() which correctly
60 converts the time even on 32-bit systems. Whether you have 64-bit time
61 values will depend on the operating system.
63 Perl_localtime64_r() is a 64-bit equivalent of localtime_r().
65 Perl_gmtime64_r() is a 64-bit equivalent of gmtime_r().
70 #define PERL_IN_TIME64_C
74 static const char days_in_month[2][12] = {
75 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
76 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
79 static const short julian_days_by_month[2][12] = {
80 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
81 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
84 static const short length_of_year[2] = { 365, 366 };
86 /* Number of days in a 400 year Gregorian cycle */
87 static const Year years_in_gregorian_cycle = 400;
88 static const int days_in_gregorian_cycle = (365 * 400) + 100 - 4 + 1;
90 /* 28 year calendar cycle between 2010 and 2037 */
91 #define SOLAR_CYCLE_LENGTH 28
92 static const short safe_years[SOLAR_CYCLE_LENGTH] = {
93 2016, 2017, 2018, 2019,
94 2020, 2021, 2022, 2023,
95 2024, 2025, 2026, 2027,
96 2028, 2029, 2030, 2031,
97 2032, 2033, 2034, 2035,
98 2036, 2037, 2010, 2011,
99 2012, 2013, 2014, 2015
102 /* Let's assume people are going to be looking for dates in the future.
103 Let's provide some cheats so you can skip ahead.
104 This has a 4x speed boost when near 2008.
106 /* Number of days since epoch on Jan 1st, 2008 GMT */
107 #define CHEAT_DAYS (1199145600 / 24 / 60 / 60)
108 #define CHEAT_YEARS 108
110 #define IS_LEAP(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0)
111 #undef WRAP /* some <termios.h> define this */
112 #define WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a))
114 #ifdef USE_SYSTEM_LOCALTIME
115 # define SHOULD_USE_SYSTEM_LOCALTIME(a) ( \
116 (a) <= SYSTEM_LOCALTIME_MAX && \
117 (a) >= SYSTEM_LOCALTIME_MIN \
120 # define SHOULD_USE_SYSTEM_LOCALTIME(a) (0)
123 #ifdef USE_SYSTEM_GMTIME
124 # define SHOULD_USE_SYSTEM_GMTIME(a) ( \
125 (a) <= SYSTEM_GMTIME_MAX && \
126 (a) >= SYSTEM_GMTIME_MIN \
129 # define SHOULD_USE_SYSTEM_GMTIME(a) (0)
132 /* Multi varadic macros are a C99 thing, alas */
134 # define TIME64_TRACE(format) (fprintf(stderr, format))
135 # define TIME64_TRACE1(format, var1) (fprintf(stderr, format, var1))
136 # define TIME64_TRACE2(format, var1, var2) (fprintf(stderr, format, var1, var2))
137 # define TIME64_TRACE3(format, var1, var2, var3) (fprintf(stderr, format, var1, var2, var3))
139 # define TIME64_TRACE(format) ((void)0)
140 # define TIME64_TRACE1(format, var1) ((void)0)
141 # define TIME64_TRACE2(format, var1, var2) ((void)0)
142 # define TIME64_TRACE3(format, var1, var2, var3) ((void)0)
145 /* Set up the mutexes for this file. There are no races possible on
146 * non-threaded perls, nor platforms that naturally don't have them.
147 * Otherwise, we need to have mutexes. If we have reentrant versions of the
148 * functions below, they automatically will be substituted for the
149 * non-reentrant ones. That solves the problem of the buffers being trashed by
150 * another thread, but not of the environment or locale changing during their
151 * execution. To do that, we only need a read lock (which prevents writing by
152 * others). However, if we don't have re-entrant functions, we can gain some
153 * measure of thread-safety by using an exclusive lock during their execution.
154 * That will protect against any other use of the functions that use the
155 * mutexes, which all of core should be using. */
156 #ifdef USE_REENTRANT_API /* This indicates a platform where we need reentrant
157 versions if have them */
158 # ifdef PERL_REENTR_USING_LOCALTIME_R
159 # define LOCALTIME_LOCK ENV_LOCALE_READ_LOCK
160 # define LOCALTIME_UNLOCK ENV_LOCALE_READ_UNLOCK
162 # define LOCALTIME_LOCK ENV_LOCALE_LOCK
163 # define LOCALTIME_UNLOCK ENV_LOCALE_UNLOCK
165 # ifdef PERL_REENTR_USING_GMTIME_R
166 # define GMTIME_LOCK ENV_LOCALE_READ_LOCK
167 # define GMTIME_UNLOCK ENV_LOCALE_READ_UNLOCK
169 # define GMTIME_LOCK ENV_LOCALE_LOCK
170 # define GMTIME_UNLOCK ENV_LOCALE_UNLOCK
172 #else /* Reentrant not needed, so races not possible */
173 # define LOCALTIME_LOCK NOOP
174 # define LOCALTIME_UNLOCK NOOP
175 # define GMTIME_LOCK NOOP
176 # define GMTIME_UNLOCK NOOP
179 static int S_is_exception_century(Year year)
181 const int is_exception = ((year % 100 == 0) && !(year % 400 == 0));
182 TIME64_TRACE1("# is_exception_century: %s\n", is_exception ? "yes" : "no");
184 return(is_exception);
188 static Time64_T S_timegm64(const struct TM *date) {
190 Time64_T seconds = 0;
192 if( date->tm_year > 70 ) {
194 while( year < date->tm_year ) {
195 days += length_of_year[IS_LEAP(year)];
199 else if ( date->tm_year < 70 ) {
202 days -= length_of_year[IS_LEAP(year)];
204 } while( year >= date->tm_year );
207 days += julian_days_by_month[IS_LEAP(date->tm_year)][date->tm_mon];
208 days += date->tm_mday - 1;
210 /* Avoid overflowing the days integer */
212 seconds = seconds * 60 * 60 * 24;
214 seconds += date->tm_hour * 60 * 60;
215 seconds += date->tm_min * 60;
216 seconds += date->tm_sec;
223 static int S_check_tm(const struct TM *tm)
225 /* Don't forget leap seconds */
226 assert(tm->tm_sec >= 0);
227 assert(tm->tm_sec <= 61);
229 assert(tm->tm_min >= 0);
230 assert(tm->tm_min <= 59);
232 assert(tm->tm_hour >= 0);
233 assert(tm->tm_hour <= 23);
235 assert(tm->tm_mday >= 1);
236 assert(tm->tm_mday <= days_in_month[IS_LEAP(tm->tm_year)][tm->tm_mon]);
238 assert(tm->tm_mon >= 0);
239 assert(tm->tm_mon <= 11);
241 assert(tm->tm_wday >= 0);
242 assert(tm->tm_wday <= 6);
244 assert(tm->tm_yday >= 0);
245 assert(tm->tm_yday <= length_of_year[IS_LEAP(tm->tm_year)]);
247 #ifdef HAS_TM_TM_GMTOFF
248 assert(tm->tm_gmtoff >= -24 * 60 * 60);
249 assert(tm->tm_gmtoff <= 24 * 60 * 60);
257 /* The exceptional centuries without leap years cause the cycle to
260 static Year S_cycle_offset(Year year)
262 const Year start_year = 2000;
263 Year year_diff = year - start_year;
266 if( year > start_year )
269 exceptions = year_diff / 100;
270 exceptions -= year_diff / 400;
272 TIME64_TRACE3("# year: %lld, exceptions: %lld, year_diff: %lld\n",
273 year, exceptions, year_diff);
275 return exceptions * 16;
278 /* For a given year after 2038, pick the latest possible matching
279 year in the 28 year calendar cycle.
282 1) Starts on the same day of the week.
283 2) Has the same leap year status.
285 This is so the calendars match up.
287 Also the previous year must match. When doing Jan 1st you might
288 wind up on Dec 31st the previous year when doing a -UTC time zone.
290 Finally, the next year must have the same start day of week. This
291 is for Dec 31st with a +UTC time zone.
292 It doesn't need the same leap year status since we only care about
295 static int S_safe_year(Year year)
298 Year year_cycle = year + S_cycle_offset(year);
300 /* Change non-leap xx00 years to an equivalent */
301 if( S_is_exception_century(year) )
304 /* Also xx01 years, since the previous year will be wrong */
305 if( S_is_exception_century(year - 1) )
308 year_cycle %= SOLAR_CYCLE_LENGTH;
310 year_cycle = SOLAR_CYCLE_LENGTH + year_cycle;
312 assert( year_cycle >= 0 );
313 assert( year_cycle < SOLAR_CYCLE_LENGTH );
314 safe_year = safe_years[year_cycle];
316 assert(safe_year <= 2037 && safe_year >= 2010);
318 TIME64_TRACE3("# year: %lld, year_cycle: %lld, safe_year: %d\n",
319 year, year_cycle, safe_year);
325 static void S_copy_little_tm_to_big_TM(const struct tm *src, struct TM *dest) {
329 dest->tm_sec = src->tm_sec;
330 dest->tm_min = src->tm_min;
331 dest->tm_hour = src->tm_hour;
332 dest->tm_mday = src->tm_mday;
333 dest->tm_mon = src->tm_mon;
334 dest->tm_year = (Year)src->tm_year;
335 dest->tm_wday = src->tm_wday;
336 dest->tm_yday = src->tm_yday;
337 dest->tm_isdst = src->tm_isdst;
339 # ifdef HAS_TM_TM_GMTOFF
340 dest->tm_gmtoff = src->tm_gmtoff;
343 # ifdef HAS_TM_TM_ZONE
344 dest->tm_zone = src->tm_zone;
348 /* They're the same type */
349 memcpy(dest, src, sizeof(*dest));
353 struct TM *Perl_gmtime64_r (const Time64_T *in_time, struct TM *p)
355 int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday;
359 Time64_T time = *in_time;
365 /* Use the system gmtime() if time_t is small enough */
366 if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) {
367 time_t safe_time = (time_t)*in_time;
373 /* reentr.h will automatically replace this with a call to gmtime_r()
374 * when appropriate */
375 result = gmtime(&safe_time);
377 assert(result != NULL);
379 #if defined(HAS_GMTIME_R) && defined(USE_REENTRANT_API)
381 PERL_UNUSED_VAR(safe_date);
383 /* Here, no gmtime_r() and is a threaded perl where the result can be
384 * overwritten by a call in another thread. Copy to a safe place,
385 * hopefully before another gmtime that isn't using the mutexes can
386 * jump in and trash this result. */
387 memcpy(&safe_date, result, sizeof(safe_date));
392 S_copy_little_tm_to_big_TM(result, p);
393 assert(S_check_tm(p));
398 #ifdef HAS_TM_TM_GMTOFF
403 #ifdef HAS_TM_TM_ZONE
407 v_tm_sec = (int)Perl_fmod(time, 60.0);
408 time = time >= 0 ? Perl_floor(time / 60.0) : Perl_ceil(time / 60.0);
409 v_tm_min = (int)Perl_fmod(time, 60.0);
410 time = time >= 0 ? Perl_floor(time / 60.0) : Perl_ceil(time / 60.0);
411 v_tm_hour = (int)Perl_fmod(time, 24.0);
412 time = time >= 0 ? Perl_floor(time / 24.0) : Perl_ceil(time / 24.0);
415 WRAP (v_tm_sec, v_tm_min, 60);
416 WRAP (v_tm_min, v_tm_hour, 60);
417 WRAP (v_tm_hour, v_tm_tday, 24);
419 v_tm_wday = (int)Perl_fmod((v_tm_tday + 4.0), 7.0);
424 if (m >= CHEAT_DAYS) {
430 /* Gregorian cycles, this is huge optimization for distant times */
431 const int cycles = (int)Perl_floor(m / (Time64_T) days_in_gregorian_cycle);
433 m -= (cycles * (Time64_T) days_in_gregorian_cycle);
434 year += (cycles * years_in_gregorian_cycle);
438 leap = IS_LEAP (year);
439 while (m >= (Time64_T) length_of_year[leap]) {
440 m -= (Time64_T) length_of_year[leap];
442 leap = IS_LEAP (year);
447 while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) {
448 m -= (Time64_T) days_in_month[leap][v_tm_mon];
456 /* Gregorian cycles */
457 cycles = (int)Perl_ceil((m / (Time64_T) days_in_gregorian_cycle) + 1);
459 m -= (cycles * (Time64_T) days_in_gregorian_cycle);
460 year += (cycles * years_in_gregorian_cycle);
464 leap = IS_LEAP (year);
465 while (m < (Time64_T) -length_of_year[leap]) {
466 m += (Time64_T) length_of_year[leap];
468 leap = IS_LEAP (year);
473 while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) {
474 m += (Time64_T) days_in_month[leap][v_tm_mon];
477 m += (Time64_T) days_in_month[leap][v_tm_mon];
481 if( p->tm_year != year ) {
488 /* At this point m is less than a year so casting to an int is safe */
489 p->tm_mday = (int) m + 1;
490 p->tm_yday = julian_days_by_month[leap][v_tm_mon] + (int)m;
491 p->tm_sec = v_tm_sec;
492 p->tm_min = v_tm_min;
493 p->tm_hour = v_tm_hour;
494 p->tm_mon = v_tm_mon;
495 p->tm_wday = v_tm_wday;
497 assert(S_check_tm(p));
503 struct TM *Perl_localtime64_r (const Time64_T *time, struct TM *local_tm)
507 const struct tm * result;
509 Year orig_year = 0; /* initialise to avoid spurious compiler warning */
511 const bool use_system = SHOULD_USE_SYSTEM_LOCALTIME(*time);
514 assert(local_tm != NULL);
516 /* Use the system localtime() if time_t is small enough */
518 safe_time = (time_t)*time;
520 TIME64_TRACE1("Using system localtime for %lld\n", *time);
523 if (Perl_gmtime64_r(time, &gm_tm) == NULL) {
524 TIME64_TRACE1("gmtime64_r returned null for %lld\n", *time);
528 orig_year = gm_tm.tm_year;
530 if (gm_tm.tm_year > (2037 - 1900) ||
531 gm_tm.tm_year < (1970 - 1900)
534 TIME64_TRACE1("Mapping tm_year %lld to safe_year\n",
535 (Year)gm_tm.tm_year);
536 gm_tm.tm_year = S_safe_year((Year)(gm_tm.tm_year + 1900)) - 1900;
539 safe_time = (time_t)S_timegm64(&gm_tm);
544 /* reentr.h will automatically replace this with a call to localtime_r()
545 * when appropriate */
546 result = localtime(&safe_time);
548 if(UNLIKELY(result == NULL)) {
550 TIME64_TRACE1("localtime(%d) returned NULL\n", (int)safe_time);
554 #if ! defined(USE_REENTRANT_API) || defined(PERL_REENTR_USING_LOCALTIME_R)
556 PERL_UNUSED_VAR(safe_date);
560 /* Here, would be using localtime_r() if it could, meaning there isn't one,
561 * and is a threaded perl where the result can be overwritten by a call in
562 * another thread. Copy to a safe place, hopefully before another
563 * localtime that isn't using the mutexes can jump in and trash this
565 memcpy(&safe_date, result, sizeof(safe_date));
572 S_copy_little_tm_to_big_TM(result, local_tm);
576 local_tm->tm_year = orig_year;
577 if( local_tm->tm_year != orig_year ) {
578 TIME64_TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n",
579 (Year)local_tm->tm_year, (Year)orig_year);
587 month_diff = local_tm->tm_mon - gm_tm.tm_mon;
589 /* When localtime is Dec 31st previous year and
590 gmtime is Jan 1st next year.
592 if( month_diff == 11 ) {
596 /* When localtime is Jan 1st, next year and
597 gmtime is Dec 31st, previous year.
599 if( month_diff == -11 ) {
603 /* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st
604 in a non-leap xx00. There is one point in the cycle
605 we can't account for which the safe xx00 year is a leap
606 year. So we need to correct for Dec 31st coming out as
607 the 366th day of the year.
609 if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 )
614 assert(S_check_tm(local_tm));