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