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