1 /*
2 * Copyright (C) 2009 Emweb bv, Herent, Belgium.
3 *
4 * See the LICENSE file for terms of use.
5 */
6 #include <boost/test/unit_test.hpp>
7
8 #include <Wt/WDate.h>
9 #include <Wt/WTime.h>
10 #include <Wt/WDateTime.h>
11 #include <Wt/WLocalDateTime.h>
12 #include <Wt/WStringStream.h>
13
14 #include <Wt/Date/tz.h>
15
16 #include <stdlib.h>
17 #include <chrono>
18 #include <iostream>
19 #include <type_traits>
20
BOOST_AUTO_TEST_CASE(WDateTime_test_WDate)21 BOOST_AUTO_TEST_CASE( WDateTime_test_WDate )
22 {
23 Wt::WDate wd(2009, 10, 1);
24 BOOST_REQUIRE(wd.toString() == "Thu Oct 1 2009");
25 BOOST_REQUIRE(wd.isValid());
26 BOOST_REQUIRE(!wd.isNull());
27 }
28
BOOST_AUTO_TEST_CASE(WDateTime_test_WDate2)29 BOOST_AUTO_TEST_CASE( WDateTime_test_WDate2 )
30 {
31 Wt::WDate wd;
32 BOOST_REQUIRE(!wd.isValid());
33 BOOST_REQUIRE(wd.isNull());
34
35 wd.setDate(40, 50, 2000);
36
37 BOOST_REQUIRE(!wd.isValid());
38 BOOST_REQUIRE(!wd.isNull());
39
40 wd = Wt::WDate::fromString("31/07/9999", "dd/MM/yyyy");
41
42 BOOST_REQUIRE(wd.isValid());
43 BOOST_REQUIRE(!wd.isNull());
44 }
45
BOOST_AUTO_TEST_CASE(WDateTime_test_WDate3)46 BOOST_AUTO_TEST_CASE( WDateTime_test_WDate3)
47 {
48 Wt::WDate wd1(2009, 10, 2);
49 Wt::WDate wd2(2010, 10, 2);
50 BOOST_REQUIRE(wd1.daysTo(wd2) == 365);
51 BOOST_REQUIRE(wd1.isLeapYear(2016));
52
53 Wt::WDate wd3(2016, 2, 18); //Thursday
54 BOOST_REQUIRE(wd3.dayOfWeek() == 4);
55 BOOST_REQUIRE(wd3.toJulianDay() == 2457437);
56
57 Wt::WDate wd4 = wd3.fromJulianDay(2457437);
58 BOOST_REQUIRE(wd4.day() == 18);
59 BOOST_REQUIRE(wd4.month() == 2);
60 BOOST_REQUIRE(wd4.year() == 2016);
61
62 /*Wt::WDate current = wd3.currentServerDate();
63 BOOST_REQUIRE(current.day() == 1);
64 BOOST_REQUIRE(current.month() == 3);
65 BOOST_REQUIRE(current.year() == 2016);*/
66 }
67
BOOST_AUTO_TEST_CASE(WDateTime_test_WDate4)68 BOOST_AUTO_TEST_CASE( WDateTime_test_WDate4 )
69 {
70 Wt::WDate current(2016, 2, 24); //Wednesday
71 Wt::WDate d = Wt::WDate::previousWeekday(current, 5);
72 //Looking for previous Friday
73 BOOST_REQUIRE(d.day() == 19);
74 BOOST_REQUIRE(d.month() == 2);
75 BOOST_REQUIRE(d.year() == 2016);
76 }
77
BOOST_AUTO_TEST_CASE(WDateTime_test_WDate5)78 BOOST_AUTO_TEST_CASE( WDateTime_test_WDate5 )
79 {
80 Wt::WDate wd = Wt::WDate::fromString("31/07/9999/not-valid", "dd/MM/yyyy");
81 BOOST_REQUIRE(!wd.isValid());
82 BOOST_REQUIRE(wd.isNull());
83 }
84
BOOST_AUTO_TEST_CASE(WDateTime_test_WDate6)85 BOOST_AUTO_TEST_CASE( WDateTime_test_WDate6 )
86 {
87 // Testing day being set to last valid day in resulting month/year when doing addMonths
88 Wt::WDate wd(2019, 5, 31);
89 wd = wd.addMonths(6);
90
91 BOOST_REQUIRE(wd.isValid());
92 BOOST_REQUIRE(wd.day() == 30);
93 BOOST_REQUIRE(wd.month() == 11);
94 BOOST_REQUIRE(wd.year() == 2019);
95 }
96
BOOST_AUTO_TEST_CASE(WDateTime_test_WDate7)97 BOOST_AUTO_TEST_CASE( WDateTime_test_WDate7 )
98 {
99 // Testing day being set to last valid day in resulting month/year when doing addYears
100 Wt::WDate wd(2016, 2, 29);
101 wd = wd.addYears(1);
102
103 BOOST_REQUIRE(wd.isValid());
104 BOOST_REQUIRE(wd.day() == 28);
105 BOOST_REQUIRE(wd.month() == 2);
106 BOOST_REQUIRE(wd.year() == 2017);
107 }
108
BOOST_AUTO_TEST_CASE(WDateTime_test_WTime)109 BOOST_AUTO_TEST_CASE( WDateTime_test_WTime )
110 {
111 Wt::WTime wt(22, 11, 31);
112 BOOST_REQUIRE(wt.toString() == "22:11:31");
113
114 std::chrono::duration<int, std::milli> duration = wt.toTimeDuration();
115 BOOST_REQUIRE(duration == std::chrono::hours(22) + std::chrono::minutes(11) + std::chrono::seconds(31) + std::chrono::milliseconds(0));
116
117 Wt::WTime wt2 = Wt::WTime::fromTimeDuration(duration);
118 BOOST_REQUIRE(wt2.hour() == 22);
119 BOOST_REQUIRE(wt2.minute() == 11);
120 BOOST_REQUIRE(wt2.second() == 31);
121
122 std::chrono::duration<int, std::milli> d = std::chrono::hours(13) +
123 std::chrono::minutes(42) + std::chrono::seconds(8) + std::chrono::milliseconds(102);
124 Wt::WTime wt3 = Wt::WTime::fromTimeDuration(d);
125 BOOST_REQUIRE(wt3.hour() == 13);
126 BOOST_REQUIRE(wt3.minute() == 42);
127 BOOST_REQUIRE(wt3.second() == 8);
128 BOOST_REQUIRE(wt3.msec() == 102);
129 BOOST_REQUIRE(wt3.toTimeDuration() == d);
130
131 Wt::WTime wt4(15, 53, 12, 100);
132 BOOST_REQUIRE(wt4.toString() == "15:53:12");
133
134 wt4 = wt4.addMSecs(900);
135 BOOST_REQUIRE(wt4.toString() == "15:53:13");
136 wt4 = wt4.addSecs(60);
137 BOOST_REQUIRE(wt4.toString() == "15:54:13");
138
139 BOOST_REQUIRE(wt4.secsTo(Wt::WTime(15, 54, 33)) == 20);
140
141 Wt::WTime wt5 = Wt::WTime::currentServerTime();
142 std::cerr << wt5.toString() << std::endl;
143
144 Wt::WTime wt6 = Wt::WTime::fromString("11:42:16");
145 BOOST_REQUIRE(wt6.hour() == 11);
146 BOOST_REQUIRE(wt6.minute() == 42);
147 BOOST_REQUIRE(wt6.second() == 16);
148 wt6 = wt6.addMSecs(587);
149 BOOST_REQUIRE(wt6.msec() == 587);
150 }
151
BOOST_AUTO_TEST_CASE(WDateTime_test_WTime2)152 BOOST_AUTO_TEST_CASE( WDateTime_test_WTime2 )
153 {
154 Wt::WTime wt = Wt::WTime::fromString("13:05:12", "hh:mm:ss");
155 BOOST_REQUIRE(wt.hour() == 13);
156 BOOST_REQUIRE(wt.minute() == 05);
157 BOOST_REQUIRE(wt.second() == 12);
158 }
159
BOOST_AUTO_TEST_CASE(WDateTime_test_WTime3)160 BOOST_AUTO_TEST_CASE( WDateTime_test_WTime3 )
161 {
162 Wt::WTime wt = Wt::WTime::fromString("13:05:12:not-valid", "hh:mm:ss");
163 BOOST_REQUIRE(!wt.isValid());
164 BOOST_REQUIRE(wt.isNull());
165 }
166
BOOST_AUTO_TEST_CASE(WDateTime_test_WDateTime)167 BOOST_AUTO_TEST_CASE( WDateTime_test_WDateTime )
168 {
169 Wt::WDate wd(2009, 10, 1);
170 Wt::WTime wt(12, 11, 31, 499);
171 Wt::WDateTime wdt(wd, wt);
172
173 BOOST_REQUIRE(wdt.isValid());
174 BOOST_REQUIRE(!wdt.isNull());
175
176 BOOST_REQUIRE(wdt.toString() == "Thu Oct 1 12:11:31 2009");
177 BOOST_REQUIRE(wdt.toString("ddd MMM d HH:mm:ss:zzz yyyy")
178 == "Thu Oct 1 12:11:31:499 2009");
179 BOOST_REQUIRE(wdt.time().msec() == 499);
180
181 Wt::WDateTime wdt2 = wdt.addMSecs(1600);
182 BOOST_REQUIRE(wdt.toString("ddd MMM d HH:mm:ss:zzz yyyy")
183 == "Thu Oct 1 12:11:31:499 2009");
184 BOOST_REQUIRE(wdt2.toString("ddd MMM d HH:mm:ss:zzz yyyy")
185 == "Thu Oct 1 12:11:33:099 2009");
186
187 Wt::WDateTime wdt3 = wdt.addSecs(50);
188 BOOST_REQUIRE(wdt.toString("ddd MMM d HH:mm:ss:zzz yyyy")
189 == "Thu Oct 1 12:11:31:499 2009");
190 BOOST_REQUIRE(wdt3.toString("ddd MMM d HH:mm:ss:zzz yyyy")
191 == "Thu Oct 1 12:12:21:499 2009");
192
193 Wt::WDateTime wdt4 = wdt.addDays(1);
194 BOOST_REQUIRE(wdt.toString("ddd MMM d HH:mm:ss:zzz yyyy")
195 == "Thu Oct 1 12:11:31:499 2009");
196 BOOST_REQUIRE(wdt4.toString("ddd MMM d HH:mm:ss:zzz yyyy")
197 == "Fri Oct 2 12:11:31:499 2009");
198
199 Wt::WDateTime wdt5 = wdt.addMonths(1);
200 BOOST_REQUIRE(wdt.toString("ddd MMM d HH:mm:ss:zzz yyyy")
201 == "Thu Oct 1 12:11:31:499 2009");
202 BOOST_REQUIRE(wdt5.toString("ddd MMM d HH:mm:ss:zzz yyyy")
203 == "Sun Nov 1 12:11:31:499 2009");
204
205 Wt::WDateTime wdt6 = wdt.addYears(6);
206 BOOST_REQUIRE(wdt.toString("ddd MMM d HH:mm:ss:zzz yyyy")
207 == "Thu Oct 1 12:11:31:499 2009");
208 BOOST_REQUIRE(wdt6.toString("ddd MMM d HH:mm:ss:zzz yyyy")
209 == "Thu Oct 1 12:11:31:499 2015");
210
211 Wt::WDateTime d = Wt::WDateTime::fromString("2000-06-14 13:05:12",
212 "yyyy-MM-dd hh:mm:ss");
213
214 BOOST_REQUIRE(d.date().year() == 2000);
215 BOOST_REQUIRE(d.date().month() == 6);
216 BOOST_REQUIRE(d.date().day() == 14);
217 BOOST_REQUIRE(d.time().hour() == 13);
218 BOOST_REQUIRE(d.time().minute() == 05);
219 BOOST_REQUIRE(d.time().second() == 12);
220
221 d = Wt::WDateTime::fromString("2000-06-14 13:05:12",
222 "yyyy-MM-dd HH:mm:ss");
223
224 BOOST_REQUIRE(d.date().year() == 2000);
225 BOOST_REQUIRE(d.date().month() == 6);
226 BOOST_REQUIRE(d.date().day() == 14);
227 BOOST_REQUIRE(d.time().hour() == 13);
228 BOOST_REQUIRE(d.time().minute() == 05);
229 BOOST_REQUIRE(d.time().second() == 12);
230
231 d = Wt::WDateTime::fromString("2000-06-14 1:05:12 AM",
232 "yyyy-MM-dd h:mm:ss AP");
233
234 BOOST_REQUIRE(d.date().year() == 2000);
235 BOOST_REQUIRE(d.date().month() == 6);
236 BOOST_REQUIRE(d.date().day() == 14);
237 BOOST_REQUIRE(d.time().hour() == 1);
238 BOOST_REQUIRE(d.time().minute() == 05);
239 BOOST_REQUIRE(d.time().second() == 12);
240
241 d = Wt::WDateTime::fromString("2000-06-14 1:05:12 pm",
242 "yyyy-MM-dd h:mm:ss ap");
243
244 BOOST_REQUIRE(d.date().year() == 2000);
245 BOOST_REQUIRE(d.date().month() == 6);
246 BOOST_REQUIRE(d.date().day() == 14);
247 BOOST_REQUIRE(d.time().hour() == 13);
248 BOOST_REQUIRE(d.time().minute() == 05);
249 BOOST_REQUIRE(d.time().second() == 12);
250
251 d = Wt::WDateTime::fromString("2000-06-14 1:05:12 PM",
252 "yyyy-MM-dd h:mm:ss a");
253
254 BOOST_REQUIRE(d.date().year() == 2000);
255 BOOST_REQUIRE(d.date().month() == 6);
256 BOOST_REQUIRE(d.date().day() == 14);
257 BOOST_REQUIRE(d.time().hour() == 13);
258 BOOST_REQUIRE(d.time().minute() == 05);
259 BOOST_REQUIRE(d.time().second() == 12);
260
261 d = Wt::WDateTime::fromString("2000-06-14 1:05:12 AM",
262 "yyyy-MM-dd h:mm:ss a");
263
264 BOOST_REQUIRE(d.date().year() == 2000);
265 BOOST_REQUIRE(d.date().month() == 6);
266 BOOST_REQUIRE(d.date().day() == 14);
267 BOOST_REQUIRE(d.time().hour() == 1);
268 BOOST_REQUIRE(d.time().minute() == 05);
269 BOOST_REQUIRE(d.time().second() == 12);
270
271 d = Wt::WDateTime::fromString("2000-06-14 1:05:12",
272 "yyyy-MM-dd h:mm:ss");
273
274 BOOST_REQUIRE(d.date().year() == 2000);
275 BOOST_REQUIRE(d.date().month() == 6);
276 BOOST_REQUIRE(d.date().day() == 14);
277 BOOST_REQUIRE(d.time().hour() == 1);
278 BOOST_REQUIRE(d.time().minute() == 05);
279 BOOST_REQUIRE(d.time().second() == 12);
280
281 d = Wt::WDateTime::fromString("2000-06-14 13:05:12",
282 "yyyy-MM-dd h:mm:ss");
283
284 BOOST_REQUIRE(d.date().year() == 2000);
285 BOOST_REQUIRE(d.date().month() == 6);
286 BOOST_REQUIRE(d.date().day() == 14);
287 BOOST_REQUIRE(d.time().hour() == 13);
288 BOOST_REQUIRE(d.time().minute() == 05);
289 BOOST_REQUIRE(d.time().second() == 12);
290
291 BOOST_REQUIRE(Wt::WDateTime::fromString(d.toString()) == d);
292
293 BOOST_REQUIRE(d.toString("ddd, MMM dd, yyyy; hh:mm:ss ap")
294 == "Wed, Jun 14, 2000; 01:05:12 pm");
295 BOOST_REQUIRE(d.toString("ddd, MMM dd, yyyy; hh:mm:ss 'a'")
296 == "Wed, Jun 14, 2000; 13:05:12 a");
297 BOOST_REQUIRE(d.toString("ddd, MMM dd, yyyy; hh:mm:ss")
298 == "Wed, Jun 14, 2000; 13:05:12");
299 }
300
BOOST_AUTO_TEST_CASE(WDateTime_test_WDateTime2)301 BOOST_AUTO_TEST_CASE( WDateTime_test_WDateTime2 )
302 {
303 Wt::WDateTime wdt;
304
305 BOOST_REQUIRE(!wdt.isValid());
306 BOOST_REQUIRE(wdt.isNull());
307 }
308
BOOST_AUTO_TEST_CASE(WDateTime_test_WDateTime3)309 BOOST_AUTO_TEST_CASE( WDateTime_test_WDateTime3 )
310 {
311 Wt::WDateTime wdt = Wt::WDateTime::fromString("2000-06-14 13:05:12:invalid",
312 "yyyy-MM-dd hh:mm:ss");
313
314 BOOST_REQUIRE(!wdt.isValid());
315 /*
316 * NOTE: This verifies that datetime is not null, matching current behavior
317 * as checked in WDateTime_testspecial_WDateTime for construction from a
318 * null date and null time....
319 */
320 BOOST_REQUIRE(!wdt.isNull());
321
322 wdt = Wt::WDateTime::fromString("2000-06-14-invalid 13:05:12",
323 "yyyy-MM-dd hh:mm:ss");
324 BOOST_REQUIRE(!wdt.isValid());
325 BOOST_REQUIRE(!wdt.isNull());
326 }
327
BOOST_AUTO_TEST_CASE(WDateTime_test_WDateTime4)328 BOOST_AUTO_TEST_CASE( WDateTime_test_WDateTime4 )
329 {
330 Wt::WDateTime d
331 = Wt::WDateTime::fromString("Sat, 24 Nov 2018 06:44:33 GMT",
332 "ddd, dd MMM yyyy hh:mm:ss 'GMT'");
333
334 BOOST_REQUIRE(d.date().year() == 2018);
335 BOOST_REQUIRE(d.date().month() == 11);
336 BOOST_REQUIRE(d.date().day() == 24);
337 BOOST_REQUIRE(d.time().hour() == 6);
338 BOOST_REQUIRE(d.time().minute() == 44);
339 BOOST_REQUIRE(d.time().second() == 33);
340 }
341
BOOST_AUTO_TEST_CASE(WDateTime_testspecial_WDateTime)342 BOOST_AUTO_TEST_CASE( WDateTime_testspecial_WDateTime )
343 {
344 Wt::WDateTime wdt;
345
346 BOOST_REQUIRE(!wdt.isValid());
347 BOOST_REQUIRE(wdt.isNull());
348
349 wdt = Wt::WDateTime(Wt::WDate(), Wt::WTime());
350
351 BOOST_REQUIRE(!wdt.isValid());
352 BOOST_REQUIRE(!wdt.isNull());
353
354 wdt = Wt::WDateTime(Wt::WDate(20, 30, 40), Wt::WTime());
355
356 BOOST_REQUIRE(!wdt.isValid());
357 BOOST_REQUIRE(!wdt.isNull());
358 }
359
BOOST_AUTO_TEST_CASE(WDateTime_test_WLocalDateTime)360 BOOST_AUTO_TEST_CASE( WDateTime_test_WLocalDateTime )
361 {
362 Wt::WDate wd(2009, 10, 1);
363 Wt::WTime wt(12, 11, 31, 499);
364 Wt::WDateTime wdt(wd, wt);
365
366 Wt::WLocale loc;
367 Wt::WLocalDateTime wldt = Wt::WLocalDateTime::offsetDateTime(wdt.toTimePoint(),
368 std::chrono::hours{-4},
369 loc.dateTimeFormat());
370
371 BOOST_REQUIRE(wldt.toString() == "2009-10-01 08:11:31");
372 BOOST_REQUIRE(wldt.timeZoneOffset() == -240); //4h behind
373
374 Wt::WDateTime utc = wldt.toUTC();
375
376 BOOST_REQUIRE(utc == wdt);
377 }
378
BOOST_AUTO_TEST_CASE(WDateTime_test_WLocalDateTime2)379 BOOST_AUTO_TEST_CASE( WDateTime_test_WLocalDateTime2 )
380 {
381 // initialize without timezone info
382 Wt::WLocalDateTime ldt(Wt::WLocale("en-US"));
383
384 BOOST_REQUIRE(!ldt.isValid());
385 BOOST_REQUIRE(ldt.isNull());
386
387 Wt::WDate wd(2021, 2, 19);
388 Wt::WTime wt(11, 03);
389 ldt.setDateTime(wd, wt);
390
391 // still invalid since there is no timezone info
392 BOOST_REQUIRE(!ldt.isValid());
393 BOOST_CHECK_THROW(ldt.timeZoneOffset(), Wt::WException);
394
395 ldt = Wt::WLocalDateTime::currentServerDateTime();
396 BOOST_REQUIRE(ldt.isValid());
397 BOOST_REQUIRE(!ldt.isNull());
398 }
399
BOOST_AUTO_TEST_CASE(WDateTime_testspecial_WLocalDateTime)400 BOOST_AUTO_TEST_CASE( WDateTime_testspecial_WLocalDateTime )
401 {
402 Wt::WDateTime wdt;
403
404 Wt::WLocale loc;
405 Wt::WLocalDateTime wldt = Wt::WLocalDateTime::offsetDateTime(std::chrono::system_clock::time_point{},
406 std::chrono::hours{-4},
407 loc.dateTimeFormat());
408 wldt.setDateTime(Wt::WDate(1976, 6, 14), Wt::WTime(3, 0, 0), true);
409
410 BOOST_REQUIRE(wldt.isValid());
411 BOOST_REQUIRE(!wldt.isNull());
412
413 Wt::WDateTime utc = wldt.toUTC();
414
415 std::cerr << utc.toString() << std::endl;
416 }
417