Line data Source code
1 : /*
2 : * Present - Date/Time Library
3 : *
4 : * Implementation of the TimeDelta methods
5 : *
6 : * Licensed under the MIT License.
7 : * For details, see LICENSE.
8 : */
9 :
10 : #include <assert.h>
11 : #include <stddef.h>
12 :
13 : #include "present.h"
14 :
15 : #include "utils/constants.h"
16 : #include "utils/impl-utils.h"
17 : #include "utils/time-utils.h"
18 :
19 : /**
20 : * This macro alters delta_nanoseconds if necessary to ensure that its
21 : * absolute value is less than NANOSECONDS_IN_SECOND, then ensures that
22 : * the signs of delta_seconds and delta_nanoseconds match (if either is
23 : * nonzero).
24 : */
25 : #define CHECK_DATA(data) \
26 : do { \
27 : if (data.delta_nanoseconds > NANOSECONDS_IN_SECOND) { \
28 : data.delta_seconds += \
29 : data.delta_nanoseconds / NANOSECONDS_IN_SECOND; \
30 : data.delta_nanoseconds %= NANOSECONDS_IN_SECOND; \
31 : } else if (data.delta_nanoseconds < -NANOSECONDS_IN_SECOND) { \
32 : data.delta_seconds -= \
33 : (-data.delta_nanoseconds) / NANOSECONDS_IN_SECOND; \
34 : data.delta_nanoseconds = -( \
35 : (-data.delta_nanoseconds) % NANOSECONDS_IN_SECOND); \
36 : } \
37 : if (data.delta_seconds > 0 && data.delta_nanoseconds < 0) { \
38 : data.delta_seconds -= 1; \
39 : data.delta_nanoseconds += NANOSECONDS_IN_SECOND; \
40 : } \
41 : if (data.delta_seconds < 0 && data.delta_nanoseconds > 0) { \
42 : data.delta_seconds += 1; \
43 : data.delta_nanoseconds -= NANOSECONDS_IN_SECOND; \
44 : } \
45 : if (data.delta_seconds > 0) assert(data.delta_nanoseconds >= 0); \
46 : if (data.delta_seconds < 0) assert(data.delta_nanoseconds <= 0); \
47 : } while (0)
48 :
49 : /** Initialize a new TimeDelta based on seconds and nanoseconds. */
50 : static void
51 2701 : init_time_delta(
52 : struct TimeDelta * const result,
53 : int_delta seconds,
54 : int_delta nanoseconds)
55 : {
56 2701 : assert(result != NULL);
57 2701 : CLEAR(result);
58 :
59 2701 : result->data_.delta_seconds = seconds;
60 2701 : result->data_.delta_nanoseconds = nanoseconds;
61 2701 : if (nanoseconds) {
62 21 : CHECK_DATA(result->data_);
63 : }
64 2701 : }
65 :
66 :
67 : struct TimeDelta
68 1266 : TimeDelta_from_nanoseconds(int_delta nanoseconds)
69 : {
70 : struct TimeDelta result;
71 1266 : init_time_delta(&result, 0, nanoseconds);
72 1266 : return result;
73 : }
74 :
75 : void
76 5 : TimeDelta_ptr_from_nanoseconds(
77 : struct TimeDelta * const result,
78 : int_delta nanoseconds)
79 : {
80 5 : init_time_delta(result, 0, nanoseconds);
81 5 : }
82 :
83 : struct TimeDelta
84 3 : TimeDelta_from_microseconds(int_delta microseconds)
85 : {
86 : struct TimeDelta result;
87 3 : init_time_delta(&result, 0, microseconds * NANOSECONDS_IN_MICROSECOND);
88 3 : return result;
89 : }
90 :
91 : void
92 3 : TimeDelta_ptr_from_microseconds(
93 : struct TimeDelta * const result,
94 : int_delta microseconds)
95 : {
96 3 : init_time_delta(result, 0, microseconds * NANOSECONDS_IN_MICROSECOND);
97 3 : }
98 :
99 : struct TimeDelta
100 3 : TimeDelta_from_milliseconds(int_delta milliseconds)
101 : {
102 : struct TimeDelta result;
103 3 : init_time_delta(&result, 0, milliseconds * NANOSECONDS_IN_MILLISECOND);
104 3 : return result;
105 : }
106 :
107 : void
108 3 : TimeDelta_ptr_from_milliseconds(
109 : struct TimeDelta * const result,
110 : int_delta milliseconds)
111 : {
112 3 : init_time_delta(result, 0, milliseconds * NANOSECONDS_IN_MILLISECOND);
113 3 : }
114 :
115 : struct TimeDelta
116 1267 : TimeDelta_from_seconds(int_delta seconds)
117 : {
118 : struct TimeDelta result;
119 1267 : init_time_delta(&result, seconds, 0);
120 1267 : return result;
121 : }
122 :
123 : void
124 15 : TimeDelta_ptr_from_seconds(struct TimeDelta * const result, int_delta seconds)
125 : {
126 15 : init_time_delta(result, seconds, 0);
127 15 : }
128 :
129 : struct TimeDelta
130 4 : TimeDelta_from_minutes(int_delta minutes)
131 : {
132 : struct TimeDelta result;
133 4 : init_time_delta(&result, minutes * SECONDS_IN_MINUTE, 0);
134 4 : return result;
135 : }
136 :
137 : void
138 12 : TimeDelta_ptr_from_minutes(struct TimeDelta * const result, int_delta minutes)
139 : {
140 12 : init_time_delta(result, minutes * SECONDS_IN_MINUTE, 0);
141 12 : }
142 :
143 : struct TimeDelta
144 4 : TimeDelta_from_hours(int_delta hours)
145 : {
146 : struct TimeDelta result;
147 4 : init_time_delta(&result, hours * SECONDS_IN_HOUR, 0);
148 4 : return result;
149 : }
150 :
151 : void
152 43 : TimeDelta_ptr_from_hours(struct TimeDelta * const result, int_delta hours)
153 : {
154 43 : init_time_delta(result, hours * SECONDS_IN_HOUR, 0);
155 43 : }
156 :
157 : struct TimeDelta
158 46 : TimeDelta_from_days(int_delta days)
159 : {
160 : struct TimeDelta result;
161 46 : init_time_delta(&result, days * SECONDS_IN_DAY, 0);
162 46 : return result;
163 : }
164 :
165 : void
166 13 : TimeDelta_ptr_from_days(struct TimeDelta * const result, int_delta days)
167 : {
168 13 : init_time_delta(result, days * SECONDS_IN_DAY, 0);
169 13 : }
170 :
171 : struct TimeDelta
172 4 : TimeDelta_from_weeks(int_delta weeks)
173 : {
174 : struct TimeDelta result;
175 4 : init_time_delta(&result, weeks * SECONDS_IN_WEEK, 0);
176 4 : return result;
177 : }
178 :
179 : void
180 4 : TimeDelta_ptr_from_weeks(struct TimeDelta * const result, int_delta weeks)
181 : {
182 4 : init_time_delta(result, weeks * SECONDS_IN_WEEK, 0);
183 4 : }
184 :
185 : struct TimeDelta
186 0 : TimeDelta_zero(void)
187 : {
188 : struct TimeDelta result;
189 0 : init_time_delta(&result, 0, 0);
190 0 : return result;
191 : }
192 :
193 : void
194 6 : TimeDelta_ptr_zero(struct TimeDelta * const result)
195 : {
196 6 : init_time_delta(result, 0, 0);
197 6 : }
198 :
199 : int_delta
200 32 : TimeDelta_nanoseconds(const struct TimeDelta * const self)
201 : {
202 32 : assert(self != NULL);
203 :
204 64 : return (self->data_.delta_seconds * NANOSECONDS_IN_SECOND +
205 32 : self->data_.delta_nanoseconds);
206 : }
207 :
208 : int_delta
209 38 : TimeDelta_microseconds(const struct TimeDelta * const self)
210 : {
211 38 : assert(self != NULL);
212 :
213 76 : return (self->data_.delta_seconds * MICROSECONDS_IN_SECOND +
214 38 : self->data_.delta_nanoseconds / NANOSECONDS_IN_MICROSECOND);
215 : }
216 :
217 : double
218 6 : TimeDelta_microseconds_decimal(const struct TimeDelta * const self)
219 : {
220 6 : assert(self != NULL);
221 :
222 12 : return (self->data_.delta_seconds * MICROSECONDS_IN_SECOND +
223 6 : (double)(self->data_.delta_nanoseconds) /
224 : (double)NANOSECONDS_IN_MICROSECOND);
225 : }
226 :
227 : int_delta
228 44 : TimeDelta_milliseconds(const struct TimeDelta * const self)
229 : {
230 44 : assert(self != NULL);
231 :
232 88 : return (self->data_.delta_seconds * MILLISECONDS_IN_SECOND +
233 44 : self->data_.delta_nanoseconds / NANOSECONDS_IN_MILLISECOND);
234 : }
235 :
236 : double
237 6 : TimeDelta_milliseconds_decimal(const struct TimeDelta * const self)
238 : {
239 6 : assert(self != NULL);
240 :
241 12 : return (self->data_.delta_seconds * MILLISECONDS_IN_SECOND +
242 6 : (double)(self->data_.delta_nanoseconds) /
243 : (double)NANOSECONDS_IN_MILLISECOND);
244 : }
245 :
246 : int_delta
247 376 : TimeDelta_seconds(const struct TimeDelta * const self)
248 : {
249 376 : assert(self != NULL);
250 :
251 376 : return self->data_.delta_seconds;
252 : }
253 :
254 : double
255 54 : TimeDelta_seconds_decimal(const struct TimeDelta * const self)
256 : {
257 54 : assert(self != NULL);
258 :
259 108 : return (self->data_.delta_seconds +
260 54 : (double)(self->data_.delta_nanoseconds) /
261 : (double)NANOSECONDS_IN_SECOND);
262 : }
263 :
264 : int_delta
265 64 : TimeDelta_minutes(const struct TimeDelta * const self)
266 : {
267 64 : return TimeDelta_seconds(self) / SECONDS_IN_MINUTE;
268 : }
269 :
270 : double
271 10 : TimeDelta_minutes_decimal(const struct TimeDelta * const self)
272 : {
273 10 : return TimeDelta_seconds_decimal(self) / (double)SECONDS_IN_MINUTE;
274 : }
275 :
276 : int_delta
277 78 : TimeDelta_hours(const struct TimeDelta * const self)
278 : {
279 78 : return TimeDelta_seconds(self) / SECONDS_IN_HOUR;
280 : }
281 :
282 : double
283 14 : TimeDelta_hours_decimal(const struct TimeDelta * const self)
284 : {
285 14 : return TimeDelta_seconds_decimal(self) / (double)SECONDS_IN_HOUR;
286 : }
287 :
288 : int_delta
289 82 : TimeDelta_days(const struct TimeDelta * const self)
290 : {
291 82 : return TimeDelta_seconds(self) / SECONDS_IN_DAY;
292 : }
293 :
294 : double
295 4 : TimeDelta_days_decimal(const struct TimeDelta * const self)
296 : {
297 4 : return TimeDelta_seconds_decimal(self) / (double)SECONDS_IN_DAY;
298 : }
299 :
300 : int_delta
301 98 : TimeDelta_weeks(const struct TimeDelta * const self)
302 : {
303 98 : return TimeDelta_seconds(self) / SECONDS_IN_WEEK;
304 : }
305 :
306 : double
307 16 : TimeDelta_weeks_decimal(const struct TimeDelta * const self)
308 : {
309 16 : return TimeDelta_seconds_decimal(self) / (double)SECONDS_IN_WEEK;
310 : }
311 :
312 : struct DayDelta
313 4 : TimeDelta_to_DayDelta_truncated(const struct TimeDelta * const self)
314 : {
315 : struct DayDelta day_delta;
316 :
317 4 : assert(self != NULL);
318 :
319 4 : if (self->data_.delta_seconds >= 0) {
320 2 : return DayDelta_from_days(self->data_.delta_seconds / SECONDS_IN_DAY);
321 : } else {
322 : /* Truncation in integer division with negative operands is
323 : implementation-dependent before C99, so we'll just use positives */
324 2 : day_delta = DayDelta_from_days(
325 2 : (-self->data_.delta_seconds) / SECONDS_IN_DAY);
326 2 : DayDelta_negate(&day_delta);
327 2 : return day_delta;
328 : }
329 : }
330 :
331 : struct DayDelta
332 0 : TimeDelta_to_DayDelta_rounded(const struct TimeDelta * const self)
333 : {
334 0 : assert(self != NULL);
335 :
336 0 : return DayDelta_from_days((int_delta)present_round(
337 0 : ((double)self->data_.delta_seconds) /
338 : (double)SECONDS_IN_DAY));
339 : }
340 :
341 : struct DayDelta
342 0 : TimeDelta_to_DayDelta_abs_ceil(const struct TimeDelta * const self)
343 : {
344 : struct DayDelta day_delta;
345 :
346 0 : assert(self != NULL);
347 :
348 0 : if (self->data_.delta_seconds % SECONDS_IN_DAY == 0) {
349 0 : return DayDelta_from_days(self->data_.delta_seconds / SECONDS_IN_DAY);
350 0 : } else if (self->data_.delta_seconds >= 0) {
351 0 : return DayDelta_from_days(self->data_.delta_seconds / SECONDS_IN_DAY +
352 : 1);
353 : } else {
354 : /* Truncation in integer division with negative operands is
355 : implementation-dependent before C99, so we'll just use positives */
356 0 : day_delta = DayDelta_from_days(
357 0 : (-self->data_.delta_seconds) / SECONDS_IN_DAY + 1);
358 0 : DayDelta_negate(&day_delta);
359 0 : return day_delta;
360 : }
361 : }
362 :
363 : present_bool
364 2 : TimeDelta_is_negative(const struct TimeDelta * const self)
365 : {
366 2 : assert(self != NULL);
367 :
368 2 : return self->data_.delta_seconds < 0 || self->data_.delta_nanoseconds < 0;
369 : }
370 :
371 : void
372 2 : TimeDelta_negate(struct TimeDelta * const self)
373 : {
374 2 : assert(self != NULL);
375 :
376 2 : self->data_.delta_seconds = -self->data_.delta_seconds;
377 2 : self->data_.delta_nanoseconds = -self->data_.delta_nanoseconds;
378 2 : }
379 :
380 : void
381 0 : TimeDelta_multiply_by(struct TimeDelta * const self, const long scale_factor)
382 : {
383 0 : assert(self != NULL);
384 :
385 0 : self->data_.delta_seconds *= scale_factor;
386 0 : self->data_.delta_nanoseconds *= scale_factor;
387 :
388 0 : CHECK_DATA(self->data_);
389 0 : }
390 :
391 : void
392 0 : TimeDelta_multiply_by_decimal(
393 : struct TimeDelta * const self,
394 : const double scale_factor)
395 : {
396 : double seconds;
397 :
398 0 : assert(self != NULL);
399 :
400 0 : seconds = (double)self->data_.delta_seconds * scale_factor;
401 0 : self->data_.delta_seconds = (int_delta)seconds;
402 0 : self->data_.delta_nanoseconds *= scale_factor;
403 :
404 : /* When scaling the seconds, we may have a fractional part that needs to
405 : be stored in the nanoseconds */
406 0 : self->data_.delta_nanoseconds +=
407 0 : (seconds - (double)self->data_.delta_seconds) *
408 : (double)NANOSECONDS_IN_SECOND;
409 :
410 0 : CHECK_DATA(self->data_);
411 0 : }
412 :
413 : void
414 0 : TimeDelta_divide_by(struct TimeDelta * const self, const long scale_factor)
415 : {
416 : int_delta orig_seconds;
417 :
418 0 : assert(self != NULL);
419 0 : assert(scale_factor != 0);
420 :
421 : /* Start by scaling just the seconds portion */
422 0 : orig_seconds = self->data_.delta_seconds;
423 0 : self->data_.delta_seconds /= scale_factor;
424 :
425 : /* When scaling down, there may be fractional seconds that could be
426 : represented as nanoseconds. Add this to the nanoseconds (which we
427 : haven't scaled yet) */
428 0 : self->data_.delta_nanoseconds += (orig_seconds % scale_factor) *
429 : NANOSECONDS_IN_SECOND;
430 :
431 : /* Now we can scale the nanoseconds */
432 0 : self->data_.delta_nanoseconds /= scale_factor;
433 :
434 0 : CHECK_DATA(self->data_);
435 0 : }
436 :
437 : void
438 0 : TimeDelta_divide_by_decimal(
439 : struct TimeDelta * const self,
440 : const double scale_factor)
441 : {
442 0 : assert(self != NULL);
443 0 : assert(scale_factor != 0.0);
444 :
445 : /* Simplify our lives by reusing the float multiplication implementation */
446 0 : TimeDelta_multiply_by_decimal(self, 1.0/scale_factor);
447 0 : }
448 :
449 : void
450 1275 : TimeDelta_add(
451 : struct TimeDelta * const self,
452 : const struct TimeDelta * const other)
453 : {
454 1275 : assert(self != NULL);
455 1275 : assert(other != NULL);
456 :
457 1275 : self->data_.delta_seconds += other->data_.delta_seconds;
458 1275 : self->data_.delta_nanoseconds += other->data_.delta_nanoseconds;
459 :
460 1275 : CHECK_DATA(self->data_);
461 1275 : }
462 :
463 : void
464 0 : TimeDelta_add_DayDelta(
465 : struct TimeDelta * const self,
466 : const struct DayDelta * const other)
467 : {
468 0 : assert(self != NULL);
469 0 : assert(other != NULL);
470 :
471 0 : self->data_.delta_seconds += other->data_.delta_days * SECONDS_IN_DAY;
472 :
473 0 : CHECK_DATA(self->data_);
474 0 : }
475 :
476 : void
477 0 : TimeDelta_subtract(
478 : struct TimeDelta * const self,
479 : const struct TimeDelta * const other)
480 : {
481 0 : assert(self != NULL);
482 0 : assert(other != NULL);
483 :
484 0 : self->data_.delta_seconds -= other->data_.delta_seconds;
485 0 : self->data_.delta_nanoseconds -= other->data_.delta_nanoseconds;
486 :
487 0 : CHECK_DATA(self->data_);
488 0 : }
489 :
490 : void
491 0 : TimeDelta_subtract_DayDelta(
492 : struct TimeDelta * const self,
493 : const struct DayDelta * const other)
494 : {
495 0 : assert(self != NULL);
496 0 : assert(other != NULL);
497 :
498 0 : self->data_.delta_seconds -= other->data_.delta_days * SECONDS_IN_DAY;
499 :
500 0 : CHECK_DATA(self->data_);
501 0 : }
502 :
503 : short
504 78 : TimeDelta_compare(
505 : const struct TimeDelta * const lhs,
506 : const struct TimeDelta * const rhs)
507 : {
508 78 : assert(lhs != NULL);
509 78 : assert(rhs != NULL);
510 :
511 250 : return
512 250 : STRUCT_COMPARE(delta_seconds,
513 : STRUCT_COMPARE(delta_nanoseconds, 0));
514 : }
515 :
516 : short
517 37 : TimeDelta_compare_to_DayDelta(
518 : const struct TimeDelta * const lhs,
519 : const struct DayDelta * const rhs)
520 : {
521 37 : assert(lhs != NULL);
522 37 : assert(rhs != NULL);
523 :
524 37 : struct TimeDelta rhs_as_time_delta = DayDelta_to_TimeDelta(rhs);
525 37 : return TimeDelta_compare(lhs, &rhs_as_time_delta);
526 : }
527 :
528 41 : STRUCT_COMPARISON_OPERATORS(TimeDelta)
529 :
530 0 : STRUCT_COMPARISON_OPERATORS_WITH_OTHER_STRUCT(TimeDelta, DayDelta)
531 :
|