1 /* Copyright (c) 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
9 #include "boost/date_time/gregorian/gregorian.hpp"
10 #include "../testfrmwk.hpp"
16 #ifndef USE_DATE_TIME_PRE_1_33_FACET_IO
17 // for tests that are expected to fail and throw exceptions
18 template<class temporal_type
, class exception_type
>
19 bool failure_test(temporal_type component
,
20 const std::string
& input
,
21 exception_type
const& /*except*/,
22 boost::gregorian::date_input_facet
* facet
)
24 using namespace boost::gregorian
;
26 std::istringstream
iss(input
);
27 iss
.exceptions(std::ios_base::failbit
); // turn on exceptions
28 iss
.imbue(std::locale(std::locale::classic(), facet
));
32 catch(exception_type
& e
) {
33 std::cout
<< "Expected exception caught: \""
34 << e
.what() << "\"" << std::endl
;
35 result
= iss
.fail(); // failbit must be set to pass test
44 // for tests that are expected to fail quietly
45 template<class temporal_type
>
46 bool failure_test(temporal_type component
,
47 const std::string
& input
,
48 boost::gregorian::date_input_facet
* facet
)
50 using namespace boost::gregorian
;
51 std::istringstream
iss(input
);
52 /* leave exceptions turned off
53 * iss.exceptions(std::ios_base::failbit); */
54 iss
.imbue(std::locale(std::locale::classic(), facet
));
59 std::cout
<< "Caught unexpected exception" << std::endl
;
63 return iss
.fail(); // failbit must be set to pass test
71 #ifndef USE_DATE_TIME_PRE_1_33_FACET_IO
72 using namespace boost::gregorian
;
75 // verify no extra character are consumed
77 std::stringstream
ss("Mar.");
78 std::istreambuf_iterator
<char> sitr(ss
), str_end
;
81 f
.get(sitr
, str_end
, ss
, m
);
82 check("No extra characters consumed", m
== greg_month(Mar
) && *sitr
== '.');
85 // set up initial objects
86 date
d(not_a_date_time
);
87 days
dd(not_a_date_time
);
92 // exceptions for failure_tests
93 std::ios_base::failure
e_failure("default");
94 bad_month e_bad_month
;
96 bad_day_of_month e_bad_day_of_month
;
97 bad_weekday e_bad_weekday
;
98 bad_day_of_year e_bad_day_of_year
;
100 // default format tests: date, days, month, weekday, day, year
101 std::istringstream
iss("2005-Jan-15 21 Feb Tue 4 2002");
103 check_equal("Default format date", d
, date(2005,Jan
,15));
105 check_equal("Default (only) format positive days", dd
, days(21));
107 check_equal("Default format month", m
, greg_month(2));
109 check_equal("Default format weekday", gw
, greg_weekday(2));
111 check_equal("Default (only) format day of month", gd
, greg_day(4));
113 check_equal("Default format year", gy
, greg_year(2002));
115 check("Input Misspelled in year (date) w/exceptions",
116 failure_test(d
, "205-Jan-15", e_bad_year
, new date_input_facet()));
117 check("Input Misspelled in year (date) no-exceptions",
118 failure_test(d
, "205-Jan-15", new date_input_facet()));
119 check("Input Misspelled in month (date) w/exceptions",
120 failure_test(d
, "2005-Jsn-15", e_bad_month
, new date_input_facet()));
121 check("Input Misspelled in month (date) no-exceptions",
122 failure_test(d
, "2005-Jsn-15", new date_input_facet()));
123 check("Input Misspelled in day (date) w/exceptions",
124 failure_test(d
, "2005-Jan-51", e_bad_day_of_month
, new date_input_facet()));
125 check("Input Misspelled in day (date) no-exceptions",
126 failure_test(d
, "2005-Jan-51", new date_input_facet()));
127 check("Input Misspelled greg_weekday w/exceptions",
128 failure_test(gw
, "San", e_bad_weekday
, new date_input_facet()));
129 check("Input Misspelled greg_weekday no-exceptions",
130 failure_test(gw
, "San", new date_input_facet()));
131 check("Input Misspelled month w/exceptions",
132 failure_test(m
, "Jsn", e_bad_month
, new date_input_facet()));
133 check("Input Misspelled month no-exceptions",
134 failure_test(m
, "Jsn", new date_input_facet()));
135 check("Bad Input greg_day w/exceptions",
136 failure_test(gd
, "Sun", e_bad_day_of_month
, new date_input_facet()));
137 check("Bad Input greg_day no-exceptions",
138 failure_test(gd
, "Sun", new date_input_facet()));
139 check("Input Misspelled greg_year w/exceptions",
140 failure_test(gy
, "205", e_bad_year
, new date_input_facet()));
141 check("Input Misspelled greg_year no-exceptions",
142 failure_test(gy
, "205", new date_input_facet()));
144 // change to full length names, iso date format, and 2 digit year
145 date_input_facet
* facet
= new date_input_facet();
146 facet
->set_iso_format();
147 facet
->month_format("%B");
148 facet
->weekday_format("%A");
149 facet
->year_format("%y");
150 iss
.str("20050115 -55 February Tuesday 02");
151 iss
.imbue(std::locale(std::locale::classic(), facet
));
154 check_equal("ISO format date", d
, date(2005,Jan
,15));
156 check_equal("Default (only) format negative days", dd
, days(-55));
158 check_equal("Full format month", m
, greg_month(2));
160 check_equal("Full format weekday", gw
, greg_weekday(2));
162 check_equal("2 digit format year", gy
, greg_year(2002));
164 date_input_facet
* f1
= new date_input_facet();
165 date_input_facet
* f2
= new date_input_facet();
166 f1
->set_iso_format();
167 f2
->set_iso_format();
168 check("Missing digit(s) in ISO string", failure_test(d
,"2005071", f1
));
169 check("Missing digit(s) in ISO string",
170 failure_test(d
,"2005071", e_bad_day_of_month
, f2
));
172 { // literal % in format tests
173 date
dx(not_a_date_time
);
177 date_input_facet
* f
= new date_input_facet("%%d %Y-%b-%d");
178 std::stringstream ss
;
179 ss
.imbue(std::locale(ss
.getloc(), f
));
181 ss
.str("%d 2005-Jun-14");
183 check_equal("Literal '%' in date format", dx
, date(2005,Jun
,14));
184 f
->format("%%%d %Y-%b-%d");
185 ss
.str("%14 2005-Jun-14");
187 check_equal("Multiple literal '%'s in date format", dx
, date(2005,Jun
,14));
189 f
->month_format("%%b %b");
192 check_equal("Literal '%' in month format", mx
, greg_month(6));
193 f
->month_format("%%%b");
196 check_equal("Multiple literal '%'s in month format", mx
, greg_month(6));
198 f
->weekday_format("%%a %a");
201 check_equal("Literal '%' in weekday format", gwx
, greg_weekday(2));
202 f
->weekday_format("%%%a");
205 check_equal("Multiple literal '%'s in weekday format", gwx
, greg_weekday(2));
207 f
->year_format("%%Y %Y");
210 check_equal("Literal '%' in year format", y
, greg_year(2005));
211 f
->year_format("%%%Y");
214 check_equal("Multiple literal '%'s in year format", y
, greg_year(2005));
216 f
->year_format("%Y%");
219 check_equal("Trailing'%'s in year format", y
, greg_year(2005));
222 // All days, month, weekday, day, and year formats have been tested
223 // begin testing other date formats
224 facet
->set_iso_extended_format();
225 iss
.str("2005-01-15");
227 check_equal("ISO Extended format date", d
, date(2005,Jan
,15));
229 facet
->format("%B %d, %Y");
230 iss
.str("March 15, 2006");
232 check_equal("Custom date format: \"%B %d, %Y\" => 'March 15, 2006'",
233 d
, date(2006,Mar
,15));
235 facet
->format("%Y-%j"); // Ordinal format ISO8601(2000 sect 5.2.2.1 extended)
238 check_equal("Custom date format: \"%Y-%j\" => '2006-074'",
239 d
, date(2006,Mar
,15));
240 check("Bad input Custom date format: \"%Y-%j\" => '2006-74' (w/exceptions)",
241 failure_test(d
, "2006-74", e_bad_day_of_year
, facet
));
242 check("Bad input Custom date format: \"%Y-%j\" => '2006-74' (no exceptions)",
243 failure_test(d
, "2006-74", facet
));
247 // A date_period is constructed with an open range. So the periods
248 // [2000-07--04/2000-07-25) <-- open range
250 // [2000-07--04/2000-07-24] <-- closed range
252 date
begin(2002, Jul
, 4);
254 date_period
dp(date(2000,Jan
,1), days(1));
255 iss
.str("[2002-07-04/2002-07-24]");
256 facet
->set_iso_extended_format();
258 check_equal("Default period (closed range)", dp
, date_period(begin
,len
));
260 std::stringstream ss
;
261 date
dx(not_a_date_time
);
262 date d2
= day_clock::local_day();
265 date_period
dpx(d2
, dx
); // date/nadt
266 date_period
dp2(dx
, dx
); // nadt/nadt
267 date_period
dp3(d3
, d4
);
270 check_equal("Special values period (reversibility test)", dpx
, dp2
);
271 ss
.str("[-infinity/+infinity]");
273 check_equal("Special values period (infinities)", dp3
, dp2
);
278 period_parser
pp(period_parser::AS_OPEN_RANGE
);
279 iss
.str("[2002-07-04/2002-07-25)");
280 facet
->period_parser(pp
);
282 check_equal("Open range period", dp
, date_period(begin
,len
));
283 // custom period delimiters
284 pp
.delimiter_strings(" to ", "from ", " exclusive", " inclusive");
285 iss
.str("from 2002-07-04 to 2002-07-25 exclusive");
286 facet
->period_parser(pp
);
288 check_equal("Open range period - custom delimiters", dp
, date_period(begin
,len
));
289 pp
.range_option(period_parser::AS_CLOSED_RANGE
);
290 iss
.str("from 2002-07-04 to 2002-07-24 inclusive");
291 facet
->period_parser(pp
);
293 check_equal("Closed range period - custom delimiters", dp
, date_period(begin
,len
));
296 // date_generator tests
298 // date_generators use formats contained in the
299 // date_input_facet for weekdays and months
300 // reset month & weekday formats to defaults
301 facet
->month_format("%b");
302 facet
->weekday_format("%a");
304 partial_date
pd(1,Jan
);
305 nth_kday_of_month
nkd(nth_kday_of_month::first
, Sunday
, Jan
);
306 first_kday_of_month
fkd(Sunday
, Jan
);
307 last_kday_of_month
lkd(Sunday
, Jan
);
308 first_kday_before
fkb(Sunday
);
309 first_kday_after
fka(Sunday
);
310 // using default date_generator_parser "nth_strings"
313 // Feb-29 is a valid date_generator, get_date() will fail in a non-leap year
314 check_equal("Default strings, partial_date",
315 pd
.get_date(2004), date(2004,Feb
,29));
316 iss
.str("second Mon of Mar");
318 check_equal("Default strings, nth_day_of_the_week_in_month",
319 nkd
.get_date(2004), date(2004,Mar
,8));
320 iss
.str("first Tue of Apr");
322 check_equal("Default strings, first_day_of_the_week_in_month",
323 fkd
.get_date(2004), date(2004,Apr
,6));
324 iss
.str("last Wed of May");
326 check_equal("Default strings, last_day_of_the_week_in_month",
327 lkd
.get_date(2004), date(2004,May
,26));
328 iss
.str("Thu before");
330 check_equal("Default strings, first_day_of_the_week_before",
331 fkb
.get_date(date(2004,Feb
,8)), date(2004,Feb
,5));
332 iss
.str("Fri after");
334 check_equal("Default strings, first_day_of_the_week_after",
335 fka
.get_date(date(2004,Feb
,1)), date(2004,Feb
,6));
337 check("Incorrect elements (date_generator) w/exceptions", // after/before type mixup
338 failure_test(fkb
, "Fri after", e_failure
, new date_input_facet()));
339 check("Incorrect elements (date_generator) no exceptions", // after/before type mixup
340 failure_test(fkb
, "Fri after", new date_input_facet()));
341 check("Incorrect elements (date_generator) w/exceptions", // first/last type mixup
342 failure_test(lkd
, "first Tue of Apr", e_failure
, new date_input_facet()));
343 check("Incorrect elements (date_generator) no exceptions", // first/last type mixup
344 failure_test(lkd
, "first Tue of Apr", new date_input_facet()));
345 check("Incorrect elements (date_generator) w/exceptions", // 'in' is wrong
346 failure_test(nkd
, "second Mon in Mar", e_failure
, new date_input_facet()));
347 check("Incorrect elements (date_generator) no exceptions", // 'in' is wrong
348 failure_test(nkd
, "second Mon in Mar", new date_input_facet()));
350 // date_generators - custom element strings
351 facet
->date_gen_element_strings("1st","2nd","3rd","4th","5th","final","prior to","past","in");
352 iss
.str("3rd Sat in Jul");
354 check_equal("Custom strings, nth_day_of_the_week_in_month",
355 nkd
.get_date(2004), date(2004,Jul
,17));
356 iss
.str("1st Wed in May");
358 check_equal("Custom strings, first_day_of_the_week_in_month",
359 fkd
.get_date(2004), date(2004,May
,5));
360 iss
.str("final Tue in Apr");
362 check_equal("Custom strings, last_day_of_the_week_in_month",
363 lkd
.get_date(2004), date(2004,Apr
,27));
364 iss
.str("Fri prior to");
366 check_equal("Custom strings, first_day_of_the_week_before",
367 fkb
.get_date(date(2004,Feb
,8)), date(2004,Feb
,6));
370 check_equal("Custom strings, first_day_of_the_week_after",
371 fka
.get_date(date(2004,Feb
,1)), date(2004,Feb
,5));
373 // date_generators - special case with empty element string
374 /* Doesn't work. Empty string returns -1 from string_parse_tree
375 * because it attempts to match the next set of characters in the
376 * stream to the wrong element. Ex. It attempts to match "Mar" to
377 * the 'of' element in the test below.
379 facet->date_gen_element_strings("1st","2nd","3rd","4th","5th","final","prior to","past",""); // the 'of' string is an empty string
380 iss.str("final Mon Mar");
382 check_equal("Special case, empty element string",
383 lkd.get_date(2005), date(2005,Mar,28));
387 // special values tests (date and days only)
388 iss
.str("minimum-date-time +infinity");
391 check_equal("Special values, default strings, min_date_time date",
392 d
, date(min_date_time
));
393 check_equal("Special values, default strings, pos_infin days",
394 dd
, days(pos_infin
));
395 iss
.str("-infinity maximum-date-time");
398 check_equal("Special values, default strings, neg_infin date",
400 check_equal("Special values, default strings, max_date_time days",
401 dd
, days(max_date_time
));
402 iss
.str("not-a-date-time");
404 check_equal("Special values, default strings, not_a_date_time date",
405 d
, date(not_a_date_time
));
407 // in addition check that special_value_from_string also works correctly for other special values
408 check_equal("Special values, default strings, not_special test",
409 special_value_from_string("not_special"), not_special
);
410 check_equal("Special values, default strings, junk test",
411 special_value_from_string("junk"), not_special
);
413 // special values custom, strings
414 special_values_parser
svp("NADT", "MINF", "INF", "MINDT", "MAXDT");
415 facet
->special_values_parser(svp
);
416 iss
.str("MINDT INF");
419 check_equal("Special values, custom strings, min_date_time date",
420 d
, date(min_date_time
));
421 check_equal("Special values, custom strings, pos_infin days",
422 dd
, days(pos_infin
));
423 iss
.str("MINF MAXDT");
426 check_equal("Special values, custom strings, neg_infin date",
428 check_equal("Special values, custom strings, max_date_time days",
429 dd
, days(max_date_time
));
432 check_equal("Special values, custom strings, not_a_date_time days",
433 dd
, days(not_a_date_time
));
435 check("Misspelled input, special_value date w/exceptions",
436 failure_test(d
, "NSDT", e_bad_year
, new date_input_facet()));
437 check("Misspelled input, special_value date no exceptions",
438 failure_test(d
, "NSDT", new date_input_facet()));
439 check("Misspelled input, special_value days w/exceptions",
440 failure_test(dd
, "NSDT", e_failure
, new date_input_facet()));
441 check("Misspelled input, special_value days no exceptions",
442 failure_test(dd
, "NSDT", new date_input_facet()));
445 // German names. Please excuse any errors, I don't speak German and
446 // had to rely on an on-line translation service.
447 // These tests check one of each (at least) from all sets of custom strings
449 // create a custom format_date_parser
450 std::string m_a
[] = {"Jan","Feb","Mar","Apr","Mai",
451 "Jun","Jul","Aug","Sep","Okt","Nov","Dez"};
452 std::string m_f
[] = {"Januar","Februar","Marz","April",
453 "Mai","Juni","Juli","August",
454 "September","Oktober","November","Dezember"};
455 std::string w_a
[] = {"Son", "Mon", "Die","Mit", "Don", "Fre", "Sam"};
456 std::string w_f
[] = {"Sonntag", "Montag", "Dienstag","Mittwoch",
457 "Donnerstag", "Freitag", "Samstag"};
458 typedef boost::date_time::format_date_parser
<date
, char> date_parser
;
459 date_parser::input_collection_type months_abbrev
;
460 date_parser::input_collection_type months_full
;
461 date_parser::input_collection_type wkdays_abbrev
;
462 date_parser::input_collection_type wkdays_full
;
463 months_abbrev
.assign(m_a
, m_a
+12);
464 months_full
.assign(m_f
, m_f
+12);
465 wkdays_abbrev
.assign(w_a
, w_a
+7);
466 wkdays_full
.assign(w_f
, w_f
+7);
467 date_parser
d_parser("%B %d %Y",
468 months_abbrev
, months_full
,
469 wkdays_abbrev
, wkdays_full
);
471 // create a special_values parser
472 special_values_parser
sv_parser("NichtDatumzeit",
473 "Negativ Unendlichkeit",
474 "Positiv Unendlichkeit",
478 // create a period_parser
479 period_parser p_parser
; // default will do
480 // create date_generator_parser
481 typedef boost::date_time::date_generator_parser
<date
,char> date_gen_parser
;
482 date_gen_parser
dg_parser("Zuerst","Zweitens","Dritt","Viert",
483 "F\xC3\xBCnft","Letzt","Vor","Nach","Von");
485 // create the date_input_facet
486 date_input_facet
* de_facet
=
487 new date_input_facet("%B %d %Y",
492 std::istringstream iss2
;
493 iss2
.imbue(std::locale(std::locale::classic(), de_facet
));
494 // June 06 2005, Dec, minimum date, Tues
495 iss2
.str("Juni 06 2005 Dez Wenigstes Datum Die");
498 check_equal("German names: date", d
, date(2005, Jun
, 6));
499 check_equal("German names: month", m
, greg_month(Dec
));
502 check_equal("German names: special value date", d
, date(min_date_time
));
503 check_equal("German names: short weekday", gw
, greg_weekday(Tuesday
));
504 de_facet
->weekday_format("%A"); // long weekday
505 // Tuesday, Second Tuesday of Mar
506 iss2
.str("Dienstag Zweitens Dienstag von Mar");
509 check_equal("German names: long weekday", gw
, greg_weekday(Tuesday
));
510 check_equal("German names, nth_day_of_the_week_in_month",
511 nkd
.get_date(2005), date(2005,Mar
,8));
513 iss2
.str("Dienstag Nach");
515 check_equal("German names, first_day_of_the_week_after",
516 fka
.get_date(date(2005,Apr
,5)), date(2005,Apr
,12));
520 // test name replacement functions
522 // collections for adding to facet
523 const char* const month_short_names
[]={"*jan*","*feb*","*mar*",
524 "*apr*","*may*","*jun*",
525 "*jul*","*aug*","*sep*",
526 "*oct*","*nov*","*dec*"};
527 const char* const month_long_names
[]={"**January**","**February**","**March**",
528 "**April**","**May**","**June**",
529 "**July**","**August**","**September**",
530 "**October**","**November**","**December**"};
531 const char* const weekday_short_names
[]={"day1", "day2","day3","day4",
532 "day5","day6","day7"};
533 const char* const weekday_long_names
[]= {"Sun-0", "Mon-1", "Tue-2",
537 std::vector
<std::basic_string
<char> > short_weekday_names
;
538 std::vector
<std::basic_string
<char> > long_weekday_names
;
539 std::vector
<std::basic_string
<char> > short_month_names
;
540 std::vector
<std::basic_string
<char> > long_month_names
;
542 std::copy(&weekday_short_names
[0],
543 &weekday_short_names
[7],
544 std::back_inserter(short_weekday_names
));
545 std::copy(&weekday_long_names
[0],
546 &weekday_long_names
[7],
547 std::back_inserter(long_weekday_names
));
548 std::copy(&month_short_names
[0],
549 &month_short_names
[12],
550 std::back_inserter(short_month_names
));
551 std::copy(&month_long_names
[0],
552 &month_long_names
[12],
553 std::back_inserter(long_month_names
));
555 date
dx(not_a_date_time
);
556 date_input_facet
* facetx
= new date_input_facet();
557 std::stringstream ss
;
558 ss
.imbue(std::locale(std::locale::classic(), facetx
));
559 facetx
->short_month_names(short_month_names
);
560 facetx
->short_weekday_names(short_weekday_names
);
561 facetx
->long_month_names(long_month_names
);
562 facetx
->long_weekday_names(long_weekday_names
);
563 facetx
->format("%a %b %d, %Y");
564 ss
.str("day7 *apr* 23, 2005");
566 check_equal("Short custom names, set via accessor function", dx
.day_of_week(), greg_weekday(6));
567 check_equal("Short custom names, set via accessor function", dx
.month(), greg_month(4));
569 ss
.str("Sun-0 **April** 24, 2005");
570 facetx
->format("%A %B %d, %Y");
572 check_equal("Long custom names, set via accessor function", dx
.day_of_week(), greg_weekday(0));
573 check_equal("Long custom names, set via accessor function", dx
.month(), greg_month(4));
577 check("This test is a nop for platforms with USE_DATE_TIME_PRE_1_33_FACET_IO",
580 return printTestStats();