xref: /PHP-7.4/ext/date/lib/unixtime2tm.c (revision aae5907c)
1 /*
2  * The MIT License (MIT)
3  *
4  * Copyright (c) 2015-2019 Derick Rethans
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 
25 #include "timelib.h"
26 #include "timelib_private.h"
27 
28 static int month_tab_leap[12] = { -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
29 static int month_tab[12] =      { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
30 
31 
32 /* Converts a Unix timestamp value into broken down time, in GMT */
timelib_unixtime2gmt(timelib_time * tm,timelib_sll ts)33 void timelib_unixtime2gmt(timelib_time* tm, timelib_sll ts)
34 {
35 	timelib_sll days, remainder, tmp_days;
36 	timelib_sll cur_year = 1970;
37 	timelib_sll i;
38 	timelib_sll hours, minutes, seconds;
39 	int *months;
40 
41 	days = ts / SECS_PER_DAY;
42 	remainder = ts - (days * SECS_PER_DAY);
43 	if (ts < 0 && remainder == 0) {
44 		days++;
45 		remainder -= SECS_PER_DAY;
46 	}
47 	TIMELIB_DEBUG(printf("days=%lld, rem=%lld\n", days, remainder););
48 
49 	if (ts >= 0) {
50 		tmp_days = days + 1;
51 	} else {
52 		tmp_days = days;
53 	}
54 
55 	if (tmp_days > DAYS_PER_LYEAR_PERIOD || tmp_days <= -DAYS_PER_LYEAR_PERIOD) {
56 		cur_year += YEARS_PER_LYEAR_PERIOD * (tmp_days / DAYS_PER_LYEAR_PERIOD);
57 		tmp_days -= DAYS_PER_LYEAR_PERIOD * (tmp_days / DAYS_PER_LYEAR_PERIOD);
58 	}
59 	TIMELIB_DEBUG(printf("tmp_days=%lld, year=%lld\n", tmp_days, cur_year););
60 
61 	if (ts >= 0) {
62 		while (tmp_days >= DAYS_PER_LYEAR) {
63 			cur_year++;
64 			if (timelib_is_leap(cur_year)) {
65 				tmp_days -= DAYS_PER_LYEAR;
66 			} else {
67 				tmp_days -= DAYS_PER_YEAR;
68 			}
69 			TIMELIB_DEBUG(printf("tmp_days=%lld, year=%lld\n", tmp_days, cur_year););
70 		}
71 	} else {
72 		while (tmp_days <= 0) {
73 			cur_year--;
74 			if (timelib_is_leap(cur_year)) {
75 				tmp_days += DAYS_PER_LYEAR;
76 			} else {
77 				tmp_days += DAYS_PER_YEAR;
78 			}
79 			TIMELIB_DEBUG(printf("tmp_days=%lld, year=%lld\n", tmp_days, cur_year););
80 		}
81 		remainder += SECS_PER_DAY;
82 	}
83 	TIMELIB_DEBUG(printf("tmp_days=%lld, year=%lld\n", tmp_days, cur_year););
84 
85 	months = timelib_is_leap(cur_year) ? month_tab_leap : month_tab;
86 	if (timelib_is_leap(cur_year) && cur_year < 1970) {
87 		tmp_days--;
88 	}
89 	i = 11;
90 	while (i > 0) {
91 		TIMELIB_DEBUG(printf("month=%lld (%d)\n", i, months[i]););
92 		if (tmp_days > months[i]) {
93 			break;
94 		}
95 		i--;
96 	}
97 	TIMELIB_DEBUG(printf("A: ts=%lld, year=%lld, month=%lld, day=%lld,", ts, cur_year, i + 1, tmp_days - months[i]););
98 
99 	/* That was the date, now we do the time */
100 	hours = remainder / 3600;
101 	minutes = (remainder - hours * 3600) / 60;
102 	seconds = remainder % 60;
103 	TIMELIB_DEBUG(printf(" hour=%lld, minute=%lld, second=%lld\n", hours, minutes, seconds););
104 
105 	tm->y = cur_year;
106 	tm->m = i + 1;
107 	tm->d = tmp_days - months[i];
108 	tm->h = hours;
109 	tm->i = minutes;
110 	tm->s = seconds;
111 	tm->z = 0;
112 	tm->dst = 0;
113 	tm->sse = ts;
114 	tm->sse_uptodate = 1;
115 	tm->tim_uptodate = 1;
116 	tm->is_localtime = 0;
117 }
118 
timelib_update_from_sse(timelib_time * tm)119 void timelib_update_from_sse(timelib_time *tm)
120 {
121 	timelib_sll sse;
122 	int z = tm->z;
123 	signed int dst = tm->dst;
124 
125 	sse = tm->sse;
126 
127 	switch (tm->zone_type) {
128 		case TIMELIB_ZONETYPE_ABBR:
129 		case TIMELIB_ZONETYPE_OFFSET: {
130 			timelib_unixtime2gmt(tm, tm->sse + tm->z + (tm->dst * 3600));
131 
132 			goto cleanup;
133 		}
134 
135 		case TIMELIB_ZONETYPE_ID: {
136 			timelib_time_offset *gmt_offset;
137 
138 			gmt_offset = timelib_get_time_zone_info(tm->sse, tm->tz_info);
139 			timelib_unixtime2gmt(tm, tm->sse + gmt_offset->offset);
140 			timelib_time_offset_dtor(gmt_offset);
141 
142 			goto cleanup;
143 		}
144 
145 		default:
146 			timelib_unixtime2gmt(tm, tm->sse);
147 			goto cleanup;
148 	}
149 cleanup:
150 	tm->sse = sse;
151 	tm->is_localtime = 1;
152 	tm->have_zone = 1;
153 	tm->z = z;
154 	tm->dst = dst;
155 }
156 
timelib_unixtime2local(timelib_time * tm,timelib_sll ts)157 void timelib_unixtime2local(timelib_time *tm, timelib_sll ts)
158 {
159 	timelib_time_offset *gmt_offset;
160 	timelib_tzinfo      *tz = tm->tz_info;
161 
162 	switch (tm->zone_type) {
163 		case TIMELIB_ZONETYPE_ABBR:
164 		case TIMELIB_ZONETYPE_OFFSET: {
165 			int z = tm->z;
166 			signed int dst = tm->dst;
167 
168 			timelib_unixtime2gmt(tm, ts + tm->z + (tm->dst * 3600));
169 
170 			tm->sse = ts;
171 			tm->z = z;
172 			tm->dst = dst;
173 			break;
174 		}
175 
176 		case TIMELIB_ZONETYPE_ID:
177 			gmt_offset = timelib_get_time_zone_info(ts, tz);
178 			timelib_unixtime2gmt(tm, ts + gmt_offset->offset);
179 
180 			/* we need to reset the sse here as unixtime2gmt modifies it */
181 			tm->sse = ts;
182 			tm->dst = gmt_offset->is_dst;
183 			tm->z = gmt_offset->offset;
184 			tm->tz_info = tz;
185 
186 			timelib_time_tz_abbr_update(tm, gmt_offset->abbr);
187 			timelib_time_offset_dtor(gmt_offset);
188 			break;
189 
190 		default:
191 			tm->is_localtime = 0;
192 			tm->have_zone = 0;
193 			return;
194 	}
195 
196 	tm->is_localtime = 1;
197 	tm->have_zone = 1;
198 }
199 
timelib_set_timezone_from_offset(timelib_time * t,timelib_sll utc_offset)200 void timelib_set_timezone_from_offset(timelib_time *t, timelib_sll utc_offset)
201 {
202 	if (t->tz_abbr) {
203 		timelib_free(t->tz_abbr);
204 	}
205 	t->tz_abbr = NULL;
206 
207 	t->z = utc_offset;
208 	t->have_zone = 1;
209 	t->zone_type = TIMELIB_ZONETYPE_OFFSET;
210 	t->dst = 0;
211 	t->tz_info = NULL;
212 }
213 
timelib_set_timezone_from_abbr(timelib_time * t,timelib_abbr_info abbr_info)214 void timelib_set_timezone_from_abbr(timelib_time *t, timelib_abbr_info abbr_info)
215 {
216 	if (t->tz_abbr) {
217 		timelib_free(t->tz_abbr);
218 	}
219 	t->tz_abbr = timelib_strdup(abbr_info.abbr);
220 
221 	t->z = abbr_info.utc_offset;
222 	t->have_zone = 1;
223 	t->zone_type = TIMELIB_ZONETYPE_ABBR;
224 	t->dst = abbr_info.dst;
225 	t->tz_info = NULL;
226 }
227 
timelib_set_timezone(timelib_time * t,timelib_tzinfo * tz)228 void timelib_set_timezone(timelib_time *t, timelib_tzinfo *tz)
229 {
230 	timelib_time_offset *gmt_offset;
231 
232 	gmt_offset = timelib_get_time_zone_info(t->sse, tz);
233 	t->z = gmt_offset->offset;
234 /*
235 	if (t->dst != gmt_offset->is_dst) {
236 		printf("ERROR (%d, %d)\n", t->dst, gmt_offset->is_dst);
237 		exit(1);
238 	}
239 */
240 	t->dst = gmt_offset->is_dst;
241 	t->tz_info = tz;
242 	if (t->tz_abbr) {
243 		timelib_free(t->tz_abbr);
244 	}
245 	t->tz_abbr = timelib_strdup(gmt_offset->abbr);
246 	timelib_time_offset_dtor(gmt_offset);
247 
248 	t->have_zone = 1;
249 	t->zone_type = TIMELIB_ZONETYPE_ID;
250 }
251 
252 /* Converts the time stored in the struct to localtime if localtime = true,
253  * otherwise it converts it to gmttime. This is only done when necessary
254  * of course. */
timelib_apply_localtime(timelib_time * t,unsigned int localtime)255 int timelib_apply_localtime(timelib_time *t, unsigned int localtime)
256 {
257 	if (localtime) {
258 		/* Converting from GMT time to local time */
259 		TIMELIB_DEBUG(printf("Converting from GMT time to local time\n"););
260 
261 		/* Check if TZ is set */
262 		if (!t->tz_info) {
263 			TIMELIB_DEBUG(printf("E: No timezone configured, can't switch to local time\n"););
264 			return -1;
265 		}
266 
267 		timelib_unixtime2local(t, t->sse);
268 	} else {
269 		/* Converting from local time to GMT time */
270 		TIMELIB_DEBUG(printf("Converting from local time to GMT time\n"););
271 
272 		timelib_unixtime2gmt(t, t->sse);
273 	}
274 	return 0;
275 }
276