/* Copyright (c) 2005 CrystalClear Software, Inc. * Use, modification and distribution is subject to the * Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) * Author: Jeff Garland, Bart Garst * $Date$ */ #include "boost/date_time/gregorian/gregorian.hpp" #include "boost/date_time/posix_time/posix_time.hpp" #include "../testfrmwk.hpp" #include #include #include #include // for tests that are expected to fail and throw exceptions template bool failure_test(temporal_type component, const std::string& input, exception_type const& /*except*/, boost::posix_time::time_input_facet* facet) { using namespace boost::posix_time; bool result = false; std::istringstream iss(input); iss.exceptions(std::ios_base::failbit); // turn on exceptions iss.imbue(std::locale(std::locale::classic(), facet)); try { iss >> component; } catch(exception_type& e) { std::cout << "Expected exception caught: \"" << e.what() << "\"" << std::endl; result = iss.fail(); // failbit must be set to pass test } catch(...) { result = false; } return result; } // for tests that are expected to fail quietly template bool failure_test(temporal_type& component, const std::string& input, boost::posix_time::time_input_facet* facet) { using namespace boost::posix_time; std::istringstream iss(input); /* leave exceptions turned off * iss.exceptions(std::ios_base::failbit); */ iss.imbue(std::locale(std::locale::classic(), facet)); try { iss >> component; } catch(...) { std::cout << "Caught unexpected exception" << std::endl; return false; } return iss.fail(); // failbit must be set to pass test } using namespace boost::gregorian; using namespace boost::posix_time; void do_all_tests() { #if defined(USE_DATE_TIME_PRE_1_33_FACET_IO) // skip this file check("No tests run for this compiler. Incompatible IO", true); #else // set up initial objects time_duration td = hours(0); ptime pt(not_a_date_time); time_period tp(pt, td); // exceptions for failure_tests std::ios_base::failure e_failure("default"); /* test ptime, time_duration, time_period. * test all formats. * test for bad input. * test special values. * do not test custom names (done in gregorian). * * format flags to test H,M,S,s,F,f,j */ // default format tests: time_duration, ptime std::istringstream iss("09:59:01.321987654321 2005-Jan-15 10:15:03.123456789123"); iss >> td; iss >> pt; #if defined(BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG) check_equal("Default format time_duration", td, time_duration(9,59,1,321987654)); check_equal("Default format ptime", pt, ptime(date(2005,01,15),time_duration(10,15,3,123456789))); #else check_equal("Default format time_duration", td, time_duration(9,59,1,321987)); check_equal("Default format ptime", pt, ptime(date(2005,01,15),time_duration(10,15,3,123456))); #endif // test all flags that appear in time_input_facet iss.str("12:34:56 2005-Jan-15 12:34:56"); iss >> td; iss >> pt; check_equal("Default format time_duration no frac_sec", td, time_duration(12,34,56)); // the following test insures %F parsing stops at the appropriate point check_equal("Default format ptime", pt, ptime(date(2005,01,15),time_duration(12,34,56))); iss.str("14:13:12 extra stuff"); // using default %H:%M:%S%F format iss >> td; check_equal("Default frac_sec format time_duration", td, time_duration(14,13,12)); time_input_facet* facet = new time_input_facet(); std::locale loc(std::locale::classic(), facet); facet->time_duration_format("%H:%M:%S""%f"); iss.imbue(loc); iss.str("14:13:12.0 extra stuff"); iss >> td; check_equal("Required-frac_sec format time_duration", td, time_duration(14,13,12)); iss.str("12"); facet->time_duration_format("%H"); iss >> td; check_equal("Hours format", td, hours(12)); iss.str("05"); facet->time_duration_format("%M"); iss >> td; check_equal("Minutes format", td, minutes(5)); iss.str("45"); facet->time_duration_format("%S"); iss >> td; check_equal("Seconds w/o frac_sec format", td, seconds(45)); iss.str("10.01"); facet->time_duration_format("%s"); iss >> td; #if defined(BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG) check_equal("Seconds w/ frac_sec format", td, time_duration(0,0,10,10000000)); #else check_equal("Seconds w/ frac_sec format", td, time_duration(0,0,10,10000)); #endif iss.str("2005-105T23:59"); facet->format("%Y-%jT%H:%M"); // extended ordinal format iss >> pt; check_equal("Extended Ordinal format", pt, ptime(date(2005,4,15),time_duration(23,59,0))); /* this is not implemented yet. The flags: %I & %p are not parsed iss.str("2005-Jun-14 03:15:00 PM"); facet->format("%Y-%b-%d %I:%M:%S %p"); iss >> pt; check_equal("12 hour time format (AM/PM)", pt, ptime(date(2005,6,14),time_duration(15,15,0))); */ iss.str("2005-Jun-14 15:15:00 %d"); facet->format("%Y-%b-%d %H:%M:%S %%d"); iss >> pt; check_equal("Literal '%' in format", pt, ptime(date(2005,6,14),time_duration(15,15,0))); iss.str("15:15:00 %d"); facet->time_duration_format("%H:%M:%S %%d"); iss >> td; check_equal("Literal '%' in time_duration format", td, time_duration(15,15,0)); iss.str("2005-Jun-14 15:15:00 %14"); facet->format("%Y-%b-%d %H:%M:%S %%%d"); // %% => % & %d => day_of_month iss >> pt; check_equal("Multiple literal '%'s in format", pt, ptime(date(2005,6,14),time_duration(15,15,0))); iss.str("15:15:00 %15"); facet->time_duration_format("%H:%M:%S %%%M"); iss >> td; check_equal("Multiple literal '%'s in time_duration format", td, time_duration(15,15,0)); { /****** iso format tests (and custom 'scrunched-together formats) ******/ time_input_facet *facet2 = new time_input_facet(); facet2->set_iso_format(); facet2->time_duration_format("%H""%M""%S""%F"); // iso format std::stringstream ss; ss.imbue(std::locale(std::locale::classic(), facet2)); ptime pt2(not_a_date_time); time_duration tdx(not_a_date_time); date dx(2002,Oct,17); #if defined(BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG) time_duration td2(23,12,17,123450000); #else time_duration td2(23,12,17,123450); #endif ptime result(dx, td2); ss.str("20021017T231217.12345"); ss >> pt2; check_equal("iso_format ptime", pt2, result); ss.str(""); facet2->set_iso_extended_format(); ss.str("2002-10-17 23:12:17.12345"); ss >> pt2; check_equal("iso_extended_format ptime", pt2, result); ss.str(""); ss.str("231217.12345"); ss >> tdx; check_equal("iso_format time_duration", tdx, td2); ss.str(""); ss.str("-infinity"); ss >> tdx; check_equal("iso_format time_duration (special_value)", tdx, time_duration(neg_infin)); ss.str(""); // the above tests prove correct parsing of time values in these formats. // these tests show they also handle special_values & exceptions properly time_duration nadt(not_a_date_time); ss.exceptions(std::ios_base::failbit); // we need exceptions turned on here int count = 0; try { facet2->time_duration_format("%H""%M""%S""%F"); ss.str("not-a-date-time"); ++count; ss >> tdx; check_equal("special value w/ hours flag", tdx, nadt); ss.str(""); facet2->time_duration_format("%M""%S""%F"); ss.str("not-a-date-time"); ++count; ss >> tdx; check_equal("special value w/ minutes flag", tdx, nadt); ss.str(""); facet2->time_duration_format("%S""%F"); ss.str("not-a-date-time"); ++count; ss >> tdx; check_equal("special value w/ seconds flag", tdx, nadt); ss.str(""); facet2->time_duration_format("%s"); ss.str("not-a-date-time"); ++count; ss >> tdx; check_equal("special value w/ sec w/frac_sec (always) flag", tdx, nadt); ss.str(""); facet2->time_duration_format("%f"); ss.str("not-a-date-time"); ++count; ss >> tdx; check_equal("special value w/ frac_sec (always) flag", tdx, nadt); ss.str(""); } catch(...) { // any exception is a failure std::stringstream msg; msg << "special_values with scrunched formats failed at test" << count; check(msg.str(), false); } // exception tests std::ios_base::failure exc("failure"); check("failure test w/ hours flag", failure_test(td, "bad_input", exc, new time_input_facet("%H""%M""%S""%F"))); check("silent failure test w/ hours flag", failure_test(td, "bad_input", new time_input_facet("%H""%M""%S""%F"))); check("failure test w/ minute flag", failure_test(td, "bad_input", exc, new time_input_facet("%M""%S""%F"))); check("silent failure test w/ minute flag", failure_test(td, "bad_input", new time_input_facet("%M""%S""%F"))); check("failure test w/ second flag", failure_test(td, "bad_input", exc, new time_input_facet("%S""%F"))); check("silent failure test w/ second flag", failure_test(td, "bad_input", new time_input_facet("%S""%F"))); check("failure test w/ sec w/frac (always) flag", failure_test(td, "bad_input", exc, new time_input_facet("%s"))); check("silent failure test w/ sec w/frac (always) flag", failure_test(td, "bad_input", new time_input_facet("%s"))); check("failure test w/ frac_sec flag", failure_test(td, "bad_input", exc, new time_input_facet("%f"))); check("silent failure test w/ frac_sec flag", failure_test(td, "bad_input", new time_input_facet("%f"))); } // special_values tests. prove the individual flags catch special_values // NOTE: these flags all by themselves will not parse a complete ptime, // these are *specific* special_values tests iss.str("+infinity -infinity"); facet->format("%H"); facet->time_duration_format("%H"); iss >> pt; iss >> td; check_equal("Special value: ptime %H flag", pt, ptime(pos_infin)); check_equal("Special value: time_duration %H flag", td, time_duration(neg_infin)); iss.str("not-a-date-time +infinity"); facet->format("%M"); facet->time_duration_format("%M"); iss >> pt; iss >> td; check_equal("Special value: ptime %M flag", pt, ptime(not_a_date_time)); check_equal("Special value: time_duration %M flag", td, time_duration(pos_infin)); iss.str("-infinity not-a-date-time "); facet->format("%S"); facet->time_duration_format("%S"); iss >> pt; iss >> td; check_equal("Special value: ptime %S flag", pt, ptime(neg_infin)); check_equal("Special value: time_duration %S flag", td, time_duration(not_a_date_time)); iss.str("+infinity -infinity"); facet->format("%s"); facet->time_duration_format("%s"); iss >> pt; iss >> td; check_equal("Special value: ptime %s flag", pt, ptime(pos_infin)); check_equal("Special value: time_duration %s flag", td, time_duration(neg_infin)); iss.str("not-a-date-time +infinity"); facet->format("%j"); facet->time_duration_format("%f"); iss >> pt; iss >> td; check_equal("Special value: ptime %j flag", pt, ptime(not_a_date_time)); check_equal("Special value: time_duration %f flag", td, time_duration(pos_infin)); // time_period tests - the time_period_parser is thoroughly tested in gregorian tests // default period format is closed range so last ptime is included in peiod iss.str("[2005-Jan-01 00:00:00/2005-Dec-31 23:59:59]"); facet->format(time_input_facet::default_time_input_format); // reset format iss >> tp; check("Time period, default formats", (tp.begin() == ptime(date(2005,1,1),hours(0))) && (tp.last() == ptime(date(2005,12,31),time_duration(23,59,59))) && (tp.end() == ptime(date(2005,12,31),time_duration(23,59,59,1))) ); { std::stringstream ss; ptime ptx(not_a_date_time); ptime pt2 = second_clock::local_time(); ptime pt3(neg_infin); ptime pt4(pos_infin); time_period tpx(pt2, ptx); // ptime/nadt time_period tp2(ptx, ptx); // nadt/nadt time_period tp3(pt3, pt4); ss << tpx; ss >> tp2; check_equal("Special values period (reversibility test)", tpx, tp2); ss.str("[-infinity/+infinity]"); ss >> tp2; check_equal("Special values period (infinities)", tp3, tp2); } // Failure tests // faliure tests for date elements tested in gregorian tests time_input_facet* facet2 = new time_input_facet(); facet2->time_duration_format("%H:%M:%S""%f"); check("Failure test: Missing frac_sec with %f flag (w/exceptions)", failure_test(td, "14:13:12 extra stuff", e_failure, facet2)); time_input_facet* facet3 = new time_input_facet(); facet3->time_duration_format("%H:%M:%S""%f"); check("Failure test: Missing frac_sec with %f flag (no exceptions)", failure_test(td, "14:13:12 extra stuff", facet3)); // Reversable format tests time_duration td_io(10,11,12,1234567); ptime pt_io(date(2004,03,15), td_io); time_facet* otp_facet = new time_facet(); time_input_facet* inp_facet = new time_input_facet(); std::stringstream ss; std::locale loc2(std::locale::classic(), otp_facet); ss.imbue(std::locale(loc2, inp_facet)); // imbue locale containing both facets ss.str(""); ss << pt_io; // stream out pt_io ss >> pt; check_equal("Stream out one ptime then into another: default format", pt_io, pt); ss.str(""); ss << td_io; // stream out td_io ss >> td; check_equal("Stream out one time_duration then into another: default format", td_io, td); td_io = time_duration(1,29,59); // no frac_sec, default format has %F pt_io = ptime(date(2004,2,29), td_io); // leap year ss.str(""); ss << pt_io; // stream out pt_io ss >> pt; check_equal("Stream out one ptime then into another: default format", pt_io, pt); ss.str(""); ss << td_io; // stream out td_io ss >> td; check_equal("Stream out one time_duration then into another: default format", td_io, td); td_io = time_duration(13,05,0); // no seconds as the next formats won't use them pt_io = ptime(date(2004,2,29), td_io); // leap year otp_facet->format("%Y-%jT%H:%M"); // extended ordinal inp_facet->format("%Y-%jT%H:%M"); ss.str(""); ss << pt_io; // stream out pt_io ss >> pt; check_equal("Stream out one ptime then into another: extended ordinal format", pt_io, pt); otp_facet->format("Time: %H:%M, Date: %B %d, %Y"); // custom format with extra words inp_facet->format("Time: %H:%M, Date: %B %d, %Y"); ss.str(""); ss << pt_io; // stream out pt_io ss >> pt; check_equal("Stream out one ptime then into another: custom format (" + ss.str() + ")", pt_io, pt); { // fully parameterized constructor - compile only test, all other features tested in gregorian boost::date_time::format_date_parser fdp("%Y-%b-%d", std::locale::classic()); special_values_parser svp; // default constructor period_parser pp; // default constructor boost::date_time::date_generator_parser dgp; // default constructor time_input_facet tif("%Y-%m-%d %H:%M:%s", fdp, svp, pp, dgp); } // trac 13194 (https://svn.boost.org/trac10/ticket/13194) { const std::string value = "December 07:27:10.435945 5 2017"; boost::posix_time::time_input_facet* facet4 = new boost::posix_time::time_input_facet("%B %H:%M:%s %e %Y"); boost::posix_time::ptime ptx; check("trac 13194 %e on \"5\" failbit set", !failure_test(ptx, value, facet4)); // proves failbit was not set check_equal("trac 13194 %e on \" 5\" valid value", "2017-12-05T07:27:10.435945000", to_iso_extended_string(ptx)); } // trac 12910 { const std::string value = "263-08:09:10"; boost::posix_time::time_input_facet* facet = new boost::posix_time::time_input_facet("%j-%H:%M:%S"); boost::posix_time::ptime pt; check("trac 12910 %j without %Y no failbit", !failure_test(pt, value, facet)); // proves failbit was not set check_equal("trac 12910 %j without %Y value", "1400-09-20T08:09:10", to_iso_extended_string(pt)); } #endif // USE_DATE_TIME_PRE_1_33_FACET_IO } int main(){ try { //wrap all the tests -- we don't expect an exception do_all_tests(); } catch(std::exception& e) { std::string failure("std::exception caught: "); failure += e.what(); check(failure, false); } catch(...) { check("Unknown exception caught -- failing", false); } return printTestStats(); }