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
timelib_diff(timelib_time * one,timelib_time * two)29 timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two)
30 {
31 timelib_rel_time *rt;
32 timelib_time *swp;
33 timelib_sll dst_corr = 0, dst_h_corr = 0, dst_m_corr = 0;
34 timelib_time_offset *trans = NULL;
35
36
37 rt = timelib_rel_time_ctor();
38 rt->invert = 0;
39 if (
40 (one->sse > two->sse) ||
41 (one->sse == two->sse && one->us > two->us)
42 ) {
43 swp = two;
44 two = one;
45 one = swp;
46 rt->invert = 1;
47 }
48
49 /* Calculate correction for UTC offset changes between first and second SSE */
50 dst_corr = two->z - one->z;
51 dst_h_corr = dst_corr / 3600;
52 dst_m_corr = (dst_corr % 3600) / 60;
53
54 rt->y = two->y - one->y;
55 rt->m = two->m - one->m;
56 rt->d = two->d - one->d;
57 rt->h = two->h - one->h;
58 rt->i = two->i - one->i;
59 rt->s = two->s - one->s;
60 rt->us = two->us - one->us;
61
62 rt->days = timelib_diff_days(one, two);
63
64 /* Fall Back: Cater for transition period, where rt->invert is 0, but there are negative numbers */
65 if (one->dst == 1 && two->dst == 0) {
66 /* First for two "Type 3" times */
67 if (one->zone_type == 3 && two->zone_type == 3) {
68 trans = timelib_get_time_zone_info(two->sse, two->tz_info);
69 if (trans) {
70 if (one->sse >= trans->transition_time + dst_corr && one->sse < trans->transition_time) {
71 timelib_sll flipped = SECS_PER_HOUR + (rt->i * 60) + (rt->s);
72 rt->h = flipped / SECS_PER_HOUR;
73 rt->i = (flipped - rt->h * SECS_PER_HOUR) / 60;
74 rt->s = flipped % 60;
75 }
76 timelib_time_offset_dtor(trans);
77 trans = NULL;
78 }
79 } else if (rt->h == 0 && (rt->i < 0 || rt->s < 0)) {
80 /* Then for all the others */
81 timelib_sll flipped = SECS_PER_HOUR + (rt->i * 60) + (rt->s);
82 rt->h = flipped / SECS_PER_HOUR;
83 rt->i = (flipped - rt->h * SECS_PER_HOUR) / 60;
84 rt->s = flipped % 60;
85 dst_corr += SECS_PER_HOUR;
86 dst_h_corr++;
87 }
88 }
89
90 timelib_do_rel_normalize(rt->invert ? one : two, rt);
91
92 /* Do corrections for "Type 3" times */
93 if (one->zone_type == 3 && two->zone_type == 3 && strcmp(one->tz_info->name, two->tz_info->name) == 0) {
94 if (one->dst == 1 && two->dst == 0) { /* Fall Back */
95 if (two->tz_info) {
96 trans = timelib_get_time_zone_info(two->sse, two->tz_info);
97
98 if (
99 trans &&
100 two->sse >= trans->transition_time &&
101 ((two->sse - one->sse + dst_corr) % SECS_PER_DAY) > (two->sse - trans->transition_time)
102 ) {
103 rt->h -= dst_h_corr;
104 rt->i -= dst_m_corr;
105 }
106 }
107 } else if (one->dst == 0 && two->dst == 1) { /* Spring Forward */
108 if (two->tz_info) {
109 trans = timelib_get_time_zone_info(two->sse, two->tz_info);
110
111 if (
112 trans &&
113 !((one->sse + SECS_PER_DAY > trans->transition_time) && (one->sse + SECS_PER_DAY <= (trans->transition_time + dst_corr))) &&
114 two->sse >= trans->transition_time &&
115 ((two->sse - one->sse + dst_corr) % SECS_PER_DAY) > (two->sse - trans->transition_time)
116 ) {
117 rt->h -= dst_h_corr;
118 rt->i -= dst_m_corr;
119 }
120 }
121 } else if (two->sse - one->sse >= SECS_PER_DAY) {
122 /* Check whether we're in the period to the next transition time */
123 trans = timelib_get_time_zone_info(two->sse - two->z, two->tz_info);
124 dst_corr = one->z - trans->offset;
125
126 if (two->sse >= trans->transition_time - dst_corr && two->sse < trans->transition_time) {
127 rt->d--;
128 rt->h = 24;
129 }
130 }
131 } else {
132 /* Then for all the others */
133 if (one->zone_type == 3 && two->zone_type == 3) {
134 rt->h -= dst_h_corr;
135 } else {
136 rt->h -= dst_h_corr + (two->dst - one->dst);
137 }
138 rt->i -= dst_m_corr;
139
140 timelib_do_rel_normalize(rt->invert ? one : two, rt);
141 }
142
143 if (trans) {
144 timelib_time_offset_dtor(trans);
145 }
146
147 return rt;
148 }
149
150
timelib_diff_days(timelib_time * one,timelib_time * two)151 int timelib_diff_days(timelib_time *one, timelib_time *two)
152 {
153 int days = 0;
154
155 if (timelib_same_timezone(one, two)) {
156 timelib_time *earliest, *latest;
157 double earliest_time, latest_time;
158
159 if (timelib_time_compare(one, two) < 0) {
160 earliest = one;
161 latest = two;
162 } else {
163 earliest = two;
164 latest = one;
165 }
166 timelib_hmsf_to_decimal_hour(earliest->h, earliest->i, earliest->s, earliest->us, &earliest_time);
167 timelib_hmsf_to_decimal_hour(latest->h, latest->i, latest->s, latest->us, &latest_time);
168
169 days = llabs(timelib_epoch_days_from_time(one) - timelib_epoch_days_from_time(two));
170 if (latest_time < earliest_time && days > 0) {
171 days--;
172 }
173 } else {
174 days = fabs(floor(one->sse - two->sse) / 86400);
175 }
176
177 return days;
178 }
179
180
timelib_add(timelib_time * old_time,timelib_rel_time * interval)181 timelib_time *timelib_add(timelib_time *old_time, timelib_rel_time *interval)
182 {
183 int bias = 1;
184 timelib_time *t = timelib_time_clone(old_time);
185
186 if (interval->have_weekday_relative || interval->have_special_relative) {
187 memcpy(&t->relative, interval, sizeof(timelib_rel_time));
188 } else {
189 if (interval->invert) {
190 bias = -1;
191 }
192 memset(&t->relative, 0, sizeof(timelib_rel_time));
193 t->relative.y = interval->y * bias;
194 t->relative.m = interval->m * bias;
195 t->relative.d = interval->d * bias;
196 t->relative.h = interval->h * bias;
197 t->relative.i = interval->i * bias;
198 t->relative.s = interval->s * bias;
199 t->relative.us = interval->us * bias;
200 }
201 t->have_relative = 1;
202 t->sse_uptodate = 0;
203
204 timelib_update_ts(t, NULL);
205
206 timelib_update_from_sse(t);
207 t->have_relative = 0;
208
209 return t;
210 }
211
timelib_sub(timelib_time * old_time,timelib_rel_time * interval)212 timelib_time *timelib_sub(timelib_time *old_time, timelib_rel_time *interval)
213 {
214 int bias = 1;
215 timelib_time *t = timelib_time_clone(old_time);
216
217 if (interval->invert) {
218 bias = -1;
219 }
220
221 memset(&t->relative, 0, sizeof(timelib_rel_time));
222 t->relative.y = 0 - (interval->y * bias);
223 t->relative.m = 0 - (interval->m * bias);
224 t->relative.d = 0 - (interval->d * bias);
225 t->relative.h = 0 - (interval->h * bias);
226 t->relative.i = 0 - (interval->i * bias);
227 t->relative.s = 0 - (interval->s * bias);
228 t->relative.us = 0 - (interval->us * bias);
229 t->have_relative = 1;
230 t->sse_uptodate = 0;
231
232 timelib_update_ts(t, NULL);
233
234 timelib_update_from_sse(t);
235
236 t->have_relative = 0;
237
238 return t;
239 }
240
do_range_limit(timelib_sll start,timelib_sll end,timelib_sll adj,timelib_sll * a,timelib_sll * b)241 static void do_range_limit(timelib_sll start, timelib_sll end, timelib_sll adj, timelib_sll *a, timelib_sll *b)
242 {
243 if (*a < start) {
244 *b -= (start - *a - 1) / adj + 1;
245 *a += adj * ((start - *a - 1) / adj + 1);
246 }
247 if (*a >= end) {
248 *b += *a / adj;
249 *a -= adj * (*a / adj);
250 }
251 }
252
253
timelib_add_wall(timelib_time * old_time,timelib_rel_time * interval)254 timelib_time *timelib_add_wall(timelib_time *old_time, timelib_rel_time *interval)
255 {
256 int bias = 1;
257 timelib_time *t = timelib_time_clone(old_time);
258
259 t->have_relative = 1;
260 t->sse_uptodate = 0;
261
262 if (interval->have_weekday_relative || interval->have_special_relative) {
263 memcpy(&t->relative, interval, sizeof(timelib_rel_time));
264
265 timelib_update_ts(t, NULL);
266
267 timelib_update_from_sse(t);
268 } else {
269 if (interval->invert) {
270 bias = -1;
271 }
272 memset(&t->relative, 0, sizeof(timelib_rel_time));
273 t->relative.y = interval->y * bias;
274 t->relative.m = interval->m * bias;
275 t->relative.d = interval->d * bias;
276
277 if (t->relative.y || t->relative.m || t->relative.d) {
278 timelib_update_ts(t, NULL);
279 }
280
281 do_range_limit(0, 1000000, 1000000, &interval->us, &interval->s);
282 t->sse += bias * timelib_hms_to_seconds(interval->h, interval->i, interval->s);
283 timelib_update_from_sse(t);
284 t->us += interval->us * bias;
285 if (bias == -1 && interval->us > 0) {
286 t->sse--;
287 }
288 timelib_do_normalize(t);
289 }
290
291 if (t->zone_type == TIMELIB_ZONETYPE_ID) {
292 timelib_set_timezone(t, t->tz_info);
293 }
294 t->have_relative = 0;
295
296 return t;
297 }
298
timelib_sub_wall(timelib_time * old_time,timelib_rel_time * interval)299 timelib_time *timelib_sub_wall(timelib_time *old_time, timelib_rel_time *interval)
300 {
301 int bias = 1;
302 timelib_time *t = timelib_time_clone(old_time);
303
304 t->have_relative = 1;
305 t->sse_uptodate = 0;
306
307 if (interval->have_weekday_relative || interval->have_special_relative) {
308 memcpy(&t->relative, interval, sizeof(timelib_rel_time));
309
310 timelib_update_ts(t, NULL);
311 } else {
312 if (interval->invert) {
313 bias = -1;
314 }
315 memset(&t->relative, 0, sizeof(timelib_rel_time));
316 t->relative.y = 0 - (interval->y * bias);
317 t->relative.m = 0 - (interval->m * bias);
318 t->relative.d = 0 - (interval->d * bias);
319
320 if (t->relative.y || t->relative.m || t->relative.d) {
321 timelib_update_ts(t, NULL);
322 }
323
324 do_range_limit(0, 1000000, 1000000, &interval->us, &interval->s);
325 t->sse -= bias * timelib_hms_to_seconds(interval->h, interval->i, interval->s);
326 timelib_update_from_sse(t);
327 t->us -= interval->us * bias;
328 if (bias == -1 && interval->us > 0) {
329 t->sse++;
330 }
331 timelib_do_normalize(t);
332 }
333
334 timelib_update_from_sse(t);
335 if (t->zone_type == TIMELIB_ZONETYPE_ID) {
336 timelib_set_timezone(t, t->tz_info);
337 }
338 t->have_relative = 0;
339
340 return t;
341 }
342