1 | /* Copyright (c) 2002,2003,2005 CrystalClear Software, Inc. |
2 | * Use, modification and distribution is subject to the |
3 | * Boost Software License, Version 1.0. (See accompanying |
4 | * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) |
5 | * Author: Jeff Garland, Bart Garst |
6 | */ |
7 | |
8 | #include "boost/date_time/gregorian/gregorian.hpp" |
9 | #include "../testfrmwk.hpp" |
10 | #include "boost/lexical_cast.hpp" |
11 | #include <iostream> |
12 | #include <string> |
13 | |
14 | // missing or misspelled parts of date string tests |
15 | // 'output_str' will be overwritten with what() from caught exception |
16 | bool failure_tests(std::string date_spec, |
17 | std::string& output_str) |
18 | { |
19 | using namespace boost::gregorian; |
20 | bool result = false; |
21 | date d(not_a_date_time); |
22 | try { |
23 | d = from_simple_string(s: date_spec); |
24 | } |
25 | catch(bad_year& by){ // ex: "205-Jan-15" |
26 | result = true; |
27 | output_str = by.what(); |
28 | } |
29 | catch(bad_month& bm){ // ex: "2005-Jsn-15" |
30 | result = true; |
31 | output_str = bm.what(); |
32 | } |
33 | catch(bad_day_of_month& bd){ // ex: "2005-Jan-51" |
34 | result = true; |
35 | output_str = bd.what(); |
36 | } |
37 | catch(...){ |
38 | // test failed - unexpected exception, leave result set to false |
39 | } |
40 | return result; |
41 | } |
42 | |
43 | int |
44 | main() |
45 | { |
46 | |
47 | |
48 | // Examples from 8601 |
49 | // Full date |
50 | // Extended CCYY-MM-DD |
51 | std::string s("2001-10-5" ); |
52 | |
53 | //This one aborts gcc2.95.3 on mandrake 8.1 linux with |
54 | //bad lexical cast? |
55 | try { |
56 | boost::gregorian::date d(boost::gregorian::from_string(s)); |
57 | check(testname: "check year" , testcond: d.year() == 2001); |
58 | check(testname: "check month" , testcond: d.month() == 10); |
59 | check(testname: "check day" , testcond: d.day() == 5); |
60 | } |
61 | catch(std::exception& e) { |
62 | check(testname: "parse 2001-10-5" , testcond: false); |
63 | std::cout << "Fail: " << e.what() << std::endl; |
64 | } |
65 | |
66 | { |
67 | using namespace boost::gregorian; |
68 | // date objects from strings & strings to date objects |
69 | date d(2000, 2, 29); |
70 | date d2 = from_string(s: "2000-2-29" ); |
71 | check(testname: "2000-2-29" , testcond: d2 == d); |
72 | date d3 = from_string(s: "2000-FEB-29" ); |
73 | check(testname: "2000-FEB-29 (uppercase)" , testcond: d3 == d); |
74 | date d4 = from_string(s: "2000-february-29" ); |
75 | check(testname: "2000-february-29 (lowercase)" , testcond: d4 == d); |
76 | date d5 = from_string(s: to_simple_string(d)); |
77 | check(testname: "date to string to date" , testcond: d5 == d); |
78 | date d6 = from_string(s: to_iso_extended_string(d)); |
79 | check(testname: "date to string to date" , testcond: d6 == d); |
80 | date d7 = from_us_string(s: "Feb-29-2000" ); |
81 | check(testname: "date from month-day-year string" , testcond: d7 == d); |
82 | date d8 = from_uk_string(s: "29-Feb-2000" ); |
83 | check(testname: "date from day-month-year string" , testcond: d8 == d); |
84 | { |
85 | std::string sn("20050229" ); // no Feb-29 in 2005 |
86 | date dn(not_a_date_time); |
87 | try { |
88 | dn = date_from_iso_string(s: sn); |
89 | check(testname: "Expected exception not thrown: from ISO string (bad_day_of_month)" , testcond: false); |
90 | std::cout << date_from_iso_string(s: sn) << std::endl; |
91 | } |
92 | catch(bad_day_of_month&) { |
93 | check(testname: "Caught expected exception: bad_day_of_month " , testcond: true); |
94 | } |
95 | catch(...) { |
96 | check(testname: "Caught unexpected exception" , testcond: false); |
97 | } |
98 | /* not currently passing due to a bug in boost::offset_separator (reported 2005-Aug-02) |
99 | sn = "2005022"; // missing a digit |
100 | try { |
101 | d = date_from_iso_string(sn); |
102 | check("Expected exception not thrown: from ISO string (missing digit)", false); |
103 | std::cout << date_from_iso_string(sn) << std::endl; |
104 | } |
105 | catch(bad_day_of_month& e) { |
106 | check("Caught expected exception: bad_day_of_month ", true); |
107 | } |
108 | catch(...) { |
109 | check("Caught unexpected exception", false); |
110 | } |
111 | */ |
112 | sn = "20050228" ; // now it's correct |
113 | dn = date_from_iso_string(s: sn); |
114 | check(testname: "from ISO string" , testcond: date(2005,Feb,28) == dn); |
115 | } |
116 | |
117 | date d9 = from_us_string(__DATE__); |
118 | std::cout << "Today's date: " << to_simple_string(d: d9) << std::endl; |
119 | date d10 = from_us_string(s: "Feb 29, 2000" ); |
120 | std::cout << "With comma: " << to_simple_string(d: d10) << std::endl; |
121 | check(testname: "american date with comma: Feb 29, 2000 " , testcond: d10 == d); |
122 | |
123 | date d11 = from_us_string(s: "feb 29 2000" ); |
124 | check(testname: "american date with comma: feb 29 2000 " , testcond: d11 == d); |
125 | |
126 | // test for missing or misspelled date spec components |
127 | std::string output_str("unexpected exception caught" ); |
128 | check(testname: "Year misspelled/out of range: " + output_str, |
129 | testcond: failure_tests(date_spec: "205-Jan-15" , output_str)); |
130 | output_str = "unexpected exception caught" ; |
131 | check(testname: "Month misspelled: " + output_str, |
132 | testcond: failure_tests(date_spec: "2005-Jsn-15" , output_str)); |
133 | output_str = "unexpected exception caught" ; |
134 | check(testname: "Day out of range: " + output_str, |
135 | testcond: failure_tests(date_spec: "2005-Jan-55" , output_str)); |
136 | output_str = "unexpected exception caught" ; |
137 | check(testname: "Missing month and day: " + output_str, |
138 | testcond: failure_tests(date_spec: "2005" , output_str)); |
139 | output_str = "unexpected exception caught" ; |
140 | check(testname: "Missing day: " + output_str, |
141 | testcond: failure_tests(date_spec: "2005-Jan" , output_str)); |
142 | |
143 | |
144 | #if defined(BOOST_DATE_TIME_NO_LOCALE) || defined(BOOST_NO_STD_ITERATOR_TRAITS) || !defined(USE_DATE_TIME_PRE_1_33_FACET_IO) |
145 | |
146 | //TODO -- all these PRE_1_33 exclusions need to be removed. In the meantime, don't make |
147 | //this stuff fail. |
148 | #if defined(USE_DATE_TIME_PRE_1_33_FACET_IO) |
149 | check("input streaming for date not available" , false); // force a failure |
150 | #endif |
151 | #else |
152 | { |
153 | std::stringstream ss("2000-2-29" ); |
154 | ss >> d2; |
155 | check("2000-2-29 stream-in" , d2 == d); |
156 | } |
157 | { |
158 | std::stringstream ss("2000-FEB-29" ); |
159 | ss >> d2; |
160 | //std::cout << d2 << std::endl; |
161 | check("2000-FEB-29 stream-in (uppercase)" , d2 == d); |
162 | } |
163 | { |
164 | std::stringstream ss("2000-february-29" ); |
165 | ss >> d2; |
166 | check("2000-february-29 stream-in (lowercase)" , d2 == d); |
167 | } |
168 | // the removed (3) tests require a stream manipulator for date_order |
169 | // and date_separator (not yet implemented) |
170 | /*{ |
171 | std::stringstream ss("Feb-29-2000"); |
172 | ss >> d2; |
173 | check("date from month-day-year string stream-in", d2 == d); |
174 | } |
175 | { |
176 | std::stringstream ss("29-Feb-2000"); |
177 | ss >> d2; |
178 | check("date from day-month-year string stream-in", d2 == d); |
179 | } |
180 | { |
181 | std::stringstream ss("Feb 29, 2000"); |
182 | ss >> d2; |
183 | check("american date with comma: Feb 29, 2000 stream-in", d2 == d); |
184 | }*/ |
185 | #endif //BOOST_DATE_TIME_NO_LOCALE |
186 | |
187 | |
188 | |
189 | // check proper range |
190 | d = date(2001, 1, 1); |
191 | d2 = from_string(s: "2001-Jan-1" ); |
192 | d3 = from_string(s: "2001-January-1" ); |
193 | check(testname: "January" , testcond: d == d2); |
194 | check(testname: "January" , testcond: d == d3); |
195 | d = date(2001, 12, 1); |
196 | d2 = from_string(s: "2001-Dec-1" ); |
197 | d3 = from_string(s: "2001-December-1" ); |
198 | check(testname: "December" , testcond: d == d2); |
199 | check(testname: "December" , testcond: d == d3); |
200 | #if defined(BOOST_NO_STD_ITERATOR_TRAITS) |
201 | check("date from stream not available: no std iterator traits" , false); |
202 | #else |
203 | // from stream |
204 | d = date(2000, 10, 31); |
205 | std::stringstream ss("" ); |
206 | ss << "2000-Oct-31 is Halloween 2k!" ; |
207 | std::istream_iterator<std::string> iter(ss), eos; |
208 | check(testname: "from stream - stringstream" , testcond: d == from_stream(beg: iter, end: eos)); |
209 | #if !(defined(BOOST_NO_STD_WSTRING)) |
210 | #if !(defined(BOOST_DATE_TIME_NO_WISTREAM_ITERATOR)) |
211 | std::wstringstream ws; |
212 | ws << "2000-Oct-31 is Halloween 2k!" ; |
213 | std::istream_iterator<std::wstring, wchar_t> witer(ws), weos; |
214 | check(testname: "from stream - wstringstream" , testcond: d == from_stream(beg: witer, end: weos)); |
215 | #endif // NO_WSTREAM_ITERATOR |
216 | #endif // BOOST_NO_WSTRING |
217 | char d2_string[] = {"2000-10-31 is Halloween 2k!" }; |
218 | char* end = d2_string + sizeof(d2_string) - 1; |
219 | check(testname: "from stream - char[]" , testcond: d == from_stream(beg: d2_string, end)); |
220 | |
221 | std::string s1_string("2000-Oct-31 is Halloween 2k!" ); |
222 | std::string::iterator s1_start = s1_string.begin(); |
223 | std::string::iterator s1_end = s1_string.end(); |
224 | check(testname: "from stream - string" , testcond: d == from_stream(beg: s1_start, end: s1_end)); |
225 | #ifndef BOOST_NO_STD_WSTRING |
226 | std::wstring w1_string(boost::lexical_cast<std::wstring>(arg: "2000-Oct-31 is Halloween 2k!" )); |
227 | std::wstring::iterator w1_start = w1_string.begin(); |
228 | std::wstring::iterator w1_end = w1_string.end(); |
229 | check(testname: "from stream - wstring" , testcond: d == from_stream(beg: w1_start, end: w1_end)); |
230 | #endif // BOOST_NO_STD_WSTRING |
231 | #endif // BOOST_NO_STD_ITERATOR_TRAITS |
232 | /* date objects from strings & strings to date objects |
233 | * with misspelled months */ |
234 | try { |
235 | date bd = from_string(s: "2002-Jull-4" ); |
236 | std::cout << "Shouldn't be reached." << |
237 | boost::gregorian::to_simple_string(d: bd) << std::endl; |
238 | } |
239 | catch(boost::gregorian::bad_month&){ |
240 | check(testname: "bad spelling 'Jull'" , testcond: true); |
241 | } |
242 | catch(std::exception& e){ |
243 | check(testname: "bad spelling" , testcond: false); |
244 | std::cout << "Fail: " << e.what() << std::endl; |
245 | } |
246 | } |
247 | |
248 | |
249 | try { |
250 | std::string s2("2001-12-41" ); //oops should be 31 |
251 | boost::gregorian::date bad_day(boost::gregorian::from_string(s: s2)); //won't construct |
252 | check(testname: "check bad day" , testcond: false); |
253 | //The line below won't execute, but make the compiler think |
254 | //we are using bad day.... |
255 | std::cout << "Oh oh, this shouldn't be reached: " |
256 | << boost::gregorian::to_iso_string(d: bad_day) << std::endl; |
257 | |
258 | } |
259 | catch(boost::gregorian::bad_day_of_month&) { //expected |
260 | check(testname: "check bad day" , testcond: true); |
261 | } |
262 | catch(std::exception& e) { |
263 | //oops wrong exception |
264 | check(testname: "check bad day" , testcond: false); |
265 | std::cout << "Fail: " << e.what() << std::endl; |
266 | } |
267 | |
268 | try { |
269 | std::string s2("2001-02-29" ); //oops should be 28 |
270 | boost::gregorian::date bad_day(boost::gregorian::from_string(s: s2)); //won't construct |
271 | check(testname: "check bad leap year" , testcond: false); |
272 | //The line below won't execute, but make the compiler think |
273 | //we are using bad day.... |
274 | std::cout << "Oh oh, this shouldn't be reached: " |
275 | << boost::gregorian::to_iso_string(d: bad_day) << std::endl; |
276 | |
277 | } |
278 | catch(boost::gregorian::bad_day_of_month&) { //expected |
279 | check(testname: "check bad leap year" , testcond: true); |
280 | } |
281 | catch(std::exception& e) { |
282 | //oops wrong exception |
283 | check(testname: "check bad leap year" , testcond: false); |
284 | std::cout << "Fail: " << e.what() << std::endl; |
285 | } |
286 | |
287 | try { |
288 | std::string s2("2001-14-1" ); //oops should be <= 12 |
289 | boost::gregorian::date bad_month(boost::date_time::parse_date<boost::gregorian::date>(s: s2)); |
290 | check(testname: "check bad month" , testcond: false); //fail the test |
291 | //The line below won't execute, but make the compiler think |
292 | //we are using bad day.... |
293 | std::cout << "Oh oh, this shouldn't be reached: " |
294 | << boost::gregorian::to_iso_string(d: bad_month) << std::endl; |
295 | |
296 | } |
297 | catch(boost::gregorian::bad_month&) { //expected |
298 | check(testname: "check bad month" , testcond: true); |
299 | } |
300 | catch(std::exception& e) { |
301 | //oops wrong exception |
302 | check(testname: "check bad month" , testcond: false); |
303 | std::cout << "Fail: " << e.what() << std::endl; |
304 | } |
305 | |
306 | //This one aborts gcc2.95.3 on mandrake 8.1 linux with |
307 | //bad lexical cast? |
308 | try { |
309 | //Example of ISO Standard -- CCYYMMDD |
310 | using namespace boost::gregorian; |
311 | std::string ud("20011009" ); //2001-Oct-09 |
312 | date d1(boost::gregorian::from_undelimited_string(s: ud)); |
313 | // std::cout << to_string(d1) << std::endl; |
314 | check(testname: "undelimited date string" , testcond: d1 == date(2001,Oct,9)); |
315 | |
316 | |
317 | std::string ad("2001/10/09" ); |
318 | date d2(boost::date_time::parse_date<date>(s: ad)); |
319 | check(testname: "check american date" , testcond: d2 == date(2001,Oct,9)); |
320 | } |
321 | catch(std::exception& e) { |
322 | check(testname: "more parsing" , testcond: false); |
323 | std::cout << "Fail: " << e.what() << std::endl; |
324 | } |
325 | |
326 | using namespace boost::gregorian; |
327 | std::string s2("2003-07-28" ); |
328 | date d2(from_string(s: s2)); |
329 | check(testname: "check date" , testcond: d2.month() == 7 && |
330 | d2.year() == 2003 && |
331 | d2.day() == 28); |
332 | // std::string s1("2001-Oct-5"); |
333 | // gregorian::date d1(parse_date<gregorian::date>(s1)); |
334 | // check("check month", d1.month() == 10); |
335 | |
336 | |
337 | //Check that the from_string and to_string can be reversed |
338 | date d10(2003, 10, 19); |
339 | std::string d10s = to_simple_string(d: d10); |
340 | date d11 = from_simple_string(s: d10s); |
341 | check(testname: "to from string inversion" , testcond: d10 == d11); |
342 | |
343 | try { |
344 | using namespace boost::gregorian; |
345 | std::string ud("" ); //empty string error sf bug# 1155556 |
346 | date d1(from_simple_string(s: ud)); |
347 | check(testname: "empty string" , testcond: false); //should never reach this piont |
348 | (void)d1; |
349 | } |
350 | catch(std::exception& e) { |
351 | check(testname: std::string("empty string parse (exception expected): " ) + e.what(), testcond: true); |
352 | } |
353 | |
354 | |
355 | //Calendar Week + Day Number |
356 | // CCYYWwwDThhmmss |
357 | // 1986W105T |
358 | // week == 10 day=5 |
359 | // see page 5 |
360 | |
361 | |
362 | //Duration rep |
363 | //CCYYMMDDThhmmss/PnYnMnDTnHnMnS |
364 | |
365 | return printTestStats(); |
366 | |
367 | } |
368 | |
369 | |