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