Line data Source code
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 : #include <math.h>
28 :
29 0 : static void swap_times(timelib_time **one, timelib_time **two, timelib_rel_time *rt)
30 : {
31 : timelib_time *swp;
32 :
33 0 : swp = *two;
34 0 : *two = *one;
35 0 : *one = swp;
36 0 : rt->invert = 1;
37 0 : }
38 :
39 0 : static void sort_old_to_new(timelib_time **one, timelib_time **two, timelib_rel_time *rt)
40 : {
41 : /* Check whether date/times need to be inverted. If both times are
42 : * TIMELIB_ZONETYPE_ID times with the same TZID, we use the y-s + us fields. */
43 0 : if (
44 0 : (*one)->zone_type == TIMELIB_ZONETYPE_ID &&
45 0 : (*two)->zone_type == TIMELIB_ZONETYPE_ID &&
46 0 : (strcmp((*one)->tz_info->name, (*two)->tz_info->name) == 0)
47 : ) {
48 0 : if (
49 0 : ((*one)->y > (*two)->y) ||
50 0 : ((*one)->y == (*two)->y && (*one)->m > (*two)->m) ||
51 0 : ((*one)->y == (*two)->y && (*one)->m == (*two)->m && (*one)->d > (*two)->d) ||
52 0 : ((*one)->y == (*two)->y && (*one)->m == (*two)->m && (*one)->d == (*two)->d && (*one)->h > (*two)->h) ||
53 0 : ((*one)->y == (*two)->y && (*one)->m == (*two)->m && (*one)->d == (*two)->d && (*one)->h == (*two)->h && (*one)->i > (*two)->i) ||
54 0 : ((*one)->y == (*two)->y && (*one)->m == (*two)->m && (*one)->d == (*two)->d && (*one)->h == (*two)->h && (*one)->i == (*two)->i && (*one)->s > (*two)->s) ||
55 0 : ((*one)->y == (*two)->y && (*one)->m == (*two)->m && (*one)->d == (*two)->d && (*one)->h == (*two)->h && (*one)->i == (*two)->i && (*one)->s == (*two)->s && (*one)->us > (*two)->us)
56 : ) {
57 0 : swap_times(one, two, rt);
58 : }
59 0 : return;
60 : }
61 :
62 : /* Fall back to using the SSE instead to rearrange */
63 0 : if (
64 0 : ((*one)->sse > (*two)->sse) ||
65 0 : ((*one)->sse == (*two)->sse && (*one)->us > (*two)->us)
66 : ) {
67 0 : swap_times(one, two, rt);
68 : }
69 : }
70 :
71 0 : static timelib_rel_time *timelib_diff_with_tzid(timelib_time *one, timelib_time *two)
72 : {
73 : timelib_rel_time *rt;
74 0 : timelib_sll dst_corr = 0, dst_h_corr = 0, dst_m_corr = 0;
75 : int32_t trans_offset;
76 : timelib_sll trans_transition_time;
77 :
78 0 : rt = timelib_rel_time_ctor();
79 0 : rt->invert = 0;
80 :
81 0 : sort_old_to_new(&one, &two, rt);
82 :
83 : /* Calculate correction for UTC offset changes between first and second SSE */
84 0 : dst_corr = two->z - one->z;
85 0 : dst_h_corr = dst_corr / 3600;
86 0 : dst_m_corr = (dst_corr % 3600) / 60;
87 :
88 0 : rt->y = two->y - one->y;
89 0 : rt->m = two->m - one->m;
90 0 : rt->d = two->d - one->d;
91 0 : rt->h = two->h - one->h;
92 0 : rt->i = two->i - one->i;
93 0 : rt->s = two->s - one->s;
94 0 : rt->us = two->us - one->us;
95 :
96 0 : rt->days = timelib_diff_days(one, two);
97 :
98 : /* Fall Back: Cater for transition period, where rt->invert is 0, but there are negative numbers */
99 0 : if (two->sse < one->sse) {
100 0 : timelib_sll flipped = llabs((rt->i * 60) + (rt->s) - dst_corr);
101 0 : rt->h = flipped / SECS_PER_HOUR;
102 0 : rt->i = (flipped - rt->h * SECS_PER_HOUR) / 60;
103 0 : rt->s = flipped % 60;
104 :
105 0 : rt->invert = 1 - rt->invert;
106 : }
107 :
108 0 : timelib_do_rel_normalize(rt->invert ? one : two, rt);
109 :
110 0 : if (one->dst == 1 && two->dst == 0) { /* Fall Back */
111 0 : if (two->tz_info) {
112 0 : if ((two->sse - one->sse + dst_corr) < SECS_PER_DAY) {
113 0 : rt->h -= dst_h_corr;
114 0 : rt->i -= dst_m_corr;
115 : }
116 : }
117 0 : } else if (one->dst == 0 && two->dst == 1) { /* Spring Forward */
118 0 : if (two->tz_info) {
119 0 : int success = timelib_get_time_zone_offset_info(two->sse, two->tz_info, &trans_offset, &trans_transition_time, NULL);
120 :
121 0 : if (
122 0 : success &&
123 0 : !((one->sse + SECS_PER_DAY > trans_transition_time) && (one->sse + SECS_PER_DAY <= (trans_transition_time + dst_corr))) &&
124 0 : two->sse >= trans_transition_time &&
125 0 : ((two->sse - one->sse + dst_corr) % SECS_PER_DAY) > (two->sse - trans_transition_time)
126 : ) {
127 0 : rt->h -= dst_h_corr;
128 0 : rt->i -= dst_m_corr;
129 : }
130 : }
131 0 : } else if (two->sse - one->sse >= SECS_PER_DAY) {
132 : /* Check whether we're in the period to the next transition time */
133 0 : if (timelib_get_time_zone_offset_info(two->sse - two->z, two->tz_info, &trans_offset, &trans_transition_time, NULL)) {
134 0 : dst_corr = one->z - trans_offset;
135 :
136 0 : if (two->sse >= trans_transition_time - dst_corr && two->sse < trans_transition_time) {
137 0 : rt->d--;
138 0 : rt->h = 24;
139 : }
140 : }
141 : }
142 :
143 0 : return rt;
144 : }
145 :
146 0 : timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two)
147 : {
148 : timelib_rel_time *rt;
149 :
150 0 : if (one->zone_type == TIMELIB_ZONETYPE_ID && two->zone_type == TIMELIB_ZONETYPE_ID && strcmp(one->tz_info->name, two->tz_info->name) == 0) {
151 0 : return timelib_diff_with_tzid(one, two);
152 : }
153 :
154 0 : rt = timelib_rel_time_ctor();
155 0 : rt->invert = 0;
156 :
157 0 : sort_old_to_new(&one, &two, rt);
158 :
159 0 : rt->y = two->y - one->y;
160 0 : rt->m = two->m - one->m;
161 0 : rt->d = two->d - one->d;
162 0 : rt->h = two->h - one->h;
163 0 : if (one->zone_type != TIMELIB_ZONETYPE_ID) {
164 0 : rt->h = rt->h + one->dst;
165 : }
166 0 : if (two->zone_type != TIMELIB_ZONETYPE_ID) {
167 0 : rt->h = rt->h - two->dst;
168 : }
169 0 : rt->i = two->i - one->i;
170 0 : rt->s = two->s - one->s - two->z + one->z;
171 0 : rt->us = two->us - one->us;
172 :
173 0 : rt->days = timelib_diff_days(one, two);
174 :
175 0 : timelib_do_rel_normalize(rt->invert ? one : two, rt);
176 :
177 0 : return rt;
178 : }
179 :
180 :
181 0 : int timelib_diff_days(timelib_time *one, timelib_time *two)
182 : {
183 0 : int days = 0;
184 :
185 0 : if (timelib_same_timezone(one, two)) {
186 : timelib_time *earliest, *latest;
187 : double earliest_time, latest_time;
188 :
189 0 : if (timelib_time_compare(one, two) < 0) {
190 0 : earliest = one;
191 0 : latest = two;
192 : } else {
193 0 : earliest = two;
194 0 : latest = one;
195 : }
196 0 : timelib_hmsf_to_decimal_hour(earliest->h, earliest->i, earliest->s, earliest->us, &earliest_time);
197 0 : timelib_hmsf_to_decimal_hour(latest->h, latest->i, latest->s, latest->us, &latest_time);
198 :
199 0 : days = llabs(timelib_epoch_days_from_time(one) - timelib_epoch_days_from_time(two));
200 0 : if (latest_time < earliest_time && days > 0) {
201 0 : days--;
202 : }
203 : } else {
204 : /* FIXME: This truncates the range of days to INT_MAX to avoid an
205 : * overflow later on. Ideally, all APIs have better error handling. */
206 0 : double ddays = fabs(floor(one->sse - two->sse) / 86400);
207 0 : days = ddays <= INT_MAX ? ddays : INT_MAX;
208 : }
209 :
210 0 : return days;
211 : }
212 :
213 :
214 0 : timelib_time *timelib_add(timelib_time *old_time, timelib_rel_time *interval)
215 : {
216 0 : int bias = 1;
217 0 : timelib_time *t = timelib_time_clone(old_time);
218 :
219 0 : if (interval->have_weekday_relative || interval->have_special_relative) {
220 0 : memcpy(&t->relative, interval, sizeof(timelib_rel_time));
221 : } else {
222 0 : if (interval->invert) {
223 0 : bias = -1;
224 : }
225 0 : memset(&t->relative, 0, sizeof(timelib_rel_time));
226 0 : t->relative.y = interval->y * bias;
227 0 : t->relative.m = interval->m * bias;
228 0 : t->relative.d = interval->d * bias;
229 0 : t->relative.h = interval->h * bias;
230 0 : t->relative.i = interval->i * bias;
231 0 : t->relative.s = interval->s * bias;
232 0 : t->relative.us = interval->us * bias;
233 : }
234 0 : t->have_relative = 1;
235 0 : t->sse_uptodate = 0;
236 :
237 0 : timelib_update_ts(t, NULL);
238 :
239 0 : timelib_update_from_sse(t);
240 0 : t->have_relative = 0;
241 :
242 0 : return t;
243 : }
244 :
245 368 : timelib_time *timelib_sub(timelib_time *old_time, timelib_rel_time *interval)
246 : {
247 368 : int bias = 1;
248 368 : timelib_time *t = timelib_time_clone(old_time);
249 :
250 368 : if (interval->invert) {
251 16 : bias = -1;
252 : }
253 :
254 368 : memset(&t->relative, 0, sizeof(timelib_rel_time));
255 368 : t->relative.y = 0 - (interval->y * bias);
256 368 : t->relative.m = 0 - (interval->m * bias);
257 368 : t->relative.d = 0 - (interval->d * bias);
258 368 : t->relative.h = 0 - (interval->h * bias);
259 368 : t->relative.i = 0 - (interval->i * bias);
260 368 : t->relative.s = 0 - (interval->s * bias);
261 368 : t->relative.us = 0 - (interval->us * bias);
262 368 : t->have_relative = 1;
263 368 : t->sse_uptodate = 0;
264 :
265 368 : timelib_update_ts(t, NULL);
266 :
267 368 : timelib_update_from_sse(t);
268 :
269 368 : t->have_relative = 0;
270 :
271 368 : return t;
272 : }
273 :
274 0 : static void do_range_limit(timelib_sll start, timelib_sll end, timelib_sll adj, timelib_sll *a, timelib_sll *b)
275 : {
276 0 : if (*a < start) {
277 0 : *b -= (start - *a - 1) / adj + 1;
278 0 : *a += adj * ((start - *a - 1) / adj + 1);
279 : }
280 0 : if (*a >= end) {
281 0 : *b += *a / adj;
282 0 : *a -= adj * (*a / adj);
283 : }
284 0 : }
285 :
286 :
287 0 : timelib_time *timelib_add_wall(timelib_time *old_time, timelib_rel_time *interval)
288 : {
289 0 : int bias = 1;
290 0 : timelib_time *t = timelib_time_clone(old_time);
291 :
292 0 : t->have_relative = 1;
293 0 : t->sse_uptodate = 0;
294 :
295 0 : if (interval->have_weekday_relative || interval->have_special_relative) {
296 0 : memcpy(&t->relative, interval, sizeof(timelib_rel_time));
297 :
298 0 : timelib_update_ts(t, NULL);
299 :
300 0 : timelib_update_from_sse(t);
301 : } else {
302 0 : if (interval->invert) {
303 0 : bias = -1;
304 : }
305 0 : memset(&t->relative, 0, sizeof(timelib_rel_time));
306 0 : t->relative.y = interval->y * bias;
307 0 : t->relative.m = interval->m * bias;
308 0 : t->relative.d = interval->d * bias;
309 :
310 0 : if (t->relative.y || t->relative.m || t->relative.d) {
311 0 : timelib_update_ts(t, NULL);
312 : }
313 :
314 0 : if (interval->us == 0) {
315 0 : t->sse += bias * timelib_hms_to_seconds(interval->h, interval->i, interval->s);
316 0 : timelib_update_from_sse(t);
317 : } else {
318 0 : timelib_rel_time *temp_interval = timelib_rel_time_clone(interval);
319 :
320 0 : do_range_limit(0, 1000000, 1000000, &temp_interval->us, &temp_interval->s);
321 0 : t->sse += bias * timelib_hms_to_seconds(temp_interval->h, temp_interval->i, temp_interval->s);
322 0 : timelib_update_from_sse(t);
323 0 : t->us += temp_interval->us * bias;
324 :
325 0 : timelib_do_normalize(t);
326 0 : timelib_update_ts(t, NULL);
327 :
328 0 : timelib_rel_time_dtor(temp_interval);
329 : }
330 0 : timelib_do_normalize(t);
331 : }
332 :
333 0 : if (t->zone_type == TIMELIB_ZONETYPE_ID) {
334 0 : timelib_set_timezone(t, t->tz_info);
335 : }
336 0 : t->have_relative = 0;
337 :
338 0 : return t;
339 : }
340 :
341 0 : timelib_time *timelib_sub_wall(timelib_time *old_time, timelib_rel_time *interval)
342 : {
343 0 : int bias = 1;
344 0 : timelib_time *t = timelib_time_clone(old_time);
345 :
346 0 : t->have_relative = 1;
347 0 : t->sse_uptodate = 0;
348 :
349 0 : if (interval->have_weekday_relative || interval->have_special_relative) {
350 0 : memcpy(&t->relative, interval, sizeof(timelib_rel_time));
351 :
352 0 : timelib_update_ts(t, NULL);
353 0 : timelib_update_from_sse(t);
354 : } else {
355 0 : if (interval->invert) {
356 0 : bias = -1;
357 : }
358 0 : memset(&t->relative, 0, sizeof(timelib_rel_time));
359 0 : t->relative.y = 0 - (interval->y * bias);
360 0 : t->relative.m = 0 - (interval->m * bias);
361 0 : t->relative.d = 0 - (interval->d * bias);
362 :
363 0 : if (t->relative.y || t->relative.m || t->relative.d) {
364 0 : timelib_update_ts(t, NULL);
365 : }
366 :
367 0 : if (interval->us == 0) {
368 0 : t->sse -= bias * timelib_hms_to_seconds(interval->h, interval->i, interval->s);
369 0 : timelib_update_from_sse(t);
370 : } else {
371 0 : timelib_rel_time *temp_interval = timelib_rel_time_clone(interval);
372 :
373 0 : do_range_limit(0, 1000000, 1000000, &temp_interval->us, &temp_interval->s);
374 0 : t->sse -= bias * timelib_hms_to_seconds(temp_interval->h, temp_interval->i, temp_interval->s);
375 0 : timelib_update_from_sse(t);
376 0 : t->us -= temp_interval->us * bias;
377 :
378 0 : timelib_do_normalize(t);
379 0 : timelib_update_ts(t, NULL);
380 :
381 0 : timelib_rel_time_dtor(temp_interval);
382 : }
383 0 : timelib_do_normalize(t);
384 : }
385 :
386 0 : if (t->zone_type == TIMELIB_ZONETYPE_ID) {
387 0 : timelib_set_timezone(t, t->tz_info);
388 : }
389 0 : t->have_relative = 0;
390 :
391 0 : return t;
392 : }
|