This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Promote v5.36 usage and feature bundles doc
[perl5.git] / time64.c
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
30 /*
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."
37  *
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:
47  *
48  * "Time! Time!"
49  *
50  * Bilbo was saved by pure luck. For that of course was the answer.
51  *
52  *     [p.84 of _The Hobbit_: "Riddles in the Dark"]
53  *
54 */
55
56 /*
57
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.
62
63 Perl_localtime64_r() is a 64-bit equivalent of localtime_r().
64
65 Perl_gmtime64_r() is a 64-bit equivalent of gmtime_r().
66
67 */
68
69 #include "EXTERN.h"
70 #define PERL_IN_TIME64_C
71 #include "perl.h"
72 #include "time64.h"
73
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},
77 };
78
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},
82 };
83
84 static const short length_of_year[2] = { 365, 366 };
85
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;
89
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
100 };
101
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.
105 */
106 /* Number of days since epoch on Jan 1st, 2008 GMT */
107 #define CHEAT_DAYS  (1199145600 / 24 / 60 / 60)
108 #define CHEAT_YEARS 108
109
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))
113
114 #ifdef USE_SYSTEM_LOCALTIME
115 #    define SHOULD_USE_SYSTEM_LOCALTIME(a)  (       \
116     (a) <= SYSTEM_LOCALTIME_MAX &&              \
117     (a) >= SYSTEM_LOCALTIME_MIN                 \
118 )
119 #else
120 #    define SHOULD_USE_SYSTEM_LOCALTIME(a)      (0)
121 #endif
122
123 #ifdef USE_SYSTEM_GMTIME
124 #    define SHOULD_USE_SYSTEM_GMTIME(a)     (       \
125     (a) <= SYSTEM_GMTIME_MAX    &&              \
126     (a) >= SYSTEM_GMTIME_MIN                    \
127 )
128 #else
129 #    define SHOULD_USE_SYSTEM_GMTIME(a)         (0)
130 #endif
131
132 /* Multi varadic macros are a C99 thing, alas */
133 #ifdef TIME_64_DEBUG
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))
138 #else
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)
143 #endif
144
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
161 #  else
162 #    define LOCALTIME_LOCK    ENV_LOCALE_LOCK
163 #    define LOCALTIME_UNLOCK  ENV_LOCALE_UNLOCK
164 #  endif
165 #  ifdef PERL_REENTR_USING_GMTIME_R
166 #    define GMTIME_LOCK    ENV_LOCALE_READ_LOCK
167 #    define GMTIME_UNLOCK  ENV_LOCALE_READ_UNLOCK
168 #  else
169 #    define GMTIME_LOCK    ENV_LOCALE_LOCK
170 #    define GMTIME_UNLOCK  ENV_LOCALE_UNLOCK
171 #  endif
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
177 #endif
178
179 static int S_is_exception_century(Year year)
180 {
181     const int is_exception = ((year % 100 == 0) && !(year % 400 == 0));
182     TIME64_TRACE1("# is_exception_century: %s\n", is_exception ? "yes" : "no");
183
184     return(is_exception);
185 }
186
187
188 static Time64_T S_timegm64(const struct TM *date) {
189     int      days    = 0;
190     Time64_T seconds = 0;
191
192     if( date->tm_year > 70 ) {
193         Year year = 70;
194         while( year < date->tm_year ) {
195             days += length_of_year[IS_LEAP(year)];
196             year++;
197         }
198     }
199     else if ( date->tm_year < 70 ) {
200         Year year = 69;
201         do {
202             days -= length_of_year[IS_LEAP(year)];
203             year--;
204         } while( year >= date->tm_year );
205     }
206
207     days += julian_days_by_month[IS_LEAP(date->tm_year)][date->tm_mon];
208     days += date->tm_mday - 1;
209
210     /* Avoid overflowing the days integer */
211     seconds = days;
212     seconds = seconds * 60 * 60 * 24;
213
214     seconds += date->tm_hour * 60 * 60;
215     seconds += date->tm_min * 60;
216     seconds += date->tm_sec;
217
218     return(seconds);
219 }
220
221
222 #ifdef DEBUGGING
223 static int S_check_tm(const struct TM *tm)
224 {
225     /* Don't forget leap seconds */
226     assert(tm->tm_sec >= 0);
227     assert(tm->tm_sec <= 61);
228
229     assert(tm->tm_min >= 0);
230     assert(tm->tm_min <= 59);
231
232     assert(tm->tm_hour >= 0);
233     assert(tm->tm_hour <= 23);
234
235     assert(tm->tm_mday >= 1);
236     assert(tm->tm_mday <= days_in_month[IS_LEAP(tm->tm_year)][tm->tm_mon]);
237
238     assert(tm->tm_mon  >= 0);
239     assert(tm->tm_mon  <= 11);
240
241     assert(tm->tm_wday >= 0);
242     assert(tm->tm_wday <= 6);
243
244     assert(tm->tm_yday >= 0);
245     assert(tm->tm_yday <= length_of_year[IS_LEAP(tm->tm_year)]);
246
247 #ifdef HAS_TM_TM_GMTOFF
248     assert(tm->tm_gmtoff >= -24 * 60 * 60);
249     assert(tm->tm_gmtoff <=  24 * 60 * 60);
250 #endif
251
252     return 1;
253 }
254 #endif
255
256
257 /* The exceptional centuries without leap years cause the cycle to
258    shift by 16
259 */
260 static Year S_cycle_offset(Year year)
261 {
262     const Year start_year = 2000;
263     Year year_diff  = year - start_year;
264     Year exceptions;
265
266     if( year > start_year )
267         year_diff--;
268
269     exceptions  = year_diff / 100;
270     exceptions -= year_diff / 400;
271
272     TIME64_TRACE3("# year: %lld, exceptions: %lld, year_diff: %lld\n",
273           year, exceptions, year_diff);
274
275     return exceptions * 16;
276 }
277
278 /* For a given year after 2038, pick the latest possible matching
279    year in the 28 year calendar cycle.
280
281    A matching year...
282    1) Starts on the same day of the week.
283    2) Has the same leap year status.
284
285    This is so the calendars match up.
286
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.
289
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
293    January 1st.
294 */
295 static int S_safe_year(Year year)
296 {
297     int safe_year;
298     Year year_cycle = year + S_cycle_offset(year);
299
300     /* Change non-leap xx00 years to an equivalent */
301     if( S_is_exception_century(year) )
302         year_cycle += 11;
303
304     /* Also xx01 years, since the previous year will be wrong */
305     if( S_is_exception_century(year - 1) )
306         year_cycle += 17;
307
308     year_cycle %= SOLAR_CYCLE_LENGTH;
309     if( year_cycle < 0 )
310         year_cycle = SOLAR_CYCLE_LENGTH + year_cycle;
311
312     assert( year_cycle >= 0 );
313     assert( year_cycle < SOLAR_CYCLE_LENGTH );
314     safe_year = safe_years[year_cycle];
315
316     assert(safe_year <= 2037 && safe_year >= 2010);
317
318     TIME64_TRACE3("# year: %lld, year_cycle: %lld, safe_year: %d\n",
319           year, year_cycle, safe_year);
320
321     return safe_year;
322 }
323
324
325 static void S_copy_little_tm_to_big_TM(const struct tm *src, struct TM *dest) {
326     assert(src);
327     assert(dest);
328 #ifdef USE_TM64
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;
338
339 #  ifdef HAS_TM_TM_GMTOFF
340     dest->tm_gmtoff     = src->tm_gmtoff;
341 #  endif
342
343 #  ifdef HAS_TM_TM_ZONE
344     dest->tm_zone       = src->tm_zone;
345 #  endif
346
347 #else
348     /* They're the same type */
349     memcpy(dest, src, sizeof(*dest));
350 #endif
351 }
352
353 struct TM *Perl_gmtime64_r (const Time64_T *in_time, struct TM *p)
354 {
355     int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday;
356     Time64_T v_tm_tday;
357     int leap;
358     Time64_T m;
359     Time64_T time = *in_time;
360     Year year = 70;
361     dTHX;
362
363     assert(p != NULL);
364
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;
368         struct tm safe_date;
369         struct tm * result;
370
371         GMTIME_LOCK;
372
373         /* reentr.h will automatically replace this with a call to gmtime_r()
374          * when appropriate */
375         result = gmtime(&safe_time);
376
377         assert(result != NULL);
378
379 #if defined(HAS_GMTIME_R) && defined(USE_REENTRANT_API)
380
381         PERL_UNUSED_VAR(safe_date);
382 #else
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));
388         result = &safe_date;
389 #endif
390         GMTIME_UNLOCK;
391
392         S_copy_little_tm_to_big_TM(result, p);
393         assert(S_check_tm(p));
394
395         return p;
396     }
397
398 #ifdef HAS_TM_TM_GMTOFF
399     p->tm_gmtoff = 0;
400 #endif
401     p->tm_isdst  = 0;
402
403 #ifdef HAS_TM_TM_ZONE
404     p->tm_zone   = "UTC";
405 #endif
406
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);
413     v_tm_tday = time;
414
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);
418
419     v_tm_wday = (int)Perl_fmod((v_tm_tday + 4.0), 7.0);
420     if (v_tm_wday < 0)
421         v_tm_wday += 7;
422     m = v_tm_tday;
423
424     if (m >= CHEAT_DAYS) {
425         year = CHEAT_YEARS;
426         m -= CHEAT_DAYS;
427     }
428
429     if (m >= 0) {
430         /* Gregorian cycles, this is huge optimization for distant times */
431         const int cycles = (int)Perl_floor(m / (Time64_T) days_in_gregorian_cycle);
432         if( cycles ) {
433             m -= (cycles * (Time64_T) days_in_gregorian_cycle);
434             year += (cycles * years_in_gregorian_cycle);
435         }
436
437         /* Years */
438         leap = IS_LEAP (year);
439         while (m >= (Time64_T) length_of_year[leap]) {
440             m -= (Time64_T) length_of_year[leap];
441             year++;
442             leap = IS_LEAP (year);
443         }
444
445         /* Months */
446         v_tm_mon = 0;
447         while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) {
448             m -= (Time64_T) days_in_month[leap][v_tm_mon];
449             v_tm_mon++;
450         }
451     } else {
452         int cycles;
453
454         year--;
455
456         /* Gregorian cycles */
457         cycles = (int)Perl_ceil((m / (Time64_T) days_in_gregorian_cycle) + 1);
458         if( cycles ) {
459             m -= (cycles * (Time64_T) days_in_gregorian_cycle);
460             year += (cycles * years_in_gregorian_cycle);
461         }
462
463         /* Years */
464         leap = IS_LEAP (year);
465         while (m < (Time64_T) -length_of_year[leap]) {
466             m += (Time64_T) length_of_year[leap];
467             year--;
468             leap = IS_LEAP (year);
469         }
470
471         /* Months */
472         v_tm_mon = 11;
473         while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) {
474             m += (Time64_T) days_in_month[leap][v_tm_mon];
475             v_tm_mon--;
476         }
477         m += (Time64_T) days_in_month[leap][v_tm_mon];
478     }
479
480     p->tm_year = year;
481     if( p->tm_year != year ) {
482 #ifdef EOVERFLOW
483         errno = EOVERFLOW;
484 #endif
485         return NULL;
486     }
487
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;
496
497     assert(S_check_tm(p));
498
499     return p;
500 }
501
502
503 struct TM *Perl_localtime64_r (const Time64_T *time, struct TM *local_tm)
504 {
505     time_t safe_time;
506     struct tm safe_date;
507     const struct tm * result;
508     struct TM gm_tm;
509     Year orig_year = 0; /* initialise to avoid spurious compiler warning */
510     int month_diff;
511     const bool use_system = SHOULD_USE_SYSTEM_LOCALTIME(*time);
512     dTHX;
513
514     assert(local_tm != NULL);
515
516     /* Use the system localtime() if time_t is small enough */
517     if (use_system) {
518         safe_time = (time_t)*time;
519
520         TIME64_TRACE1("Using system localtime for %lld\n", *time);
521     }
522     else {
523         if (Perl_gmtime64_r(time, &gm_tm) == NULL) {
524             TIME64_TRACE1("gmtime64_r returned null for %lld\n", *time);
525             return NULL;
526         }
527
528         orig_year = gm_tm.tm_year;
529
530         if (gm_tm.tm_year > (2037 - 1900) ||
531             gm_tm.tm_year < (1970 - 1900)
532            )
533         {
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;
537         }
538
539         safe_time = (time_t)S_timegm64(&gm_tm);
540     }
541
542     LOCALTIME_LOCK;
543
544     /* reentr.h will automatically replace this with a call to localtime_r()
545      * when appropriate */
546     result = localtime(&safe_time);
547
548     if(UNLIKELY(result == NULL)) {
549         LOCALTIME_UNLOCK;
550         TIME64_TRACE1("localtime(%d) returned NULL\n", (int)safe_time);
551         return NULL;
552     }
553
554 #if ! defined(USE_REENTRANT_API) || defined(PERL_REENTR_USING_LOCALTIME_R)
555
556     PERL_UNUSED_VAR(safe_date);
557
558 #else
559
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
564      * result. */
565     memcpy(&safe_date, result, sizeof(safe_date));
566     result = &safe_date;
567
568 #endif
569
570     LOCALTIME_UNLOCK;
571
572     S_copy_little_tm_to_big_TM(result, local_tm);
573
574     if (! use_system) {
575
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);
580
581 #ifdef EOVERFLOW
582             errno = EOVERFLOW;
583 #endif
584             return NULL;
585         }
586
587         month_diff = local_tm->tm_mon - gm_tm.tm_mon;
588
589         /*  When localtime is Dec 31st previous year and
590             gmtime is Jan 1st next year.
591         */
592         if( month_diff == 11 ) {
593             local_tm->tm_year--;
594         }
595
596         /*  When localtime is Jan 1st, next year and
597             gmtime is Dec 31st, previous year.
598         */
599         if( month_diff == -11 ) {
600             local_tm->tm_year++;
601         }
602
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.
608         */
609         if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 )
610             local_tm->tm_yday--;
611
612     }
613
614     assert(S_check_tm(local_tm));
615
616     return local_tm;
617 }