1 /*
2 Copyright (c) 1994 Cygnus Support.
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms are permitted
6 provided that the above copyright notice and this paragraph are
7 duplicated in all such forms and that any documentation,
8 and/or other materials related to such
9 distribution and use acknowledge that the software was developed
10 at Cygnus Support, Inc.  Cygnus Support, Inc. may not be used to
11 endorse or promote products derived from this software without
12 specific prior written permission.
13 THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 /*
18  * gmtime_r.c
19  * Original Author: Adapted from tzcode maintained by Arthur David Olson.
20  * Modifications:
21  * - Changed to mktm_r and added __tzcalc_limits - 04/10/02, Jeff Johnston
22  * - Fixed bug in mday computations - 08/12/04, Alex Mogilnikov <alx@intellectronika.ru>
23  * - Fixed bug in __tzcalc_limits - 08/12/04, Alex Mogilnikov <alx@intellectronika.ru>
24  * - Move code from _mktm_r() to gmtime_r() - 05/09/14, Freddie Chopin <freddie_chopin@op.pl>
25  * - Fixed bug in calculations for dates after year 2069 or before year 1901. Ideas for
26  *   solution taken from musl's __secs_to_tm() - 07/12/2014, Freddie Chopin
27  *   <freddie_chopin@op.pl>
28  * - Use faster algorithm from civil_from_days() by Howard Hinnant - 12/06/2014,
29  * Freddie Chopin <freddie_chopin@op.pl>
30  *
31  * Converts the calendar time pointed to by tim_p into a broken-down time
32  * expressed as local time. Returns a pointer to a structure containing the
33  * broken-down time.
34  */
35 
36 #include "local.h"
37 
38 /* Move epoch from 01.01.1970 to 01.03.0000 (yes, Year 0) - this is the first
39  * day of a 400-year long "era", right after additional day of leap year.
40  * This adjustment is required only for date calculation, so instead of
41  * modifying time_t value (which would require 64-bit operations to work
42  * correctly) it's enough to adjust the calculated number of days since epoch.
43  */
44 #define EPOCH_ADJUSTMENT_DAYS	719468L
45 /* year to which the adjustment was made */
46 #define ADJUSTED_EPOCH_YEAR	0
47 /* 1st March of year 0 is Wednesday */
48 #define ADJUSTED_EPOCH_WDAY	3
49 /* there are 97 leap years in 400-year periods. ((400 - 97) * 365 + 97 * 366) */
50 #define DAYS_PER_ERA		146097L
51 /* there are 24 leap years in 100-year periods. ((100 - 24) * 365 + 24 * 366) */
52 #define DAYS_PER_CENTURY	36524L
53 /* there is one leap year every 4 years */
54 #define DAYS_PER_4_YEARS	(3 * 365 + 366)
55 /* number of days in a non-leap year */
56 #define DAYS_PER_YEAR		365
57 /* number of days in January */
58 #define DAYS_IN_JANUARY		31
59 /* number of days in non-leap February */
60 #define DAYS_IN_FEBRUARY	28
61 /* number of years per era */
62 #define YEARS_PER_ERA		400
63 
64 struct tm *
gmtime_r(const time_t * __restrict tim_p,struct tm * __restrict res)65 gmtime_r (const time_t *__restrict tim_p,
66 	struct tm *__restrict res)
67 {
68   long days, rem;
69   const time_t lcltime = *tim_p;
70   int era, weekday, year;
71   unsigned erayear, yearday, month, day;
72   unsigned long eraday;
73 
74   days = lcltime / SECSPERDAY + EPOCH_ADJUSTMENT_DAYS;
75   rem = lcltime % SECSPERDAY;
76   if (rem < 0)
77     {
78       rem += SECSPERDAY;
79       --days;
80     }
81 
82   /* compute hour, min, and sec */
83   res->tm_hour = (int) (rem / SECSPERHOUR);
84   rem %= SECSPERHOUR;
85   res->tm_min = (int) (rem / SECSPERMIN);
86   res->tm_sec = (int) (rem % SECSPERMIN);
87 
88   /* compute day of week */
89   if ((weekday = ((ADJUSTED_EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
90     weekday += DAYSPERWEEK;
91   res->tm_wday = weekday;
92 
93   /* compute year, month, day & day of year */
94   /* for description of this algorithm see
95    * http://howardhinnant.github.io/date_algorithms.html#civil_from_days */
96   era = (days >= 0 ? days : days - (DAYS_PER_ERA - 1)) / DAYS_PER_ERA;
97   eraday = days - era * DAYS_PER_ERA;	/* [0, 146096] */
98   erayear = (eraday - eraday / (DAYS_PER_4_YEARS - 1) + eraday / DAYS_PER_CENTURY -
99       eraday / (DAYS_PER_ERA - 1)) / 365;	/* [0, 399] */
100   yearday = eraday - (DAYS_PER_YEAR * erayear + erayear / 4 - erayear / 100);	/* [0, 365] */
101   month = (5 * yearday + 2) / 153;	/* [0, 11] */
102   day = yearday - (153 * month + 2) / 5 + 1;	/* [1, 31] */
103   month += month < 10 ? 2 : -10;
104   year = ADJUSTED_EPOCH_YEAR + erayear + era * YEARS_PER_ERA + (month <= 1);
105 
106   res->tm_yday = yearday >= DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY ?
107       yearday - (DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY) :
108       yearday + DAYS_IN_JANUARY + DAYS_IN_FEBRUARY + isleap(erayear);
109   res->tm_year = year - YEAR_BASE;
110   res->tm_mon = month;
111   res->tm_mday = day;
112 
113   res->tm_isdst = 0;
114 
115   return (res);
116 }
117