/src/php-src/ext/date/lib/unixtime2tm.c
Line | Count | Source (jump to first uncovered line) |
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 | | void timelib_unixtime2date(timelib_sll ts, timelib_sll *y, timelib_sll *m, timelib_sll *d) |
30 | 5.52k | { |
31 | 5.52k | timelib_sll days, era, t; |
32 | 5.52k | timelib_ull day_of_era, year_of_era, day_of_year, month_portion; |
33 | | |
34 | | /* Calculate days since algorithm's epoch (0000-03-01) */ |
35 | 5.52k | days = ts / SECS_PER_DAY + HINNANT_EPOCH_SHIFT; |
36 | | |
37 | | /* Adjustment for a negative time portion */ |
38 | 5.52k | t = ts % SECS_PER_DAY; |
39 | 5.52k | days += (t < 0) ? -1 : 0; |
40 | | |
41 | | /* Calculate year, month, and day. Algorithm from: |
42 | | * http://howardhinnant.github.io/date_algorithms.html#civil_from_days */ |
43 | 5.52k | era = (days >= 0 ? days : days - DAYS_PER_ERA + 1) / DAYS_PER_ERA; |
44 | 5.52k | day_of_era = days - era * DAYS_PER_ERA; |
45 | 5.52k | year_of_era = (day_of_era - day_of_era / 1460 + day_of_era / 36524 - day_of_era / 146096) / DAYS_PER_YEAR; |
46 | 5.52k | *y = year_of_era + era * YEARS_PER_ERA; |
47 | 5.52k | day_of_year = day_of_era - (DAYS_PER_YEAR * year_of_era + year_of_era / 4 - year_of_era / 100); |
48 | 5.52k | month_portion = (5 * day_of_year + 2) / 153; |
49 | 5.52k | *d = day_of_year - (153 * month_portion + 2) / 5 + 1; |
50 | 5.52k | *m = month_portion + (month_portion < 10 ? 3 : -9); |
51 | 5.52k | *y += (*m <= 2); |
52 | | |
53 | 5.52k | TIMELIB_DEBUG(printf("A: ts=%lld, year=%lld, month=%lld, day=%lld,", ts, *y, *m, *d);); |
54 | 5.52k | } |
55 | | |
56 | | /* Converts a Unix timestamp value into broken down time, in GMT */ |
57 | | void timelib_unixtime2gmt(timelib_time* tm, timelib_sll ts) |
58 | 5.52k | { |
59 | 5.52k | timelib_sll remainder; |
60 | 5.52k | timelib_sll hours, minutes, seconds; |
61 | | |
62 | 5.52k | timelib_unixtime2date(ts, &tm->y, &tm->m, &tm->d); |
63 | 5.52k | remainder = ts % SECS_PER_DAY; |
64 | 5.52k | remainder += (remainder < 0) * SECS_PER_DAY; |
65 | | |
66 | | /* That was the date, now we do the time */ |
67 | 5.52k | hours = remainder / 3600; |
68 | 5.52k | minutes = (remainder - hours * 3600) / 60; |
69 | 5.52k | seconds = remainder % 60; |
70 | 5.52k | TIMELIB_DEBUG(printf(" hour=%lld, minute=%lld, second=%lld\n", hours, minutes, seconds);); |
71 | | |
72 | 5.52k | tm->h = hours; |
73 | 5.52k | tm->i = minutes; |
74 | 5.52k | tm->s = seconds; |
75 | 5.52k | tm->z = 0; |
76 | 5.52k | tm->dst = 0; |
77 | 5.52k | tm->sse = ts; |
78 | 5.52k | tm->sse_uptodate = 1; |
79 | 5.52k | tm->tim_uptodate = 1; |
80 | 5.52k | tm->is_localtime = 0; |
81 | 5.52k | } |
82 | | |
83 | | void timelib_update_from_sse(timelib_time *tm) |
84 | 244 | { |
85 | 244 | timelib_sll sse; |
86 | 244 | int z = tm->z; |
87 | 244 | signed int dst = tm->dst; |
88 | | |
89 | 244 | sse = tm->sse; |
90 | | |
91 | 244 | switch (tm->zone_type) { |
92 | 0 | case TIMELIB_ZONETYPE_ABBR: |
93 | 13 | case TIMELIB_ZONETYPE_OFFSET: { |
94 | 13 | timelib_unixtime2gmt(tm, tm->sse + tm->z + (tm->dst * 3600)); |
95 | | |
96 | 13 | goto cleanup; |
97 | 0 | } |
98 | | |
99 | 231 | case TIMELIB_ZONETYPE_ID: { |
100 | 231 | int32_t offset = 0; |
101 | | |
102 | 231 | timelib_get_time_zone_offset_info(tm->sse, tm->tz_info, &offset, NULL, NULL); |
103 | 231 | timelib_unixtime2gmt(tm, tm->sse + offset); |
104 | | |
105 | 231 | goto cleanup; |
106 | 0 | } |
107 | | |
108 | 0 | default: |
109 | 0 | timelib_unixtime2gmt(tm, tm->sse); |
110 | 0 | goto cleanup; |
111 | 244 | } |
112 | 244 | cleanup: |
113 | 244 | tm->sse = sse; |
114 | 244 | tm->is_localtime = 1; |
115 | 244 | tm->have_zone = 1; |
116 | 244 | tm->z = z; |
117 | 244 | tm->dst = dst; |
118 | 244 | } |
119 | | |
120 | | void timelib_unixtime2local(timelib_time *tm, timelib_sll ts) |
121 | 5.28k | { |
122 | 5.28k | timelib_time_offset *gmt_offset; |
123 | 5.28k | timelib_tzinfo *tz = tm->tz_info; |
124 | | |
125 | 5.28k | switch (tm->zone_type) { |
126 | 25 | case TIMELIB_ZONETYPE_ABBR: |
127 | 25 | case TIMELIB_ZONETYPE_OFFSET: { |
128 | 25 | int z = tm->z; |
129 | 25 | signed int dst = tm->dst; |
130 | | |
131 | 25 | timelib_unixtime2gmt(tm, ts + tm->z + (tm->dst * 3600)); |
132 | | |
133 | 25 | tm->sse = ts; |
134 | 25 | tm->z = z; |
135 | 25 | tm->dst = dst; |
136 | 25 | break; |
137 | 25 | } |
138 | | |
139 | 5.25k | case TIMELIB_ZONETYPE_ID: |
140 | 5.25k | gmt_offset = timelib_get_time_zone_info(ts, tz); |
141 | 5.25k | timelib_unixtime2gmt(tm, ts + gmt_offset->offset); |
142 | | |
143 | | /* we need to reset the sse here as unixtime2gmt modifies it */ |
144 | 5.25k | tm->sse = ts; |
145 | 5.25k | tm->dst = gmt_offset->is_dst; |
146 | 5.25k | tm->z = gmt_offset->offset; |
147 | 5.25k | tm->tz_info = tz; |
148 | | |
149 | 5.25k | timelib_time_tz_abbr_update(tm, gmt_offset->abbr); |
150 | 5.25k | timelib_time_offset_dtor(gmt_offset); |
151 | 5.25k | break; |
152 | | |
153 | 0 | default: |
154 | 0 | tm->is_localtime = 0; |
155 | 0 | tm->have_zone = 0; |
156 | 0 | return; |
157 | 5.28k | } |
158 | | |
159 | 5.28k | tm->is_localtime = 1; |
160 | 5.28k | tm->have_zone = 1; |
161 | 5.28k | } |
162 | | |
163 | | void timelib_set_timezone_from_offset(timelib_time *t, timelib_sll utc_offset) |
164 | 0 | { |
165 | 0 | if (t->tz_abbr) { |
166 | 0 | timelib_free(t->tz_abbr); |
167 | 0 | } |
168 | 0 | t->tz_abbr = NULL; |
169 | |
|
170 | 0 | t->z = utc_offset; |
171 | 0 | t->have_zone = 1; |
172 | 0 | t->zone_type = TIMELIB_ZONETYPE_OFFSET; |
173 | 0 | t->dst = 0; |
174 | 0 | t->tz_info = NULL; |
175 | 0 | } |
176 | | |
177 | | void timelib_set_timezone_from_abbr(timelib_time *t, timelib_abbr_info abbr_info) |
178 | 0 | { |
179 | 0 | if (t->tz_abbr) { |
180 | 0 | timelib_free(t->tz_abbr); |
181 | 0 | } |
182 | 0 | t->tz_abbr = timelib_strdup(abbr_info.abbr); |
183 | |
|
184 | 0 | t->z = abbr_info.utc_offset; |
185 | 0 | t->have_zone = 1; |
186 | 0 | t->zone_type = TIMELIB_ZONETYPE_ABBR; |
187 | 0 | t->dst = abbr_info.dst; |
188 | 0 | t->tz_info = NULL; |
189 | 0 | } |
190 | | |
191 | | void timelib_set_timezone(timelib_time *t, timelib_tzinfo *tz) |
192 | 236 | { |
193 | 236 | timelib_time_offset *gmt_offset; |
194 | | |
195 | 236 | gmt_offset = timelib_get_time_zone_info(t->sse, tz); |
196 | 236 | t->z = gmt_offset->offset; |
197 | | /* |
198 | | if (t->dst != gmt_offset->is_dst) { |
199 | | printf("ERROR (%d, %d)\n", t->dst, gmt_offset->is_dst); |
200 | | exit(1); |
201 | | } |
202 | | */ |
203 | 236 | t->dst = gmt_offset->is_dst; |
204 | 236 | t->tz_info = tz; |
205 | 236 | if (t->tz_abbr) { |
206 | 236 | timelib_free(t->tz_abbr); |
207 | 236 | } |
208 | 236 | t->tz_abbr = timelib_strdup(gmt_offset->abbr); |
209 | 236 | timelib_time_offset_dtor(gmt_offset); |
210 | | |
211 | 236 | t->have_zone = 1; |
212 | 236 | t->zone_type = TIMELIB_ZONETYPE_ID; |
213 | 236 | } |
214 | | |
215 | | /* Converts the time stored in the struct to localtime if localtime = true, |
216 | | * otherwise it converts it to gmttime. This is only done when necessary |
217 | | * of course. */ |
218 | | int timelib_apply_localtime(timelib_time *t, unsigned int localtime) |
219 | 0 | { |
220 | 0 | if (localtime) { |
221 | | /* Converting from GMT time to local time */ |
222 | 0 | TIMELIB_DEBUG(printf("Converting from GMT time to local time\n");); |
223 | | |
224 | | /* Check if TZ is set */ |
225 | 0 | if (!t->tz_info) { |
226 | 0 | TIMELIB_DEBUG(printf("E: No timezone configured, can't switch to local time\n");); |
227 | 0 | return -1; |
228 | 0 | } |
229 | | |
230 | 0 | timelib_unixtime2local(t, t->sse); |
231 | 0 | } else { |
232 | | /* Converting from local time to GMT time */ |
233 | 0 | TIMELIB_DEBUG(printf("Converting from local time to GMT time\n");); |
234 | |
|
235 | 0 | timelib_unixtime2gmt(t, t->sse); |
236 | 0 | } |
237 | 0 | return 0; |
238 | 0 | } |