1b39c5158Smillert /*
2b39c5158Smillert
3b39c5158Smillert Copyright (c) 2007-2008 Michael G Schwern
4b39c5158Smillert
5b39c5158Smillert This software originally derived from Paul Sheer's pivotal_gmtime_r.c.
6b39c5158Smillert
7b39c5158Smillert The MIT License:
8b39c5158Smillert
9b39c5158Smillert Permission is hereby granted, free of charge, to any person obtaining a copy
10b39c5158Smillert of this software and associated documentation files (the "Software"), to deal
11b39c5158Smillert in the Software without restriction, including without limitation the rights
12b39c5158Smillert to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13b39c5158Smillert copies of the Software, and to permit persons to whom the Software is
14b39c5158Smillert furnished to do so, subject to the following conditions:
15b39c5158Smillert
16b39c5158Smillert The above copyright notice and this permission notice shall be included in
17b39c5158Smillert all copies or substantial portions of the Software.
18b39c5158Smillert
19b39c5158Smillert THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20b39c5158Smillert IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21b39c5158Smillert FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22b39c5158Smillert AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23b39c5158Smillert LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24b39c5158Smillert OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25b39c5158Smillert THE SOFTWARE.
26b39c5158Smillert
27b39c5158Smillert */
28b39c5158Smillert
29*e0680481Safresh1
30*e0680481Safresh1 /*
31*e0680481Safresh1 * This thing all things devours:
32*e0680481Safresh1 * Birds, beasts, trees, flowers;
33*e0680481Safresh1 * Gnaws iron, bites steel;
34*e0680481Safresh1 * Grinds hard stones to meal;
35*e0680481Safresh1 * Slays king, ruins town,
36*e0680481Safresh1 * And beats high mountain down."
37*e0680481Safresh1 *
38*e0680481Safresh1 * Poor Bilbo sat in the dark thinking of all the horrible names of all the
39*e0680481Safresh1 * giants and ogres he had ever heard told of in tales, but not one of them had
40*e0680481Safresh1 * done all these things. He had a feeling that the answer was quite different
41*e0680481Safresh1 * and that he ought to know it, but he could not think of it. He began to get
42*e0680481Safresh1 * frightened, and that is bad for thinking. Gollum began to get out of his
43*e0680481Safresh1 * boat. He flapped into the water and paddled to the bank; Bilbo could see his
44*e0680481Safresh1 * eyes coming towards him. His tongue seemed to stick in his mouth; he wanted
45*e0680481Safresh1 * to shout out: "Give me more time! Give me time!" But all that came out with
46*e0680481Safresh1 * a sudden squeal was:
47*e0680481Safresh1 *
48*e0680481Safresh1 * "Time! Time!"
49*e0680481Safresh1 *
50*e0680481Safresh1 * Bilbo was saved by pure luck. For that of course was the answer.
51*e0680481Safresh1 *
52*e0680481Safresh1 * [p.84 of _The Hobbit_: "Riddles in the Dark"]
53*e0680481Safresh1 *
54*e0680481Safresh1 */
55*e0680481Safresh1
56b39c5158Smillert /*
57b39c5158Smillert
58b39c5158Smillert Programmers who have available to them 64-bit time values as a 'long
59b39c5158Smillert long' type can use localtime64_r() and gmtime64_r() which correctly
60b39c5158Smillert converts the time even on 32-bit systems. Whether you have 64-bit time
61b39c5158Smillert values will depend on the operating system.
62b39c5158Smillert
63b8851fccSafresh1 Perl_localtime64_r() is a 64-bit equivalent of localtime_r().
64b39c5158Smillert
65b8851fccSafresh1 Perl_gmtime64_r() is a 64-bit equivalent of gmtime_r().
66b39c5158Smillert
67b39c5158Smillert */
68b39c5158Smillert
69b8851fccSafresh1 #include "EXTERN.h"
70b8851fccSafresh1 #define PERL_IN_TIME64_C
71b8851fccSafresh1 #include "perl.h"
72b39c5158Smillert #include "time64.h"
73b39c5158Smillert
746fb12b70Safresh1 static const char days_in_month[2][12] = {
75b39c5158Smillert {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
76b39c5158Smillert {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
77b39c5158Smillert };
78b39c5158Smillert
796fb12b70Safresh1 static const short julian_days_by_month[2][12] = {
80b39c5158Smillert {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
81b39c5158Smillert {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
82b39c5158Smillert };
83b39c5158Smillert
846fb12b70Safresh1 static const short length_of_year[2] = { 365, 366 };
85b39c5158Smillert
86b39c5158Smillert /* Number of days in a 400 year Gregorian cycle */
87b39c5158Smillert static const Year years_in_gregorian_cycle = 400;
88b39c5158Smillert static const int days_in_gregorian_cycle = (365 * 400) + 100 - 4 + 1;
89b39c5158Smillert
90b39c5158Smillert /* 28 year calendar cycle between 2010 and 2037 */
91b39c5158Smillert #define SOLAR_CYCLE_LENGTH 28
926fb12b70Safresh1 static const short safe_years[SOLAR_CYCLE_LENGTH] = {
93b39c5158Smillert 2016, 2017, 2018, 2019,
94b39c5158Smillert 2020, 2021, 2022, 2023,
95b39c5158Smillert 2024, 2025, 2026, 2027,
96b39c5158Smillert 2028, 2029, 2030, 2031,
97b39c5158Smillert 2032, 2033, 2034, 2035,
98b39c5158Smillert 2036, 2037, 2010, 2011,
99b39c5158Smillert 2012, 2013, 2014, 2015
100b39c5158Smillert };
101b39c5158Smillert
102b39c5158Smillert /* Let's assume people are going to be looking for dates in the future.
103b39c5158Smillert Let's provide some cheats so you can skip ahead.
104b39c5158Smillert This has a 4x speed boost when near 2008.
105b39c5158Smillert */
106b39c5158Smillert /* Number of days since epoch on Jan 1st, 2008 GMT */
107b39c5158Smillert #define CHEAT_DAYS (1199145600 / 24 / 60 / 60)
108b39c5158Smillert #define CHEAT_YEARS 108
109b39c5158Smillert
110b39c5158Smillert #define IS_LEAP(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0)
111b8851fccSafresh1 #undef WRAP /* some <termios.h> define this */
112b39c5158Smillert #define WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a))
113b39c5158Smillert
114b39c5158Smillert #ifdef USE_SYSTEM_LOCALTIME
115b39c5158Smillert # define SHOULD_USE_SYSTEM_LOCALTIME(a) ( \
116b39c5158Smillert (a) <= SYSTEM_LOCALTIME_MAX && \
117b39c5158Smillert (a) >= SYSTEM_LOCALTIME_MIN \
118b39c5158Smillert )
119b39c5158Smillert #else
120b39c5158Smillert # define SHOULD_USE_SYSTEM_LOCALTIME(a) (0)
121b39c5158Smillert #endif
122b39c5158Smillert
123b39c5158Smillert #ifdef USE_SYSTEM_GMTIME
124b39c5158Smillert # define SHOULD_USE_SYSTEM_GMTIME(a) ( \
125b39c5158Smillert (a) <= SYSTEM_GMTIME_MAX && \
126b39c5158Smillert (a) >= SYSTEM_GMTIME_MIN \
127b39c5158Smillert )
128b39c5158Smillert #else
129b39c5158Smillert # define SHOULD_USE_SYSTEM_GMTIME(a) (0)
130b39c5158Smillert #endif
131b39c5158Smillert
132b39c5158Smillert /* Multi varadic macros are a C99 thing, alas */
133b39c5158Smillert #ifdef TIME_64_DEBUG
134b39c5158Smillert # define TIME64_TRACE(format) (fprintf(stderr, format))
135b39c5158Smillert # define TIME64_TRACE1(format, var1) (fprintf(stderr, format, var1))
136b39c5158Smillert # define TIME64_TRACE2(format, var1, var2) (fprintf(stderr, format, var1, var2))
137b39c5158Smillert # define TIME64_TRACE3(format, var1, var2, var3) (fprintf(stderr, format, var1, var2, var3))
138b39c5158Smillert #else
139b39c5158Smillert # define TIME64_TRACE(format) ((void)0)
140b39c5158Smillert # define TIME64_TRACE1(format, var1) ((void)0)
141b39c5158Smillert # define TIME64_TRACE2(format, var1, var2) ((void)0)
142b39c5158Smillert # define TIME64_TRACE3(format, var1, var2, var3) ((void)0)
143b39c5158Smillert #endif
144b39c5158Smillert
S_is_exception_century(Year year)145b39c5158Smillert static int S_is_exception_century(Year year)
146b39c5158Smillert {
1479f11ffb7Safresh1 const int is_exception = ((year % 100 == 0) && !(year % 400 == 0));
148b39c5158Smillert TIME64_TRACE1("# is_exception_century: %s\n", is_exception ? "yes" : "no");
149b39c5158Smillert
150b39c5158Smillert return(is_exception);
151b39c5158Smillert }
152b39c5158Smillert
153b39c5158Smillert
S_timegm64(const struct TM * date)1549f11ffb7Safresh1 static Time64_T S_timegm64(const struct TM *date) {
155b39c5158Smillert int days = 0;
156b39c5158Smillert Time64_T seconds = 0;
157b39c5158Smillert
158b39c5158Smillert if( date->tm_year > 70 ) {
1599f11ffb7Safresh1 Year year = 70;
160b39c5158Smillert while( year < date->tm_year ) {
161b39c5158Smillert days += length_of_year[IS_LEAP(year)];
162b39c5158Smillert year++;
163b39c5158Smillert }
164b39c5158Smillert }
165b39c5158Smillert else if ( date->tm_year < 70 ) {
1669f11ffb7Safresh1 Year year = 69;
167b39c5158Smillert do {
168b39c5158Smillert days -= length_of_year[IS_LEAP(year)];
169b39c5158Smillert year--;
170b39c5158Smillert } while( year >= date->tm_year );
171b39c5158Smillert }
172b39c5158Smillert
173b39c5158Smillert days += julian_days_by_month[IS_LEAP(date->tm_year)][date->tm_mon];
174b39c5158Smillert days += date->tm_mday - 1;
175b39c5158Smillert
176b39c5158Smillert /* Avoid overflowing the days integer */
177b39c5158Smillert seconds = days;
178b39c5158Smillert seconds = seconds * 60 * 60 * 24;
179b39c5158Smillert
180b39c5158Smillert seconds += date->tm_hour * 60 * 60;
181b39c5158Smillert seconds += date->tm_min * 60;
182b39c5158Smillert seconds += date->tm_sec;
183b39c5158Smillert
184b39c5158Smillert return(seconds);
185b39c5158Smillert }
186b39c5158Smillert
187b39c5158Smillert
188b39c5158Smillert #ifdef DEBUGGING
S_check_tm(const struct TM * tm)1899f11ffb7Safresh1 static int S_check_tm(const struct TM *tm)
190b39c5158Smillert {
191b39c5158Smillert /* Don't forget leap seconds */
192b39c5158Smillert assert(tm->tm_sec >= 0);
193b39c5158Smillert assert(tm->tm_sec <= 61);
194b39c5158Smillert
195b39c5158Smillert assert(tm->tm_min >= 0);
196b39c5158Smillert assert(tm->tm_min <= 59);
197b39c5158Smillert
198b39c5158Smillert assert(tm->tm_hour >= 0);
199b39c5158Smillert assert(tm->tm_hour <= 23);
200b39c5158Smillert
201b39c5158Smillert assert(tm->tm_mday >= 1);
202b39c5158Smillert assert(tm->tm_mday <= days_in_month[IS_LEAP(tm->tm_year)][tm->tm_mon]);
203b39c5158Smillert
204b39c5158Smillert assert(tm->tm_mon >= 0);
205b39c5158Smillert assert(tm->tm_mon <= 11);
206b39c5158Smillert
207b39c5158Smillert assert(tm->tm_wday >= 0);
208b39c5158Smillert assert(tm->tm_wday <= 6);
209b39c5158Smillert
210b39c5158Smillert assert(tm->tm_yday >= 0);
211b39c5158Smillert assert(tm->tm_yday <= length_of_year[IS_LEAP(tm->tm_year)]);
212b39c5158Smillert
213b39c5158Smillert #ifdef HAS_TM_TM_GMTOFF
214b39c5158Smillert assert(tm->tm_gmtoff >= -24 * 60 * 60);
215b39c5158Smillert assert(tm->tm_gmtoff <= 24 * 60 * 60);
216b39c5158Smillert #endif
217b39c5158Smillert
218b39c5158Smillert return 1;
219b39c5158Smillert }
220b39c5158Smillert #endif
221b39c5158Smillert
222b39c5158Smillert
223b39c5158Smillert /* The exceptional centuries without leap years cause the cycle to
224b39c5158Smillert shift by 16
225b39c5158Smillert */
S_cycle_offset(Year year)226b39c5158Smillert static Year S_cycle_offset(Year year)
227b39c5158Smillert {
228b39c5158Smillert const Year start_year = 2000;
229b39c5158Smillert Year year_diff = year - start_year;
230b39c5158Smillert Year exceptions;
231b39c5158Smillert
232b39c5158Smillert if( year > start_year )
233b39c5158Smillert year_diff--;
234b39c5158Smillert
235b39c5158Smillert exceptions = year_diff / 100;
236b39c5158Smillert exceptions -= year_diff / 400;
237b39c5158Smillert
238b39c5158Smillert TIME64_TRACE3("# year: %lld, exceptions: %lld, year_diff: %lld\n",
239b39c5158Smillert year, exceptions, year_diff);
240b39c5158Smillert
241b39c5158Smillert return exceptions * 16;
242b39c5158Smillert }
243b39c5158Smillert
244b39c5158Smillert /* For a given year after 2038, pick the latest possible matching
245b39c5158Smillert year in the 28 year calendar cycle.
246b39c5158Smillert
247b39c5158Smillert A matching year...
248b39c5158Smillert 1) Starts on the same day of the week.
249b39c5158Smillert 2) Has the same leap year status.
250b39c5158Smillert
251b39c5158Smillert This is so the calendars match up.
252b39c5158Smillert
253b39c5158Smillert Also the previous year must match. When doing Jan 1st you might
254b39c5158Smillert wind up on Dec 31st the previous year when doing a -UTC time zone.
255b39c5158Smillert
256b39c5158Smillert Finally, the next year must have the same start day of week. This
257b39c5158Smillert is for Dec 31st with a +UTC time zone.
258b39c5158Smillert It doesn't need the same leap year status since we only care about
259b39c5158Smillert January 1st.
260b39c5158Smillert */
S_safe_year(Year year)261b39c5158Smillert static int S_safe_year(Year year)
262b39c5158Smillert {
263b39c5158Smillert int safe_year;
264b39c5158Smillert Year year_cycle = year + S_cycle_offset(year);
265b39c5158Smillert
266b39c5158Smillert /* Change non-leap xx00 years to an equivalent */
267b39c5158Smillert if( S_is_exception_century(year) )
268b39c5158Smillert year_cycle += 11;
269b39c5158Smillert
270b39c5158Smillert /* Also xx01 years, since the previous year will be wrong */
271b39c5158Smillert if( S_is_exception_century(year - 1) )
272b39c5158Smillert year_cycle += 17;
273b39c5158Smillert
274b39c5158Smillert year_cycle %= SOLAR_CYCLE_LENGTH;
275b39c5158Smillert if( year_cycle < 0 )
276b39c5158Smillert year_cycle = SOLAR_CYCLE_LENGTH + year_cycle;
277b39c5158Smillert
278b39c5158Smillert assert( year_cycle >= 0 );
279b39c5158Smillert assert( year_cycle < SOLAR_CYCLE_LENGTH );
280b39c5158Smillert safe_year = safe_years[year_cycle];
281b39c5158Smillert
282b39c5158Smillert assert(safe_year <= 2037 && safe_year >= 2010);
283b39c5158Smillert
284b39c5158Smillert TIME64_TRACE3("# year: %lld, year_cycle: %lld, safe_year: %d\n",
285b39c5158Smillert year, year_cycle, safe_year);
286b39c5158Smillert
287b39c5158Smillert return safe_year;
288b39c5158Smillert }
289b39c5158Smillert
290b39c5158Smillert
S_copy_little_tm_to_big_TM(const struct tm * src,struct TM * dest)291b39c5158Smillert static void S_copy_little_tm_to_big_TM(const struct tm *src, struct TM *dest) {
29291f110e0Safresh1 assert(src);
29391f110e0Safresh1 assert(dest);
294b39c5158Smillert #ifdef USE_TM64
295b39c5158Smillert dest->tm_sec = src->tm_sec;
296b39c5158Smillert dest->tm_min = src->tm_min;
297b39c5158Smillert dest->tm_hour = src->tm_hour;
298b39c5158Smillert dest->tm_mday = src->tm_mday;
299b39c5158Smillert dest->tm_mon = src->tm_mon;
300b39c5158Smillert dest->tm_year = (Year)src->tm_year;
301b39c5158Smillert dest->tm_wday = src->tm_wday;
302b39c5158Smillert dest->tm_yday = src->tm_yday;
303b39c5158Smillert dest->tm_isdst = src->tm_isdst;
304b39c5158Smillert
305b39c5158Smillert # ifdef HAS_TM_TM_GMTOFF
306b39c5158Smillert dest->tm_gmtoff = src->tm_gmtoff;
307b39c5158Smillert # endif
308b39c5158Smillert
309b39c5158Smillert # ifdef HAS_TM_TM_ZONE
310b39c5158Smillert dest->tm_zone = src->tm_zone;
311b39c5158Smillert # endif
312b39c5158Smillert
313b39c5158Smillert #else
314b39c5158Smillert /* They're the same type */
315b39c5158Smillert memcpy(dest, src, sizeof(*dest));
316b39c5158Smillert #endif
317b39c5158Smillert }
318b39c5158Smillert
Perl_gmtime64_r(const Time64_T * in_time,struct TM * p)319b8851fccSafresh1 struct TM *Perl_gmtime64_r (const Time64_T *in_time, struct TM *p)
320b39c5158Smillert {
321b39c5158Smillert int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday;
322b39c5158Smillert Time64_T v_tm_tday;
323b39c5158Smillert int leap;
324b39c5158Smillert Time64_T m;
325b39c5158Smillert Time64_T time = *in_time;
326b39c5158Smillert Year year = 70;
32756d68f1eSafresh1 dTHX;
328b39c5158Smillert
329b39c5158Smillert assert(p != NULL);
330b39c5158Smillert
331b39c5158Smillert /* Use the system gmtime() if time_t is small enough */
332b39c5158Smillert if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) {
333b39c5158Smillert time_t safe_time = (time_t)*in_time;
334b39c5158Smillert struct tm safe_date;
33556d68f1eSafresh1 struct tm * result;
336b39c5158Smillert
33756d68f1eSafresh1 GMTIME_LOCK;
33856d68f1eSafresh1
33956d68f1eSafresh1 /* reentr.h will automatically replace this with a call to gmtime_r()
34056d68f1eSafresh1 * when appropriate */
34156d68f1eSafresh1 result = gmtime(&safe_time);
34256d68f1eSafresh1
34356d68f1eSafresh1 assert(result != NULL);
34456d68f1eSafresh1
34556d68f1eSafresh1 #if defined(HAS_GMTIME_R) && defined(USE_REENTRANT_API)
34656d68f1eSafresh1
34756d68f1eSafresh1 PERL_UNUSED_VAR(safe_date);
34856d68f1eSafresh1 #else
34956d68f1eSafresh1 /* Here, no gmtime_r() and is a threaded perl where the result can be
35056d68f1eSafresh1 * overwritten by a call in another thread. Copy to a safe place,
35156d68f1eSafresh1 * hopefully before another gmtime that isn't using the mutexes can
35256d68f1eSafresh1 * jump in and trash this result. */
35356d68f1eSafresh1 memcpy(&safe_date, result, sizeof(safe_date));
35456d68f1eSafresh1 result = &safe_date;
35556d68f1eSafresh1 #endif
35656d68f1eSafresh1 GMTIME_UNLOCK;
35756d68f1eSafresh1
35856d68f1eSafresh1 S_copy_little_tm_to_big_TM(result, p);
359b39c5158Smillert assert(S_check_tm(p));
360b39c5158Smillert
361b39c5158Smillert return p;
362b39c5158Smillert }
363b39c5158Smillert
364b39c5158Smillert #ifdef HAS_TM_TM_GMTOFF
365b39c5158Smillert p->tm_gmtoff = 0;
366b39c5158Smillert #endif
367b39c5158Smillert p->tm_isdst = 0;
368b39c5158Smillert
369b39c5158Smillert #ifdef HAS_TM_TM_ZONE
370eac174f2Safresh1 p->tm_zone = "UTC";
371b39c5158Smillert #endif
372b39c5158Smillert
373b8851fccSafresh1 v_tm_sec = (int)Perl_fmod(time, 60.0);
374b8851fccSafresh1 time = time >= 0 ? Perl_floor(time / 60.0) : Perl_ceil(time / 60.0);
375b8851fccSafresh1 v_tm_min = (int)Perl_fmod(time, 60.0);
376b8851fccSafresh1 time = time >= 0 ? Perl_floor(time / 60.0) : Perl_ceil(time / 60.0);
377b8851fccSafresh1 v_tm_hour = (int)Perl_fmod(time, 24.0);
378b8851fccSafresh1 time = time >= 0 ? Perl_floor(time / 24.0) : Perl_ceil(time / 24.0);
379b39c5158Smillert v_tm_tday = time;
380b39c5158Smillert
381b39c5158Smillert WRAP (v_tm_sec, v_tm_min, 60);
382b39c5158Smillert WRAP (v_tm_min, v_tm_hour, 60);
383b39c5158Smillert WRAP (v_tm_hour, v_tm_tday, 24);
384b39c5158Smillert
385b8851fccSafresh1 v_tm_wday = (int)Perl_fmod((v_tm_tday + 4.0), 7.0);
386b39c5158Smillert if (v_tm_wday < 0)
387b39c5158Smillert v_tm_wday += 7;
388b39c5158Smillert m = v_tm_tday;
389b39c5158Smillert
390b39c5158Smillert if (m >= CHEAT_DAYS) {
391b39c5158Smillert year = CHEAT_YEARS;
392b39c5158Smillert m -= CHEAT_DAYS;
393b39c5158Smillert }
394b39c5158Smillert
395b39c5158Smillert if (m >= 0) {
396b39c5158Smillert /* Gregorian cycles, this is huge optimization for distant times */
3979f11ffb7Safresh1 const int cycles = (int)Perl_floor(m / (Time64_T) days_in_gregorian_cycle);
398b39c5158Smillert if( cycles ) {
399b39c5158Smillert m -= (cycles * (Time64_T) days_in_gregorian_cycle);
400b39c5158Smillert year += (cycles * years_in_gregorian_cycle);
401b39c5158Smillert }
402b39c5158Smillert
403b39c5158Smillert /* Years */
404b39c5158Smillert leap = IS_LEAP (year);
405b39c5158Smillert while (m >= (Time64_T) length_of_year[leap]) {
406b39c5158Smillert m -= (Time64_T) length_of_year[leap];
407b39c5158Smillert year++;
408b39c5158Smillert leap = IS_LEAP (year);
409b39c5158Smillert }
410b39c5158Smillert
411b39c5158Smillert /* Months */
412b39c5158Smillert v_tm_mon = 0;
413b39c5158Smillert while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) {
414b39c5158Smillert m -= (Time64_T) days_in_month[leap][v_tm_mon];
415b39c5158Smillert v_tm_mon++;
416b39c5158Smillert }
417b39c5158Smillert } else {
4189f11ffb7Safresh1 int cycles;
4199f11ffb7Safresh1
420b39c5158Smillert year--;
421b39c5158Smillert
422b39c5158Smillert /* Gregorian cycles */
423b8851fccSafresh1 cycles = (int)Perl_ceil((m / (Time64_T) days_in_gregorian_cycle) + 1);
424b39c5158Smillert if( cycles ) {
425b39c5158Smillert m -= (cycles * (Time64_T) days_in_gregorian_cycle);
426b39c5158Smillert year += (cycles * years_in_gregorian_cycle);
427b39c5158Smillert }
428b39c5158Smillert
429b39c5158Smillert /* Years */
430b39c5158Smillert leap = IS_LEAP (year);
431b39c5158Smillert while (m < (Time64_T) -length_of_year[leap]) {
432b39c5158Smillert m += (Time64_T) length_of_year[leap];
433b39c5158Smillert year--;
434b39c5158Smillert leap = IS_LEAP (year);
435b39c5158Smillert }
436b39c5158Smillert
437b39c5158Smillert /* Months */
438b39c5158Smillert v_tm_mon = 11;
439b39c5158Smillert while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) {
440b39c5158Smillert m += (Time64_T) days_in_month[leap][v_tm_mon];
441b39c5158Smillert v_tm_mon--;
442b39c5158Smillert }
443b39c5158Smillert m += (Time64_T) days_in_month[leap][v_tm_mon];
444b39c5158Smillert }
445b39c5158Smillert
446b39c5158Smillert p->tm_year = year;
447b39c5158Smillert if( p->tm_year != year ) {
448b39c5158Smillert #ifdef EOVERFLOW
449b39c5158Smillert errno = EOVERFLOW;
450b39c5158Smillert #endif
451b39c5158Smillert return NULL;
452b39c5158Smillert }
453b39c5158Smillert
454b39c5158Smillert /* At this point m is less than a year so casting to an int is safe */
455b39c5158Smillert p->tm_mday = (int) m + 1;
456b39c5158Smillert p->tm_yday = julian_days_by_month[leap][v_tm_mon] + (int)m;
457b39c5158Smillert p->tm_sec = v_tm_sec;
458b39c5158Smillert p->tm_min = v_tm_min;
459b39c5158Smillert p->tm_hour = v_tm_hour;
460b39c5158Smillert p->tm_mon = v_tm_mon;
461b39c5158Smillert p->tm_wday = v_tm_wday;
462b39c5158Smillert
463b39c5158Smillert assert(S_check_tm(p));
464b39c5158Smillert
465b39c5158Smillert return p;
466b39c5158Smillert }
467b39c5158Smillert
468b39c5158Smillert
Perl_localtime64_r(const Time64_T * time,struct TM * local_tm)469b8851fccSafresh1 struct TM *Perl_localtime64_r (const Time64_T *time, struct TM *local_tm)
470b39c5158Smillert {
471b39c5158Smillert time_t safe_time;
472b39c5158Smillert struct tm safe_date;
47356d68f1eSafresh1 const struct tm * result;
474b39c5158Smillert struct TM gm_tm;
475eac174f2Safresh1 Year orig_year = 0; /* initialise to avoid spurious compiler warning */
476b39c5158Smillert int month_diff;
47756d68f1eSafresh1 const bool use_system = SHOULD_USE_SYSTEM_LOCALTIME(*time);
47856d68f1eSafresh1 dTHX;
479b39c5158Smillert
480b39c5158Smillert assert(local_tm != NULL);
481b39c5158Smillert
482b39c5158Smillert /* Use the system localtime() if time_t is small enough */
48356d68f1eSafresh1 if (use_system) {
484b39c5158Smillert safe_time = (time_t)*time;
485b39c5158Smillert
486b39c5158Smillert TIME64_TRACE1("Using system localtime for %lld\n", *time);
487b39c5158Smillert }
48856d68f1eSafresh1 else {
489b8851fccSafresh1 if (Perl_gmtime64_r(time, &gm_tm) == NULL) {
490b39c5158Smillert TIME64_TRACE1("gmtime64_r returned null for %lld\n", *time);
491b39c5158Smillert return NULL;
492b39c5158Smillert }
493b39c5158Smillert
494b39c5158Smillert orig_year = gm_tm.tm_year;
495b39c5158Smillert
496b39c5158Smillert if (gm_tm.tm_year > (2037 - 1900) ||
497b39c5158Smillert gm_tm.tm_year < (1970 - 1900)
498b39c5158Smillert )
499b39c5158Smillert {
50056d68f1eSafresh1 TIME64_TRACE1("Mapping tm_year %lld to safe_year\n",
50156d68f1eSafresh1 (Year)gm_tm.tm_year);
502b39c5158Smillert gm_tm.tm_year = S_safe_year((Year)(gm_tm.tm_year + 1900)) - 1900;
503b39c5158Smillert }
504b39c5158Smillert
505b39c5158Smillert safe_time = (time_t)S_timegm64(&gm_tm);
50656d68f1eSafresh1 }
50756d68f1eSafresh1
50856d68f1eSafresh1 LOCALTIME_LOCK;
50956d68f1eSafresh1
51056d68f1eSafresh1 /* reentr.h will automatically replace this with a call to localtime_r()
51156d68f1eSafresh1 * when appropriate */
51256d68f1eSafresh1 result = localtime(&safe_time);
51356d68f1eSafresh1
51456d68f1eSafresh1 if(UNLIKELY(result == NULL)) {
51556d68f1eSafresh1 LOCALTIME_UNLOCK;
51656d68f1eSafresh1 TIME64_TRACE1("localtime(%d) returned NULL\n", (int)safe_time);
517b39c5158Smillert return NULL;
518b39c5158Smillert }
519b39c5158Smillert
52056d68f1eSafresh1 #if ! defined(USE_REENTRANT_API) || defined(PERL_REENTR_USING_LOCALTIME_R)
52156d68f1eSafresh1
52256d68f1eSafresh1 PERL_UNUSED_VAR(safe_date);
52356d68f1eSafresh1
52456d68f1eSafresh1 #else
52556d68f1eSafresh1
52656d68f1eSafresh1 /* Here, would be using localtime_r() if it could, meaning there isn't one,
52756d68f1eSafresh1 * and is a threaded perl where the result can be overwritten by a call in
52856d68f1eSafresh1 * another thread. Copy to a safe place, hopefully before another
52956d68f1eSafresh1 * localtime that isn't using the mutexes can jump in and trash this
53056d68f1eSafresh1 * result. */
53156d68f1eSafresh1 memcpy(&safe_date, result, sizeof(safe_date));
53256d68f1eSafresh1 result = &safe_date;
53356d68f1eSafresh1
53456d68f1eSafresh1 #endif
53556d68f1eSafresh1
53656d68f1eSafresh1 LOCALTIME_UNLOCK;
53756d68f1eSafresh1
53856d68f1eSafresh1 S_copy_little_tm_to_big_TM(result, local_tm);
53956d68f1eSafresh1
54056d68f1eSafresh1 if (! use_system) {
541b39c5158Smillert
542b39c5158Smillert local_tm->tm_year = orig_year;
543b39c5158Smillert if( local_tm->tm_year != orig_year ) {
544b39c5158Smillert TIME64_TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n",
545b39c5158Smillert (Year)local_tm->tm_year, (Year)orig_year);
546b39c5158Smillert
547b39c5158Smillert #ifdef EOVERFLOW
548b39c5158Smillert errno = EOVERFLOW;
549b39c5158Smillert #endif
550b39c5158Smillert return NULL;
551b39c5158Smillert }
552b39c5158Smillert
553b39c5158Smillert month_diff = local_tm->tm_mon - gm_tm.tm_mon;
554b39c5158Smillert
555b39c5158Smillert /* When localtime is Dec 31st previous year and
556b39c5158Smillert gmtime is Jan 1st next year.
557b39c5158Smillert */
558b39c5158Smillert if( month_diff == 11 ) {
559b39c5158Smillert local_tm->tm_year--;
560b39c5158Smillert }
561b39c5158Smillert
562b39c5158Smillert /* When localtime is Jan 1st, next year and
563b39c5158Smillert gmtime is Dec 31st, previous year.
564b39c5158Smillert */
565b39c5158Smillert if( month_diff == -11 ) {
566b39c5158Smillert local_tm->tm_year++;
567b39c5158Smillert }
568b39c5158Smillert
569b39c5158Smillert /* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st
570b39c5158Smillert in a non-leap xx00. There is one point in the cycle
571b39c5158Smillert we can't account for which the safe xx00 year is a leap
572898184e3Ssthen year. So we need to correct for Dec 31st coming out as
573b39c5158Smillert the 366th day of the year.
574b39c5158Smillert */
575b39c5158Smillert if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 )
576b39c5158Smillert local_tm->tm_yday--;
577b39c5158Smillert
57856d68f1eSafresh1 }
57956d68f1eSafresh1
580b39c5158Smillert assert(S_check_tm(local_tm));
581b39c5158Smillert
582b39c5158Smillert return local_tm;
583b39c5158Smillert }
584