Line data Source code
1 : /*
2 : * Present - Date/Time Library
3 : *
4 : * Tests for the Timestamp C++ class and C-compatible methods
5 : *
6 : * Licensed under the MIT License.
7 : * For details, see LICENSE.
8 : */
9 :
10 : #include "catch.hpp"
11 : #include "test-utils.hpp"
12 :
13 : #include "present.h"
14 :
15 : #include "utils/constants.h"
16 : #include "utils/time-utils.h"
17 :
18 : // If we're using GCC, disable the annoying warning about missing initializers
19 : #ifdef __GNUC__
20 : # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
21 : #endif
22 :
23 : static const struct Timestamp EMPTY_TIMESTAMP = {};
24 :
25 : /** Absolute value for time_t's */
26 : #define ABS(a) (((a) < (time_t) 0) ? -(a) : (a))
27 :
28 : /**
29 : * Shortcut macro to compare timestamp_seconds and additional_nanoseconds all
30 : * in one.
31 : * Expects that the Timestamp is "t".
32 : */
33 : #define IS(test_timestamp_seconds, test_additional_nanoseconds) \
34 : REQUIRE_FALSE(t.has_error); \
35 : CHECK(t.data_.timestamp_seconds == test_timestamp_seconds); \
36 : CHECK(t.data_.additional_nanoseconds == test_additional_nanoseconds);
37 :
38 : /**
39 : * Shortcut macro to check if there's a certain error.
40 : * Expects that the Timestamp is "t".
41 : */
42 : #define IS_ERROR(eRR_tYPE) \
43 : CHECK(t.has_error); \
44 : CHECK(t.errors.eRR_tYPE);
45 :
46 : /**
47 : * Check that creating a Timestamp from a struct tm works correctly.
48 : */
49 : #define TEST_CREATE_FROM_STRUCT_TM(yr, mon, mday, hr, min, sec, \
50 : tz_offset, expected_unix_timestamp) \
51 : do { \
52 : struct tm tm = {}; \
53 : tm.tm_year = yr - STRUCT_TM_YEAR_OFFSET; \
54 : tm.tm_mon = mon - STRUCT_TM_MONTH_OFFSET; \
55 : tm.tm_mday = mday; \
56 : tm.tm_hour = hr; \
57 : tm.tm_min = min; \
58 : tm.tm_sec = sec; \
59 : tm.tm_isdst = 0; \
60 : REQUIRE(mktime(&tm) != -1); \
61 : { \
62 : Timestamp t = EMPTY_TIMESTAMP; \
63 : t = Timestamp::create(tm, \
64 : TimeDelta::from_hours(tz_offset)); \
65 : REQUIRE_FALSE(t.has_error); \
66 : IS(expected_unix_timestamp, 0); \
67 : CHECK(t.get_clock_time_utc().minute() == min); \
68 : CHECK(t.get_clock_time_utc().second() == sec); \
69 : CHECK(t.get_clock_time_utc().nanosecond() == 0); \
70 : } \
71 : { \
72 : Timestamp t = EMPTY_TIMESTAMP; \
73 : TimeDelta delta = TimeDelta::from_hours(tz_offset); \
74 : t = Timestamp_from_struct_tm(tm, &delta); \
75 : REQUIRE_FALSE(t.has_error); \
76 : IS(expected_unix_timestamp, 0); \
77 : CHECK(t.get_clock_time_utc().minute() == min); \
78 : CHECK(t.get_clock_time_utc().second() == sec); \
79 : CHECK(t.get_clock_time_utc().nanosecond() == 0); \
80 : } \
81 : { \
82 : Timestamp t = EMPTY_TIMESTAMP; \
83 : TimeDelta delta = TimeDelta::from_hours(tz_offset); \
84 : Timestamp_ptr_from_struct_tm(&t, tm, &delta); \
85 : REQUIRE_FALSE(t.has_error); \
86 : IS(expected_unix_timestamp, 0); \
87 : CHECK(t.get_clock_time_utc().minute() == min); \
88 : CHECK(t.get_clock_time_utc().second() == sec); \
89 : CHECK(t.get_clock_time_utc().nanosecond() == 0); \
90 : } \
91 : } while (0)
92 :
93 : /**
94 : * Check that creating a Timestamp from a Date and a ClockTime works
95 : * correctly. This tests the C++ @p Timestamp::create function and the C
96 : * @p Timestamp_create and @p Timestamp_ptr_create functions.
97 : */
98 : #define TEST_CREATE_FROM_DATE_TIME(yr, mon, mday, hr, min, sec, \
99 : tz_offset, expected_unix_timestamp) \
100 : do { \
101 : const Date testDate = Date::create(yr, mon, mday); \
102 : const ClockTime testTime = ClockTime::create(hr, min, sec); \
103 : const TimeDelta testDelta = TimeDelta::from_hours(tz_offset); \
104 : { \
105 : Timestamp t = EMPTY_TIMESTAMP; \
106 : t = Timestamp::create(testDate, testTime, testDelta); \
107 : REQUIRE_FALSE(t.has_error); \
108 : IS(expected_unix_timestamp, 0); \
109 : CHECK(t.get_clock_time_utc().minute() == min); \
110 : CHECK(t.get_clock_time_utc().second() == sec); \
111 : CHECK(t.get_clock_time_utc().nanosecond() == 0); \
112 : } \
113 : { \
114 : Timestamp t = EMPTY_TIMESTAMP; \
115 : t = Timestamp_create(&testDate, &testTime, &testDelta); \
116 : REQUIRE_FALSE(t.has_error); \
117 : IS(expected_unix_timestamp, 0); \
118 : CHECK(t.get_clock_time_utc().minute() == min); \
119 : CHECK(t.get_clock_time_utc().second() == sec); \
120 : CHECK(t.get_clock_time_utc().nanosecond() == 0); \
121 : } \
122 : { \
123 : Timestamp t = EMPTY_TIMESTAMP; \
124 : Timestamp_ptr_create(&t, &testDate, &testTime, &testDelta); \
125 : REQUIRE_FALSE(t.has_error); \
126 : IS(expected_unix_timestamp, 0); \
127 : CHECK(t.get_clock_time_utc().minute() == min); \
128 : CHECK(t.get_clock_time_utc().second() == sec); \
129 : CHECK(t.get_clock_time_utc().nanosecond() == 0); \
130 : } \
131 : } while (0)
132 :
133 : /**
134 : * This test case tests all the overloads of the "create" method (which also
135 : * tests the C "Timestamp_create_..." functions).
136 : */
137 7 : TEST_CASE("Timestamp creators", "[timestamp]") {
138 : Timestamp t;
139 :
140 : // create(time_t)
141 : // from_time_t(time_t)
142 6 : SECTION("creating from a time_t") {
143 1 : t = Timestamp::create((time_t) 0);
144 1 : IS(0, 0);
145 1 : t = Timestamp::create((time_t) 1325376000);
146 1 : IS(1325376000, 0);
147 :
148 1 : t = Timestamp_from_time_t((time_t) 0);
149 1 : IS(0, 0);
150 1 : t = Timestamp_from_time_t((time_t) 918491000);
151 1 : IS(918491000, 0);
152 :
153 1 : Timestamp_ptr_from_time_t(&t, (time_t) 0);
154 1 : IS(0, 0);
155 1 : Timestamp_ptr_from_time_t(&t, (time_t) 1);
156 1 : IS(1, 0);
157 6 : }
158 :
159 : // create(struct tm, TimeDelta)
160 : // create(Date, ClockTime, TimeDelta)
161 : // from_struct_tm(struct tm, TimeDelta)
162 6 : SECTION("creating from a struct tm, or from a Date/ClockTime/TimeDelta") {
163 1 : TEST_CREATE_FROM_STRUCT_TM(
164 : 1970, 1, 1, // date
165 : 0, 0, 0, // time
166 : 0, // time zone offset
167 : 0); // expected UNIX timestamp
168 1 : TEST_CREATE_FROM_DATE_TIME(1970, 1, 1, 0, 0, 0, 0, 0);
169 :
170 1 : TEST_CREATE_FROM_STRUCT_TM(1969, 12, 31, 0, 0, 0, 0, -86400);
171 1 : TEST_CREATE_FROM_DATE_TIME(1969, 12, 31, 0, 0, 0, 0, -86400);
172 :
173 1 : TEST_CREATE_FROM_STRUCT_TM(1970, 1, 2, 0, 0, 0, 0, 86400);
174 1 : TEST_CREATE_FROM_DATE_TIME(1970, 1, 2, 0, 0, 0, 0, 86400);
175 :
176 : // All the same timestamp (197589599):
177 : // Apr. 5, 1976 21:59:59 UTC
178 : // Apr. 5, 1976 16:59:59 EST (UTC-05:00)
179 : // Apr. 6, 1976 00:59:59 MSK (UTC+03:00)
180 1 : TEST_CREATE_FROM_STRUCT_TM(1976, 4, 5, 21, 59, 59, 0, 197589599);
181 1 : TEST_CREATE_FROM_DATE_TIME(1976, 4, 5, 21, 59, 59, 0, 197589599);
182 :
183 1 : TEST_CREATE_FROM_STRUCT_TM(1976, 4, 5, 16, 59, 59, -5, 197589599);
184 1 : TEST_CREATE_FROM_DATE_TIME(1976, 4, 5, 16, 59, 59, -5, 197589599);
185 :
186 1 : TEST_CREATE_FROM_STRUCT_TM(1976, 4, 6, 0, 59, 59, +3, 197589599);
187 1 : TEST_CREATE_FROM_DATE_TIME(1976, 4, 6, 0, 59, 59, +3, 197589599);
188 6 : }
189 :
190 : // create_utc(struct tm)
191 : // from_struct_tm_utc(struct tm)
192 6 : SECTION("creating from a struct tm in UTC") {
193 1 : struct tm nicks_bday = {};
194 1 : nicks_bday.tm_year = 2000 - STRUCT_TM_YEAR_OFFSET;
195 1 : nicks_bday.tm_mon = 8 - STRUCT_TM_MONTH_OFFSET;
196 1 : nicks_bday.tm_mday = 18;
197 1 : nicks_bday.tm_hour = 17;
198 1 : nicks_bday.tm_min = 12;
199 1 : nicks_bday.tm_sec = 59;
200 1 : nicks_bday.tm_isdst = -1;
201 :
202 1 : t = Timestamp_from_struct_tm_utc(nicks_bday);
203 1 : CHECK(t.get_date_utc() == Date::create(2000, 8, 18));
204 1 : CHECK(t.get_clock_time_utc() == ClockTime::create(17, 12, 59));
205 :
206 1 : t = Timestamp::create_utc(nicks_bday);
207 1 : CHECK(t.get_date_utc() == Date::create(2000, 8, 18));
208 1 : CHECK(t.get_clock_time_utc() == ClockTime::create(17, 12, 59));
209 6 : }
210 :
211 6 : SECTION("creating from a Date/ClockTime with a bad Date or ClockTime") {
212 1 : Date bad_d = Date::create(0, 0, 0);
213 1 : REQUIRE(bad_d.has_error);
214 1 : Date good_d = Date::create(2000, 1, 1);
215 1 : REQUIRE_FALSE(good_d.has_error);
216 :
217 1 : ClockTime bad_ct = ClockTime::create(99, 99, 99);
218 1 : REQUIRE(bad_ct.has_error);
219 1 : ClockTime good_ct = ClockTime::noon();
220 1 : REQUIRE_FALSE(good_ct.has_error);
221 :
222 1 : t = Timestamp::create(bad_d, good_ct, TimeDelta::zero());
223 1 : IS_ERROR(invalid_date);
224 1 : t = Timestamp::create_utc(bad_d, good_ct);
225 1 : IS_ERROR(invalid_date);
226 1 : t = Timestamp::create_local(bad_d, good_ct);
227 1 : IS_ERROR(invalid_date);
228 :
229 1 : t = Timestamp::create(good_d, bad_ct, TimeDelta::zero());
230 1 : IS_ERROR(invalid_clock_time);
231 1 : t = Timestamp::create_utc(good_d, bad_ct);
232 1 : IS_ERROR(invalid_clock_time);
233 1 : t = Timestamp::create_local(good_d, bad_ct);
234 1 : IS_ERROR(invalid_clock_time);
235 6 : }
236 :
237 6 : SECTION("now()") {
238 : // (change what present_now() returns to 1999-2-28 05:34:41.986 UTC)
239 : struct PresentNowStruct test_now = {
240 : (time_t) 920180081,
241 : (long) 986000000
242 1 : };
243 1 : present_set_test_time(test_now);
244 :
245 1 : t = EMPTY_TIMESTAMP;
246 1 : t = Timestamp::now();
247 1 : IS(920180081, 986000000);
248 :
249 1 : t = EMPTY_TIMESTAMP;
250 1 : t = Timestamp_now();
251 1 : IS(920180081, 986000000);
252 :
253 1 : t = EMPTY_TIMESTAMP;
254 1 : Timestamp_ptr_now(&t);
255 1 : IS(920180081, 986000000);
256 :
257 1 : present_reset_test_time();
258 :
259 : // (make sure it returns something different now)
260 1 : t = EMPTY_TIMESTAMP;
261 1 : t = Timestamp::now();
262 1 : REQUIRE_FALSE(t.has_error);
263 1 : CHECK(t.data_.timestamp_seconds != 920180081);
264 6 : }
265 :
266 6 : SECTION("epoch()") {
267 1 : t = EMPTY_TIMESTAMP;
268 1 : t = Timestamp::epoch();
269 1 : IS(0, 0);
270 :
271 1 : t = EMPTY_TIMESTAMP;
272 1 : t = Timestamp_epoch();
273 1 : IS(0, 0);
274 :
275 1 : t = EMPTY_TIMESTAMP;
276 1 : Timestamp_ptr_epoch(&t);
277 1 : IS(0, 0);
278 6 : }
279 6 : }
280 :
281 2 : TEST_CASE("Timestamp creators edge case finder", "[timestamp]") {
282 : Timestamp t;
283 :
284 601 : for (int_year year = 2099; year >= 1500; year--) {
285 : t = Timestamp::create_utc(
286 : Date::create(year, 1, 1),
287 600 : ClockTime::midnight());
288 600 : CHECK(t.get_date_utc() == Date::create(year, 1, 1));
289 :
290 : t = Timestamp::create_utc(
291 : Date::create(year, 3, 1),
292 600 : ClockTime::midnight());
293 600 : CHECK(t.get_date_utc() == Date::create(year, 3, 1));
294 : }
295 1 : }
296 :
297 4 : TEST_CASE("Timestamp accessors", "[timestamp]") {
298 : // All the same timestamp (197589599):
299 : // Apr. 5, 1976 21:59:59 UTC
300 : // Apr. 5, 1976 16:59:59 EST (UTC-05:00)
301 : // Apr. 6, 1976 00:59:59 MSK (UTC+03:00)
302 :
303 3 : Timestamp t = Timestamp::create((time_t) 197589599);
304 3 : REQUIRE_FALSE(t.has_error);
305 :
306 : // get_time_t
307 3 : CHECK(t.get_time_t() == 197589599);
308 :
309 3 : SECTION("get_struct_tm family") {
310 1 : struct tm tm_utc = t.get_struct_tm_utc();
311 1 : CHECK(tm_utc.tm_year == 1976 - STRUCT_TM_YEAR_OFFSET);
312 1 : CHECK(tm_utc.tm_mon == 4 - STRUCT_TM_MONTH_OFFSET);
313 1 : CHECK(tm_utc.tm_mday == 5);
314 1 : CHECK(tm_utc.tm_hour == 21);
315 1 : CHECK(tm_utc.tm_min == 59);
316 1 : CHECK(tm_utc.tm_sec == 59);
317 :
318 1 : struct tm tm_est = t.get_struct_tm(TimeDelta::from_hours(-5));
319 1 : CHECK(tm_est.tm_year == 1976 - STRUCT_TM_YEAR_OFFSET);
320 1 : CHECK(tm_est.tm_mon == 4 - STRUCT_TM_MONTH_OFFSET);
321 1 : CHECK(tm_est.tm_mday == 5);
322 1 : CHECK(tm_est.tm_hour == 16);
323 1 : CHECK(tm_est.tm_min == 59);
324 1 : CHECK(tm_est.tm_sec == 59);
325 :
326 1 : struct tm tm_msk = t.get_struct_tm(TimeDelta::from_hours(3));
327 1 : CHECK(tm_msk.tm_year == 1976 - STRUCT_TM_YEAR_OFFSET);
328 1 : CHECK(tm_msk.tm_mon == 4 - STRUCT_TM_MONTH_OFFSET);
329 1 : CHECK(tm_msk.tm_mday == 6);
330 1 : CHECK(tm_msk.tm_hour == 0);
331 1 : CHECK(tm_msk.tm_min == 59);
332 1 : CHECK(tm_msk.tm_sec == 59);
333 3 : }
334 :
335 3 : SECTION("get_date family") {
336 1 : CHECK(t.get_date_utc() == Date::create(1976, 4, 5));
337 1 : CHECK(t.get_date(TimeDelta::zero()) == Date::create(1976, 4, 5));
338 1 : CHECK(t.get_date(TimeDelta::from_hours(-5)) ==
339 : Date::create(1976, 4, 5));
340 1 : CHECK(t.get_date(TimeDelta::from_hours(3)) ==
341 : Date::create(1976, 4, 6));
342 3 : }
343 :
344 3 : SECTION("get_clock_time family") {
345 1 : CHECK(t.get_clock_time_utc() == ClockTime::create(21, 59, 59));
346 1 : CHECK(t.get_clock_time(TimeDelta::zero()) ==
347 : ClockTime::create(21, 59, 59));
348 1 : CHECK(t.get_clock_time(TimeDelta::from_hours(-5)) ==
349 : ClockTime::create(16, 59, 59));
350 1 : CHECK(t.get_clock_time(TimeDelta::from_hours(3)) ==
351 : ClockTime::create(0, 59, 59));
352 3 : }
353 3 : }
354 :
355 3 : TEST_CASE("Timestamp creators and accessors in local time", "[timestamp]") {
356 2 : SECTION("create in local time, access in local time (winter)") {
357 : // create with the C function, from a Date and ClockTime
358 1 : const Date date_input = Date::create(1959, 1, 17);
359 1 : const ClockTime clock_time_input = ClockTime::create(14, 39, 45);
360 1 : Timestamp t = Timestamp_create_local(&date_input, &clock_time_input);
361 1 : REQUIRE_FALSE(t.has_error);
362 :
363 1 : struct tm tm_local = t.get_struct_tm_local();
364 1 : CHECK(tm_local.tm_year == 1959 - STRUCT_TM_YEAR_OFFSET);
365 1 : CHECK(tm_local.tm_mon == 1 - STRUCT_TM_MONTH_OFFSET);
366 1 : CHECK(tm_local.tm_mday == 17);
367 1 : CHECK(tm_local.tm_hour == 14);
368 1 : CHECK(tm_local.tm_min == 39);
369 1 : CHECK(tm_local.tm_sec == 45);
370 :
371 1 : CHECK(t.get_date_local() == date_input);
372 1 : CHECK(t.get_clock_time_local() == clock_time_input);
373 2 : }
374 :
375 2 : SECTION("create in local time, access in local time (summer)") {
376 1 : struct tm tm_input = {};
377 1 : tm_input.tm_year = 1959 - STRUCT_TM_YEAR_OFFSET;
378 1 : tm_input.tm_mon = 7 - STRUCT_TM_MONTH_OFFSET;
379 1 : tm_input.tm_mday = 17;
380 1 : tm_input.tm_hour = 14;
381 1 : tm_input.tm_min = 39;
382 1 : tm_input.tm_sec = 45;
383 1 : tm_input.tm_isdst = -1;
384 :
385 : // create with the C function, from a struct tm
386 1 : Timestamp t = Timestamp_from_struct_tm_local(tm_input);
387 1 : REQUIRE_FALSE(t.has_error);
388 :
389 1 : struct tm tm_local = t.get_struct_tm_local();
390 1 : CHECK(tm_local.tm_year == 1959 - STRUCT_TM_YEAR_OFFSET);
391 1 : CHECK(tm_local.tm_mon == 7 - STRUCT_TM_MONTH_OFFSET);
392 1 : CHECK(tm_local.tm_mday == 17);
393 1 : CHECK(tm_local.tm_hour == 14);
394 1 : CHECK(tm_local.tm_min == 39);
395 1 : CHECK(tm_local.tm_sec == 45);
396 :
397 1 : CHECK(t.get_date_local() == Date::create(1959, 7, 17));
398 1 : CHECK(t.get_clock_time_local() == ClockTime::create(14, 39, 45));
399 2 : }
400 :
401 : // This test won't work if the absolute value of the time zone offset is
402 : // greater than 12 hours :(
403 2 : time_t time_zone_offset_winter = get_local_time_zone_offset_for_january();
404 2 : if (ABS(time_zone_offset_winter) < 12 * SECONDS_IN_HOUR) {
405 : TimeDelta time_zone_offset(
406 0 : TimeDelta::from_seconds(time_zone_offset_winter));
407 :
408 0 : SECTION("create in UTC, access in local time (winter)") {
409 : Timestamp t = Timestamp::create_utc(
410 : Date::create(1987, 1, 4),
411 0 : ClockTime::noon());
412 0 : REQUIRE_FALSE(t.has_error);
413 :
414 0 : CHECK(t.get_date_local() == Date::create(1987, 1, 4));
415 0 : CHECK(t.get_clock_time_local() - time_zone_offset
416 : == ClockTime::noon());
417 :
418 : // Sanity check
419 0 : CHECK((t - time_zone_offset).get_date_local() ==
420 : Date::create(1987, 1, 4));
421 0 : CHECK((t - time_zone_offset).get_clock_time_local() ==
422 : ClockTime::noon());
423 0 : }
424 :
425 0 : SECTION("create in local time, access in UTC (winter)") {
426 : Timestamp t = Timestamp::create_local(
427 : Date::create(1987, 1, 4),
428 0 : ClockTime::noon());
429 0 : REQUIRE_FALSE(t.has_error);
430 :
431 0 : CHECK(t.get_date_utc() == Date::create(1987, 1, 4));
432 0 : CHECK(t.get_clock_time_utc() + time_zone_offset
433 : == ClockTime::noon());
434 :
435 : // Sanity check
436 0 : CHECK((t + time_zone_offset).get_date_utc() ==
437 : Date::create(1987, 1, 4));
438 0 : CHECK((t + time_zone_offset).get_clock_time_utc() ==
439 : ClockTime::noon());
440 0 : }
441 : }
442 :
443 : // Now, do the same thing for summer
444 2 : time_t time_zone_offset_summer = get_local_time_zone_offset_for_july();
445 2 : if (ABS(time_zone_offset_summer) < 12 * SECONDS_IN_HOUR) {
446 : TimeDelta time_zone_offset(
447 0 : TimeDelta::from_seconds(time_zone_offset_summer));
448 :
449 0 : SECTION("create in UTC, access in local time (summer)") {
450 : Timestamp t = Timestamp::create_utc(
451 : Date::create(1992, 7, 4),
452 0 : ClockTime::noon());
453 0 : REQUIRE_FALSE(t.has_error);
454 :
455 0 : CHECK(t.get_date_local() == Date::create(1992, 7, 4));
456 0 : CHECK(t.get_clock_time_local() - time_zone_offset
457 : == ClockTime::noon());
458 :
459 : // Sanity check
460 0 : CHECK((t - time_zone_offset).get_date_local() ==
461 : Date::create(1992, 7, 4));
462 0 : CHECK((t - time_zone_offset).get_clock_time_local() ==
463 : ClockTime::noon());
464 0 : }
465 :
466 0 : SECTION("create in local time, access in UTC (summer)") {
467 : Timestamp t = Timestamp::create_local(
468 : Date::create(1992, 7, 4),
469 0 : ClockTime::noon());
470 0 : REQUIRE_FALSE(t.has_error);
471 :
472 0 : CHECK(t.get_date_utc() == Date::create(1992, 7, 4));
473 0 : CHECK(t.get_clock_time_utc() + time_zone_offset
474 : == ClockTime::noon());
475 :
476 : // Sanity check
477 0 : CHECK((t + time_zone_offset).get_date_utc() ==
478 : Date::create(1992, 7, 4));
479 0 : CHECK((t + time_zone_offset).get_clock_time_utc() ==
480 : ClockTime::noon());
481 0 : }
482 : }
483 :
484 2 : }
485 :
486 2 : TEST_CASE("Timestamp 'difference' functions", "[timestamp]") {
487 1 : const time_t base = 197589599;
488 1 : Timestamp t1 = Timestamp::create(base),
489 1 : t2 = Timestamp::create(base + 86400 + 3600); // + 1 day, 1 hour
490 1 : REQUIRE_FALSE(t1.has_error);
491 1 : REQUIRE_FALSE(t2.has_error);
492 :
493 1 : TimeDelta exp_diff = TimeDelta::from_seconds(86400 + 3600);
494 1 : CHECK(t1.difference(t2) == -exp_diff);
495 1 : CHECK(t2.difference(t1) == exp_diff);
496 1 : CHECK(t1.absolute_difference(t2) == exp_diff);
497 1 : CHECK(t2.absolute_difference(t1) == exp_diff);
498 1 : }
499 :
500 2 : TEST_CASE("Timestamp arithmetic operators with TimeDelta", "[timestamp]") {
501 1 : const time_t base_time = 197589599;
502 1 : TimeDelta seconds_plus4 = TimeDelta::from_seconds(4),
503 1 : hours_minus4 = TimeDelta::from_hours(-4);
504 :
505 1 : Timestamp orig_t = Timestamp::create(base_time),
506 : t;
507 1 : REQUIRE_FALSE(orig_t.has_error);
508 :
509 1 : t = orig_t;
510 1 : t += seconds_plus4;
511 1 : CHECK(t.get_time_t() == base_time + 4);
512 :
513 1 : t = orig_t;
514 1 : t -= seconds_plus4;
515 1 : CHECK(t.get_time_t() == base_time - 4);
516 :
517 1 : t = orig_t;
518 1 : t += hours_minus4;
519 1 : CHECK(t.get_time_t() == base_time + (3600 * -4));
520 :
521 1 : t = orig_t;
522 1 : t -= hours_minus4;
523 1 : CHECK(t.get_time_t() == base_time - (3600 * -4));
524 :
525 : // TODO: more...
526 1 : }
527 :
528 2 : TEST_CASE("Timestamp arithmetic operators with DayDelta", "[timestamp]") {
529 1 : DayDelta days_plus5 = DayDelta::from_days(5),
530 1 : weeks_minus2 = DayDelta::from_weeks(-2);
531 :
532 : Timestamp orig_t = Timestamp::create_utc(Date::create(1981, 4, 1),
533 1 : ClockTime::midnight());
534 1 : REQUIRE_FALSE(orig_t.has_error);
535 : Timestamp t;
536 :
537 1 : t = orig_t;
538 1 : t += days_plus5;
539 1 : CHECK(t.get_date_utc() == Date::create(1981, 4, 6));
540 1 : t += days_plus5;
541 1 : CHECK(t.get_date_utc() == Date::create(1981, 4, 11));
542 :
543 1 : t = orig_t;
544 1 : t -= days_plus5;
545 1 : CHECK(t.get_date_utc() == Date::create(1981, 3, 27));
546 1 : t -= days_plus5;
547 1 : CHECK(t.get_date_utc() == Date::create(1981, 3, 22));
548 :
549 1 : t = orig_t;
550 1 : t += weeks_minus2;
551 1 : CHECK(t.get_date_utc() == Date::create(1981, 3, 18));
552 :
553 1 : t = orig_t;
554 1 : t -= weeks_minus2;
555 1 : CHECK(t.get_date_utc() == Date::create(1981, 4, 15));
556 1 : t += weeks_minus2;
557 1 : CHECK(t.get_date_utc() == Date::create(1981, 4, 1));
558 :
559 :
560 2 : t = Timestamp::create_utc(Date::create(1995, 6, 23), ClockTime::midnight())
561 3 : + DayDelta::from_days(4);
562 1 : CHECK(t.get_date_utc() == Date::create(1995, 6, 27));
563 :
564 2 : t = Timestamp::create_utc(Date::create(1968, 2, 24), ClockTime::midnight())
565 3 : - DayDelta::from_days(16);
566 1 : CHECK(t.get_date_utc() == Date::create(1968, 2, 8));
567 :
568 2 : t = (Timestamp::create_utc(Date::create(2002, 1, 1), ClockTime::midnight())
569 4 : - DayDelta::from_days(3))
570 3 : + DayDelta::from_weeks(2);
571 1 : CHECK(t.get_date_utc() == Date::create(2002, 1, 12));
572 :
573 1 : t = DayDelta::from_days(8) + orig_t;
574 1 : CHECK(t.get_date_utc() == Date::create(1981, 4, 9));
575 1 : }
576 :
577 2 : TEST_CASE("Timestamp arithmetic operators with MonthDelta", "[timestamp]") {
578 1 : MonthDelta months_plus2 = MonthDelta::from_months(2),
579 1 : months_minus5 = MonthDelta::from_months(-5),
580 1 : years_plus4 = MonthDelta::from_years(4),
581 1 : years_minus16 = MonthDelta::from_years(-16);
582 :
583 : Timestamp orig_t = Timestamp::create_utc(Date::create(1982, 11, 13),
584 1 : ClockTime::midnight());
585 : Timestamp t;
586 :
587 1 : t = orig_t;
588 1 : t += months_plus2;
589 1 : CHECK(t.get_date_utc() == Date::create(1983, 1, 13));
590 1 : t += months_minus5;
591 1 : CHECK(t.get_date_utc() == Date::create(1982, 8, 13));
592 :
593 1 : t = orig_t;
594 1 : t -= months_plus2;
595 1 : CHECK(t.get_date_utc() == Date::create(1982, 9, 13));
596 1 : t -= months_minus5;
597 1 : CHECK(t.get_date_utc() == Date::create(1983, 2, 13));
598 :
599 1 : t = orig_t;
600 1 : t += years_plus4;
601 1 : CHECK(t.get_date_utc() == Date::create(1986, 11, 13));
602 1 : t += years_minus16;
603 1 : CHECK(t.get_date_utc() == Date::create(1970, 11, 13));
604 :
605 1 : t = orig_t;
606 1 : t -= years_plus4;
607 1 : CHECK(t.get_date_utc() == Date::create(1978, 11, 13));
608 1 : t -= years_minus16;
609 1 : CHECK(t.get_date_utc() == Date::create(1994, 11, 13));
610 :
611 1 : t = orig_t + MonthDelta::from_months(7);
612 1 : CHECK(t.get_date_utc() == Date::create(1983, 6, 13));
613 :
614 1 : t = orig_t - MonthDelta::from_months(3);
615 1 : CHECK(t.get_date_utc() == Date::create(1982, 8, 13));
616 :
617 1 : t = MonthDelta::from_years(4) + orig_t;
618 1 : CHECK(t.get_date_utc() == Date::create(1986, 11, 13));
619 1 : }
620 :
621 2 : TEST_CASE("Timestamp arithmetic operators edge cases", "[timestamp]") {
622 1 : DayDelta days_plus8 = DayDelta::from_days(8);
623 1 : MonthDelta months_plus5 = MonthDelta::from_months(5),
624 1 : months_minus11 = MonthDelta::from_months(-11);
625 :
626 : Timestamp orig_t = Timestamp::create_utc(
627 1 : Date::create(1934, 8, 16), ClockTime::create(23, 37, 48));
628 : Timestamp t;
629 :
630 1 : t = orig_t;
631 1 : t += days_plus8;
632 1 : CHECK(t.get_date_utc() == Date::create(1934, 8, 24));
633 :
634 1 : t = orig_t;
635 1 : t -= days_plus8;
636 1 : CHECK(t.get_date_utc() == Date::create(1934, 8, 8));
637 :
638 1 : t = orig_t;
639 1 : t += months_plus5;
640 1 : CHECK(t.get_date_utc() == Date::create(1935, 1, 16));
641 :
642 1 : t = orig_t;
643 1 : t -= months_plus5;
644 1 : CHECK(t.get_date_utc() == Date::create(1934, 3, 16));
645 :
646 1 : t = orig_t;
647 1 : t += months_minus11;
648 1 : CHECK(t.get_date_utc() == Date::create(1933, 9, 16));
649 :
650 1 : t = orig_t;
651 1 : t -= months_minus11;
652 1 : CHECK(t.get_date_utc() == Date::create(1935, 7, 16));
653 4 : }
654 :
|