Coverage Report

Created: 2025-07-11 06:29

/src/libplist/src/time64.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
3
Copyright (c) 2007-2010  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
Programmers who have available to them 64-bit time values as a 'long
32
long' type can use localtime64_r() and gmtime64_r() which correctly
33
converts the time even on 32-bit systems. Whether you have 64-bit time
34
values will depend on the operating system.
35
36
localtime64_r() is a 64-bit equivalent of localtime_r().
37
38
gmtime64_r() is a 64-bit equivalent of gmtime_r().
39
40
*/
41
42
#include <assert.h>
43
#include <stdlib.h>
44
#include <stdio.h>
45
#include <string.h>
46
#include <time.h>
47
#include <errno.h>
48
#include "time64.h"
49
#include "time64_limits.h"
50
51
52
static const char days_in_month[2][12] = {
53
    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
54
    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
55
};
56
57
static const short julian_days_by_month[2][12] = {
58
    {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
59
    {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
60
};
61
62
static const char wday_name[7][4] = {
63
    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
64
};
65
66
static const char mon_name[12][4] = {
67
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
68
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
69
};
70
71
static const short length_of_year[2] = { 365, 366 };
72
73
/* Some numbers relating to the gregorian cycle */
74
static const Year     years_in_gregorian_cycle   = 400;
75
0
#define               days_in_gregorian_cycle      ((365 * 400) + 100 - 4 + 1)
76
static const Time64_T seconds_in_gregorian_cycle = days_in_gregorian_cycle * 60LL * 60LL * 24LL;
77
78
/* Year range we can trust the time funcitons with */
79
0
#define MAX_SAFE_YEAR 2037
80
0
#define MIN_SAFE_YEAR 1971
81
82
/* 28 year Julian calendar cycle */
83
0
#define SOLAR_CYCLE_LENGTH 28
84
85
/* Year cycle from MAX_SAFE_YEAR down. */
86
static const short safe_years_high[SOLAR_CYCLE_LENGTH] = {
87
    2016, 2017, 2018, 2019,
88
    2020, 2021, 2022, 2023,
89
    2024, 2025, 2026, 2027,
90
    2028, 2029, 2030, 2031,
91
    2032, 2033, 2034, 2035,
92
    2036, 2037, 2010, 2011,
93
    2012, 2013, 2014, 2015
94
};
95
96
/* Year cycle from MIN_SAFE_YEAR up */
97
static const int safe_years_low[SOLAR_CYCLE_LENGTH] = {
98
    1996, 1997, 1998, 1971,
99
    1972, 1973, 1974, 1975,
100
    1976, 1977, 1978, 1979,
101
    1980, 1981, 1982, 1983,
102
    1984, 1985, 1986, 1987,
103
    1988, 1989, 1990, 1991,
104
    1992, 1993, 1994, 1995,
105
};
106
107
/* This isn't used, but it's handy to look at */
108
#if 0
109
static const char dow_year_start[SOLAR_CYCLE_LENGTH] = {
110
    5, 0, 1, 2,     /* 0       2016 - 2019 */
111
    3, 5, 6, 0,     /* 4  */
112
    1, 3, 4, 5,     /* 8       1996 - 1998, 1971*/
113
    6, 1, 2, 3,     /* 12      1972 - 1975 */
114
    4, 6, 0, 1,     /* 16 */
115
    2, 4, 5, 6,     /* 20      2036, 2037, 2010, 2011 */
116
    0, 2, 3, 4      /* 24      2012, 2013, 2014, 2015 */
117
};
118
#endif
119
120
/* Let's assume people are going to be looking for dates in the future.
121
   Let's provide some cheats so you can skip ahead.
122
   This has a 4x speed boost when near 2008.
123
*/
124
/* Number of days since epoch on Jan 1st, 2008 GMT */
125
0
#define CHEAT_DAYS  (1199145600 / 24 / 60 / 60)
126
0
#define CHEAT_YEARS 108
127
128
0
#define IS_LEAP(n)      ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0)
129
0
#define WRAP(a,b,m)     ((a) = ((a) <  0  ) ? ((b)--, (a) + (m)) : (a))
130
131
#ifdef USE_SYSTEM_LOCALTIME
132
#    define SHOULD_USE_SYSTEM_LOCALTIME(a)  (       \
133
    (a) <= SYSTEM_LOCALTIME_MAX &&              \
134
    (a) >= SYSTEM_LOCALTIME_MIN                 \
135
)
136
#else
137
0
#    define SHOULD_USE_SYSTEM_LOCALTIME(a)      (0)
138
#endif
139
140
#ifdef USE_SYSTEM_GMTIME
141
#    define SHOULD_USE_SYSTEM_GMTIME(a)     (       \
142
    (a) <= SYSTEM_GMTIME_MAX    &&              \
143
    (a) >= SYSTEM_GMTIME_MIN                    \
144
)
145
#else
146
0
#    define SHOULD_USE_SYSTEM_GMTIME(a)         (0)
147
#endif
148
149
/* Multi varadic macros are a C99 thing, alas */
150
#ifdef TIME_64_DEBUG
151
#    define TIME64_TRACE(format) (fprintf(stderr, format))
152
#    define TIME64_TRACE1(format, var1)    (fprintf(stderr, format, var1))
153
#    define TIME64_TRACE2(format, var1, var2)    (fprintf(stderr, format, var1, var2))
154
#    define TIME64_TRACE3(format, var1, var2, var3)    (fprintf(stderr, format, var1, var2, var3))
155
#else
156
#    define TIME64_TRACE(format) ((void)0)
157
0
#    define TIME64_TRACE1(format, var1) ((void)0)
158
0
#    define TIME64_TRACE2(format, var1, var2) ((void)0)
159
0
#    define TIME64_TRACE3(format, var1, var2, var3) ((void)0)
160
#endif
161
162
163
static int is_exception_century(Year year)
164
0
{
165
0
    int is_exception = ((year % 100 == 0) && !(year % 400 == 0));
166
0
    TIME64_TRACE1("# is_exception_century: %s\n", is_exception ? "yes" : "no");
167
168
0
    return(is_exception);
169
0
}
170
171
172
/* Compare two dates.
173
   The result is like cmp.
174
   Ignores things like gmtoffset and dst
175
*/
176
0
static int cmp_date( const struct TM* left, const struct tm* right ) {
177
0
    if( left->tm_year > right->tm_year )
178
0
        return 1;
179
0
    if( left->tm_year < right->tm_year )
180
0
        return -1;
181
0
    if( left->tm_mon > right->tm_mon )
182
0
        return 1;
183
0
    if( left->tm_mon < right->tm_mon )
184
0
        return -1;
185
0
    if( left->tm_mday > right->tm_mday )
186
0
        return 1;
187
0
    if( left->tm_mday < right->tm_mday )
188
0
        return -1;
189
0
    if( left->tm_hour > right->tm_hour )
190
0
        return 1;
191
0
    if( left->tm_hour < right->tm_hour )
192
0
        return -1;
193
0
    if( left->tm_min > right->tm_min )
194
0
        return 1;
195
0
    if( left->tm_min < right->tm_min )
196
0
        return -1;
197
0
    if( left->tm_sec > right->tm_sec )
198
0
        return 1;
199
0
    if( left->tm_sec < right->tm_sec )
200
0
        return -1;
201
0
    return 0;
202
0
}
203
204
205
/* Check if a date is safely inside a range.
206
   The intention is to check if its a few days inside.
207
*/
208
0
static int date_in_safe_range( const struct TM* date, const struct tm* min, const struct tm* max ) {
209
0
    if( cmp_date(date, min) == -1 )
210
0
        return 0;
211
212
0
    if( cmp_date(date, max) == 1 )
213
0
        return 0;
214
215
0
    return 1;
216
0
}
217
218
219
/* timegm() is not in the C or POSIX spec, but it is such a useful
220
   extension I would be remiss in leaving it out.  Also I need it
221
   for localtime64()
222
*/
223
0
Time64_T timegm64(const struct TM *date) {
224
0
    Time64_T days    = 0;
225
0
    Time64_T seconds = 0;
226
0
    Year     year;
227
0
    Year     orig_year = (Year)date->tm_year;
228
0
    int      cycles  = 0;
229
230
0
    if( (orig_year > 100) || (orig_year < -300) ) {
231
0
        cycles = (orig_year - 100) / 400;
232
0
        orig_year -= cycles * 400;
233
0
        days      += (Time64_T)cycles * days_in_gregorian_cycle;
234
0
    }
235
0
    TIME64_TRACE3("# timegm/ cycles: %d, days: %lld, orig_year: %lld\n", cycles, days, orig_year);
236
237
0
    if( orig_year > 70 ) {
238
0
        year = 70;
239
0
        while( year < orig_year ) {
240
0
            days += length_of_year[IS_LEAP(year)];
241
0
            year++;
242
0
        }
243
0
    }
244
0
    else if ( orig_year < 70 ) {
245
0
        year = 69;
246
0
        do {
247
0
            days -= length_of_year[IS_LEAP(year)];
248
0
            year--;
249
0
        } while( year >= orig_year );
250
0
    }
251
252
0
    days += julian_days_by_month[IS_LEAP(orig_year)][date->tm_mon];
253
0
    days += date->tm_mday - 1;
254
255
0
    seconds = days * 60 * 60 * 24;
256
257
0
    seconds += date->tm_hour * 60 * 60;
258
0
    seconds += date->tm_min * 60;
259
0
    seconds += date->tm_sec;
260
261
0
    return(seconds);
262
0
}
263
264
265
static int check_tm(struct TM *tm)
266
0
{
267
    /* Don't forget leap seconds */
268
0
    assert(tm->tm_sec >= 0);
269
0
    assert(tm->tm_sec <= 61);
270
271
0
    assert(tm->tm_min >= 0);
272
0
    assert(tm->tm_min <= 59);
273
274
0
    assert(tm->tm_hour >= 0);
275
0
    assert(tm->tm_hour <= 23);
276
277
0
    assert(tm->tm_mday >= 1);
278
0
    assert(tm->tm_mday <= days_in_month[IS_LEAP(tm->tm_year)][tm->tm_mon]);
279
280
0
    assert(tm->tm_mon  >= 0);
281
0
    assert(tm->tm_mon  <= 11);
282
283
0
    assert(tm->tm_wday >= 0);
284
0
    assert(tm->tm_wday <= 6);
285
286
0
    assert(tm->tm_yday >= 0);
287
0
    assert(tm->tm_yday <= length_of_year[IS_LEAP(tm->tm_year)]);
288
289
0
#ifdef HAVE_TM_TM_GMTOFF
290
0
    assert(tm->tm_gmtoff >= -24 * 60 * 60);
291
0
    assert(tm->tm_gmtoff <=  24 * 60 * 60);
292
0
#endif
293
294
0
    return 1;
295
0
}
296
297
298
/* The exceptional centuries without leap years cause the cycle to
299
   shift by 16
300
*/
301
static Year cycle_offset(Year year)
302
0
{
303
0
    const Year start_year = 2000;
304
0
    Year year_diff  = year - start_year;
305
0
    Year exceptions;
306
307
0
    if( year > start_year )
308
0
        year_diff--;
309
310
0
    exceptions  = year_diff / 100;
311
0
    exceptions -= year_diff / 400;
312
313
0
    TIME64_TRACE3("# year: %lld, exceptions: %lld, year_diff: %lld\n",
314
0
          year, exceptions, year_diff);
315
316
0
    return exceptions * 16;
317
0
}
318
319
/* For a given year after 2038, pick the latest possible matching
320
   year in the 28 year calendar cycle.
321
322
   A matching year...
323
   1) Starts on the same day of the week.
324
   2) Has the same leap year status.
325
326
   This is so the calendars match up.
327
328
   Also the previous year must match.  When doing Jan 1st you might
329
   wind up on Dec 31st the previous year when doing a -UTC time zone.
330
331
   Finally, the next year must have the same start day of week.  This
332
   is for Dec 31st with a +UTC time zone.
333
   It doesn't need the same leap year status since we only care about
334
   January 1st.
335
*/
336
static int safe_year(const Year year)
337
0
{
338
0
    int _safe_year = (int)year;
339
0
    Year year_cycle;
340
341
0
    if( year >= MIN_SAFE_YEAR && year <= MAX_SAFE_YEAR ) {
342
0
        return _safe_year;
343
0
    }
344
345
0
    year_cycle = year + cycle_offset(year);
346
347
    /* safe_years_low is off from safe_years_high by 8 years */
348
0
    if( year < MIN_SAFE_YEAR )
349
0
        year_cycle -= 8;
350
351
    /* Change non-leap xx00 years to an equivalent */
352
0
    if( is_exception_century(year) )
353
0
        year_cycle += 11;
354
355
    /* Also xx01 years, since the previous year will be wrong */
356
0
    if( is_exception_century(year - 1) )
357
0
        year_cycle += 17;
358
359
0
    year_cycle %= SOLAR_CYCLE_LENGTH;
360
0
    if( year_cycle < 0 )
361
0
        year_cycle = SOLAR_CYCLE_LENGTH + year_cycle;
362
363
0
    assert( year_cycle >= 0 );
364
0
    assert( year_cycle < SOLAR_CYCLE_LENGTH );
365
0
    if( year < MIN_SAFE_YEAR )
366
0
        _safe_year = safe_years_low[year_cycle];
367
0
    else if( year > MAX_SAFE_YEAR )
368
0
        _safe_year = safe_years_high[year_cycle];
369
0
    else
370
0
        assert(0);
371
372
0
    TIME64_TRACE3("# year: %lld, year_cycle: %lld, safe_year: %d\n",
373
0
          year, year_cycle, _safe_year);
374
375
0
    assert(_safe_year <= MAX_SAFE_YEAR && _safe_year >= MIN_SAFE_YEAR);
376
377
0
    return _safe_year;
378
0
}
379
380
381
0
void copy_tm_to_TM64(const struct tm *src, struct TM *dest) {
382
0
    if( src == NULL ) {
383
0
        memset(dest, 0, sizeof(*dest));
384
0
    }
385
0
    else {
386
#       ifdef USE_TM64
387
            dest->tm_sec        = src->tm_sec;
388
            dest->tm_min        = src->tm_min;
389
            dest->tm_hour       = src->tm_hour;
390
            dest->tm_mday       = src->tm_mday;
391
            dest->tm_mon        = src->tm_mon;
392
            dest->tm_year       = (Year)src->tm_year;
393
            dest->tm_wday       = src->tm_wday;
394
            dest->tm_yday       = src->tm_yday;
395
            dest->tm_isdst      = src->tm_isdst;
396
397
#           ifdef HAVE_TM_TM_GMTOFF
398
                dest->tm_gmtoff  = src->tm_gmtoff;
399
#           endif
400
401
#           ifdef HAVE_TM_TM_ZONE
402
                dest->tm_zone  = src->tm_zone;
403
#           endif
404
405
#       else
406
            /* They're the same type */
407
0
            memcpy(dest, src, sizeof(*dest));
408
0
#       endif
409
0
    }
410
0
}
411
412
413
0
void copy_TM64_to_tm(const struct TM *src, struct tm *dest) {
414
0
    if( src == NULL ) {
415
0
        memset(dest, 0, sizeof(*dest));
416
0
    }
417
0
    else {
418
#       ifdef USE_TM64
419
            dest->tm_sec        = src->tm_sec;
420
            dest->tm_min        = src->tm_min;
421
            dest->tm_hour       = src->tm_hour;
422
            dest->tm_mday       = src->tm_mday;
423
            dest->tm_mon        = src->tm_mon;
424
            dest->tm_year       = (int)src->tm_year;
425
            dest->tm_wday       = src->tm_wday;
426
            dest->tm_yday       = src->tm_yday;
427
            dest->tm_isdst      = src->tm_isdst;
428
429
#           ifdef HAVE_TM_TM_GMTOFF
430
                dest->tm_gmtoff  = src->tm_gmtoff;
431
#           endif
432
433
#           ifdef HAVE_TM_TM_ZONE
434
                dest->tm_zone  = src->tm_zone;
435
#           endif
436
437
#       else
438
            /* They're the same type */
439
0
            memcpy(dest, src, sizeof(*dest));
440
0
#       endif
441
0
    }
442
0
}
443
444
445
#ifndef HAVE_LOCALTIME_R
446
/* Simulate localtime_r() to the best of our ability */
447
static struct tm * fake_localtime_r(const time_t *time, struct tm *result) {
448
    const struct tm *static_result = localtime(time);
449
450
    assert(result != NULL);
451
452
    if( static_result == NULL ) {
453
        memset(result, 0, sizeof(*result));
454
        return NULL;
455
    }
456
    else {
457
        memcpy(result, static_result, sizeof(*result));
458
        return result;
459
    }
460
}
461
#endif
462
463
464
#ifndef HAVE_GMTIME_R
465
/* Simulate gmtime_r() to the best of our ability */
466
static struct tm * fake_gmtime_r(const time_t *time, struct tm *result) {
467
    const struct tm *static_result = gmtime(time);
468
469
    assert(result != NULL);
470
471
    if( static_result == NULL ) {
472
        memset(result, 0, sizeof(*result));
473
        return NULL;
474
    }
475
    else {
476
        memcpy(result, static_result, sizeof(*result));
477
        return result;
478
    }
479
}
480
#endif
481
482
483
0
static Time64_T seconds_between_years(Year left_year, Year right_year) {
484
0
    int increment = (left_year > right_year) ? 1 : -1;
485
0
    Time64_T seconds = 0;
486
0
    int cycles;
487
488
0
    if( left_year > 2400 ) {
489
0
        cycles = (left_year - 2400) / 400;
490
0
        left_year -= cycles * 400;
491
0
        seconds   += cycles * seconds_in_gregorian_cycle;
492
0
    }
493
0
    else if( left_year < 1600 ) {
494
0
        cycles = (left_year - 1600) / 400;
495
0
        left_year += cycles * 400;
496
0
        seconds   += cycles * seconds_in_gregorian_cycle;
497
0
    }
498
499
0
    while( left_year != right_year ) {
500
0
        seconds += length_of_year[IS_LEAP(right_year - 1900)] * 60 * 60 * 24;
501
0
        right_year += increment;
502
0
    }
503
504
0
    return seconds * increment;
505
0
}
506
507
508
0
Time64_T mktime64(struct TM *input_date) {
509
0
    struct tm safe_date;
510
0
    struct TM date;
511
0
    Time64_T  timev;
512
0
    Year      year = input_date->tm_year + 1900;
513
514
0
    if( date_in_safe_range(input_date, &SYSTEM_MKTIME_MIN, &SYSTEM_MKTIME_MAX) )
515
0
    {
516
0
        copy_TM64_to_tm(input_date, &safe_date);
517
0
        timev = (Time64_T)mktime(&safe_date);
518
519
        /* Correct the possibly out of bound input date */
520
0
        copy_tm_to_TM64(&safe_date, input_date);
521
0
        return timev;
522
0
    }
523
524
    /* Have to make the year safe in date else it won't fit in safe_date */
525
0
    date = *input_date;
526
0
    date.tm_year = safe_year(year) - 1900;
527
0
    copy_TM64_to_tm(&date, &safe_date);
528
529
0
    timev = (Time64_T)mktime(&safe_date);
530
531
    /* Correct the user's possibly out of bound input date */
532
0
    copy_tm_to_TM64(&safe_date, input_date);
533
534
0
    timev += seconds_between_years(year, (Year)(safe_date.tm_year) + 1900);
535
536
0
    return timev;
537
0
}
538
539
540
/* Because I think mktime() is a crappy name */
541
0
Time64_T timelocal64(struct TM *date) {
542
0
    return mktime64(date);
543
0
}
544
545
546
struct TM *gmtime64_r (const Time64_T *in_time, struct TM *p)
547
0
{
548
0
    int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday;
549
0
    Time64_T v_tm_tday;
550
0
    int leap;
551
0
    Time64_T m;
552
0
    Time64_T timev = *in_time;
553
0
    Year year = 70;
554
0
    int cycles = 0;
555
556
0
    assert(p != NULL);
557
558
    /* Use the system gmtime() if time_t is small enough */
559
0
    if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) {
560
0
        time_t safe_time = (time_t)*in_time;
561
0
        struct tm safe_date;
562
0
        GMTIME_R(&safe_time, &safe_date);
563
564
0
        copy_tm_to_TM64(&safe_date, p);
565
0
        assert(check_tm(p));
566
567
0
        return p;
568
0
    }
569
570
0
#ifdef HAVE_TM_TM_GMTOFF
571
0
    p->tm_gmtoff = 0;
572
0
#endif
573
0
    p->tm_isdst  = 0;
574
575
0
#ifdef HAVE_TM_TM_ZONE
576
0
    p->tm_zone   = (char*)"UTC";
577
0
#endif
578
579
0
    v_tm_sec =  (int)(timev % 60);
580
0
    timev /= 60;
581
0
    v_tm_min =  (int)(timev % 60);
582
0
    timev /= 60;
583
0
    v_tm_hour = (int)(timev % 24);
584
0
    timev /= 24;
585
0
    v_tm_tday = timev;
586
587
0
    WRAP (v_tm_sec, v_tm_min, 60);
588
0
    WRAP (v_tm_min, v_tm_hour, 60);
589
0
    WRAP (v_tm_hour, v_tm_tday, 24);
590
591
0
    v_tm_wday = (int)((v_tm_tday + 4) % 7);
592
0
    if (v_tm_wday < 0)
593
0
        v_tm_wday += 7;
594
0
    m = v_tm_tday;
595
596
0
    if (m >= CHEAT_DAYS) {
597
0
        year = CHEAT_YEARS;
598
0
        m -= CHEAT_DAYS;
599
0
    }
600
601
0
    if (m >= 0) {
602
        /* Gregorian cycles, this is huge optimization for distant times */
603
0
        cycles = (int)(m / (Time64_T) days_in_gregorian_cycle);
604
0
        if( cycles ) {
605
0
            m -= (cycles * (Time64_T) days_in_gregorian_cycle);
606
0
            year += (cycles * years_in_gregorian_cycle);
607
0
        }
608
609
        /* Years */
610
0
        leap = IS_LEAP (year);
611
0
        while (m >= (Time64_T) length_of_year[leap]) {
612
0
            m -= (Time64_T) length_of_year[leap];
613
0
            year++;
614
0
            leap = IS_LEAP (year);
615
0
        }
616
617
        /* Months */
618
0
        v_tm_mon = 0;
619
0
        while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) {
620
0
            m -= (Time64_T) days_in_month[leap][v_tm_mon];
621
0
            v_tm_mon++;
622
0
        }
623
0
    } else {
624
0
        year--;
625
626
        /* Gregorian cycles */
627
0
        cycles = (int)((m / (Time64_T) days_in_gregorian_cycle) + 1);
628
0
        if( cycles ) {
629
0
            m -= (cycles * (Time64_T) days_in_gregorian_cycle);
630
0
            year += (cycles * years_in_gregorian_cycle);
631
0
        }
632
633
        /* Years */
634
0
        leap = IS_LEAP (year);
635
0
        while (m < (Time64_T) -length_of_year[leap]) {
636
0
            m += (Time64_T) length_of_year[leap];
637
0
            year--;
638
0
            leap = IS_LEAP (year);
639
0
        }
640
641
        /* Months */
642
0
        v_tm_mon = 11;
643
0
        while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) {
644
0
            m += (Time64_T) days_in_month[leap][v_tm_mon];
645
0
            v_tm_mon--;
646
0
        }
647
0
        m += (Time64_T) days_in_month[leap][v_tm_mon];
648
0
    }
649
650
0
    p->tm_year = year;
651
0
    if( p->tm_year != year ) {
652
0
#ifdef EOVERFLOW
653
0
        errno = EOVERFLOW;
654
0
#endif
655
0
        return NULL;
656
0
    }
657
658
    /* At this point m is less than a year so casting to an int is safe */
659
0
    p->tm_mday = (int) m + 1;
660
0
    p->tm_yday = julian_days_by_month[leap][v_tm_mon] + (int)m;
661
0
    p->tm_sec  = v_tm_sec;
662
0
    p->tm_min  = v_tm_min;
663
0
    p->tm_hour = v_tm_hour;
664
0
    p->tm_mon  = v_tm_mon;
665
0
    p->tm_wday = v_tm_wday;
666
667
0
    assert(check_tm(p));
668
669
0
    return p;
670
0
}
671
672
673
struct TM *localtime64_r (const Time64_T *timev, struct TM *local_tm)
674
0
{
675
0
    time_t safe_time;
676
0
    struct tm safe_date;
677
0
    struct TM gm_tm;
678
0
    Year orig_year;
679
0
    int month_diff;
680
681
0
    assert(local_tm != NULL);
682
683
    /* Use the system localtime() if time_t is small enough */
684
0
    if( SHOULD_USE_SYSTEM_LOCALTIME(*timev) ) {
685
0
        safe_time = (time_t)*timev;
686
687
0
        TIME64_TRACE1("Using system localtime for %lld\n", *timev);
688
689
0
        LOCALTIME_R(&safe_time, &safe_date);
690
691
0
        copy_tm_to_TM64(&safe_date, local_tm);
692
0
        assert(check_tm(local_tm));
693
694
0
        return local_tm;
695
0
    }
696
697
0
    if( gmtime64_r(timev, &gm_tm) == NULL ) {
698
0
        TIME64_TRACE1("gmtime64_r returned null for %lld\n", *timev);
699
0
        return NULL;
700
0
    }
701
702
0
    orig_year = gm_tm.tm_year;
703
704
0
    if (gm_tm.tm_year > (2037 - 1900) ||
705
0
        gm_tm.tm_year < (1970 - 1900)
706
0
       )
707
0
    {
708
0
        TIME64_TRACE1("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year);
709
0
        gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year) + 1900) - 1900;
710
0
    }
711
712
0
    safe_time = (time_t)timegm64(&gm_tm);
713
0
    if( LOCALTIME_R(&safe_time, &safe_date) == NULL ) {
714
0
        TIME64_TRACE1("localtime_r(%d) returned NULL\n", (int)safe_time);
715
0
        return NULL;
716
0
    }
717
718
0
    copy_tm_to_TM64(&safe_date, local_tm);
719
720
0
    local_tm->tm_year = orig_year;
721
0
    if( local_tm->tm_year != orig_year ) {
722
0
        TIME64_TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n",
723
0
              (Year)local_tm->tm_year, (Year)orig_year);
724
725
0
#ifdef EOVERFLOW
726
0
        errno = EOVERFLOW;
727
0
#endif
728
0
        return NULL;
729
0
    }
730
731
732
0
    month_diff = local_tm->tm_mon - gm_tm.tm_mon;
733
734
    /*  When localtime is Dec 31st previous year and
735
        gmtime is Jan 1st next year.
736
    */
737
0
    if( month_diff == 11 ) {
738
0
        local_tm->tm_year--;
739
0
    }
740
741
    /*  When localtime is Jan 1st, next year and
742
        gmtime is Dec 31st, previous year.
743
    */
744
0
    if( month_diff == -11 ) {
745
0
        local_tm->tm_year++;
746
0
    }
747
748
    /* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st
749
       in a non-leap xx00.  There is one point in the cycle
750
       we can't account for which the safe xx00 year is a leap
751
       year.  So we need to correct for Dec 31st comming out as
752
       the 366th day of the year.
753
    */
754
0
    if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 )
755
0
        local_tm->tm_yday--;
756
757
0
    assert(check_tm(local_tm));
758
759
0
    return local_tm;
760
0
}
761
762
763
0
static int valid_tm_wday( const struct TM* date ) {
764
0
    if( 0 <= date->tm_wday && date->tm_wday <= 6 )
765
0
        return 1;
766
767
0
    return 0;
768
0
}
769
770
0
static int valid_tm_mon( const struct TM* date ) {
771
0
    if( 0 <= date->tm_mon && date->tm_mon <= 11 )
772
0
        return 1;
773
774
0
    return 0;
775
0
}
776
777
778
0
char *asctime64_r( const struct TM* date, char *result ) {
779
    /* I figure everything else can be displayed, even hour 25, but if
780
       these are out of range we walk off the name arrays */
781
0
    if( !valid_tm_wday(date) || !valid_tm_mon(date) )
782
0
        return NULL;
783
784
0
    sprintf(result, TM64_ASCTIME_FORMAT,
785
0
        wday_name[date->tm_wday],
786
0
        mon_name[date->tm_mon],
787
0
        date->tm_mday, date->tm_hour,
788
0
        date->tm_min, date->tm_sec,
789
0
        1900 + date->tm_year);
790
791
0
    return result;
792
0
}
793
794
795
0
char *ctime64_r( const Time64_T* timev, char* result ) {
796
0
    struct TM date;
797
798
0
    if (!localtime64_r( timev, &date ))
799
0
        return NULL;
800
801
0
    return asctime64_r( &date, result );
802
0
}
803