diff --git a/cql3/Cql.g b/cql3/Cql.g index beefdf90a070..b98a716929c9 100644 --- a/cql3/Cql.g +++ b/cql3/Cql.g @@ -1465,12 +1465,16 @@ native_type returns [shared_ptr t] | K_FLOAT { $t = cql3_type::float_; } | K_INET { $t = cql3_type::inet; } | K_INT { $t = cql3_type::int_; } + | K_SMALLINT { $t = cql3_type::smallint; } | K_TEXT { $t = cql3_type::text; } | K_TIMESTAMP { $t = cql3_type::timestamp; } + | K_TINYINT { $t = cql3_type::tinyint; } | K_UUID { $t = cql3_type::uuid; } | K_VARCHAR { $t = cql3_type::varchar; } | K_VARINT { $t = cql3_type::varint; } | K_TIMEUUID { $t = cql3_type::timeuuid; } + | K_DATE { $t = cql3_type::date; } + | K_TIME { $t = cql3_type::time; } ; collection_type returns [shared_ptr pt] @@ -1644,6 +1648,8 @@ K_DOUBLE: D O U B L E; K_FLOAT: F L O A T; K_INET: I N E T; K_INT: I N T; +K_SMALLINT: S M A L L I N T; +K_TINYINT: T I N Y I N T; K_TEXT: T E X T; K_UUID: U U I D; K_VARCHAR: V A R C H A R; @@ -1651,6 +1657,8 @@ K_VARINT: V A R I N T; K_TIMEUUID: T I M E U U I D; K_TOKEN: T O K E N; K_WRITETIME: W R I T E T I M E; +K_DATE: D A T E; +K_TIME: T I M E; K_NULL: N U L L; K_NOT: N O T; diff --git a/cql3/constants.cc b/cql3/constants.cc index 6aca7c90407a..68885f90722e 100644 --- a/cql3/constants.cc +++ b/cql3/constants.cc @@ -97,7 +97,9 @@ constants::literal::test_assignment(database& db, const sstring& keyspace, ::sha cql3_type::kind::TEXT, cql3_type::kind::INET, cql3_type::kind::VARCHAR, - cql3_type::kind::TIMESTAMP>::contains(kind)) { + cql3_type::kind::TIMESTAMP, + cql3_type::kind::DATE, + cql3_type::kind::TIME>::contains(kind)) { return assignment_testable::test_result::WEAKLY_ASSIGNABLE; } break; @@ -109,7 +111,10 @@ constants::literal::test_assignment(database& db, const sstring& keyspace, ::sha cql3_type::kind::DOUBLE, cql3_type::kind::FLOAT, cql3_type::kind::INT, + cql3_type::kind::SMALLINT, cql3_type::kind::TIMESTAMP, + cql3_type::kind::DATE, + cql3_type::kind::TINYINT, cql3_type::kind::VARINT>::contains(kind)) { return assignment_testable::test_result::WEAKLY_ASSIGNABLE; } diff --git a/cql3/cql3_type.cc b/cql3/cql3_type.cc index 252157082586..ba71023b6338 100644 --- a/cql3/cql3_type.cc +++ b/cql3/cql3_type.cc @@ -273,11 +273,15 @@ thread_local shared_ptr cql3_type::boolean = make("boolean", boolean_ thread_local shared_ptr cql3_type::double_ = make("double", double_type, cql3_type::kind::DOUBLE); thread_local shared_ptr cql3_type::float_ = make("float", float_type, cql3_type::kind::FLOAT); thread_local shared_ptr cql3_type::int_ = make("int", int32_type, cql3_type::kind::INT); +thread_local shared_ptr cql3_type::smallint = make("smallint", short_type, cql3_type::kind::SMALLINT); thread_local shared_ptr cql3_type::text = make("text", utf8_type, cql3_type::kind::TEXT); thread_local shared_ptr cql3_type::timestamp = make("timestamp", timestamp_type, cql3_type::kind::TIMESTAMP); +thread_local shared_ptr cql3_type::tinyint = make("tinyint", byte_type, cql3_type::kind::TINYINT); thread_local shared_ptr cql3_type::uuid = make("uuid", uuid_type, cql3_type::kind::UUID); thread_local shared_ptr cql3_type::varchar = make("varchar", utf8_type, cql3_type::kind::TEXT); thread_local shared_ptr cql3_type::timeuuid = make("timeuuid", timeuuid_type, cql3_type::kind::TIMEUUID); +thread_local shared_ptr cql3_type::date = make("date", simple_date_type, cql3_type::kind::DATE); +thread_local shared_ptr cql3_type::time = make("time", time_type, cql3_type::kind::TIME); thread_local shared_ptr cql3_type::inet = make("inet", inet_addr_type, cql3_type::kind::INET); thread_local shared_ptr cql3_type::varint = make("varint", varint_type, cql3_type::kind::VARINT); thread_local shared_ptr cql3_type::decimal = make("decimal", decimal_type, cql3_type::kind::DECIMAL); @@ -296,12 +300,16 @@ cql3_type::values() { cql3_type::float_, cql3_type:inet, cql3_type::int_, + cql3_type::smallint, cql3_type::text, cql3_type::timestamp, + cql3_type::tinyint, cql3_type::uuid, cql3_type::varchar, cql3_type::varint, cql3_type::timeuuid, + cql3_type::date, + cql3_type::time, }; return v; } diff --git a/cql3/cql3_type.hh b/cql3/cql3_type.hh index 7503b59f95c9..7df4ff432789 100644 --- a/cql3/cql3_type.hh +++ b/cql3/cql3_type.hh @@ -98,7 +98,7 @@ private: public: enum class kind : int8_t { - ASCII, BIGINT, BLOB, BOOLEAN, COUNTER, DECIMAL, DOUBLE, FLOAT, INT, INET, TEXT, TIMESTAMP, UUID, VARCHAR, VARINT, TIMEUUID + ASCII, BIGINT, BLOB, BOOLEAN, COUNTER, DECIMAL, DOUBLE, FLOAT, INT, SMALLINT, TINYINT, INET, TEXT, TIMESTAMP, UUID, VARCHAR, VARINT, TIMEUUID, DATE, TIME }; using kind_enum = super_enum; + kind::TIMEUUID, + kind::DATE, + kind::TIME>; using kind_enum_set = enum_set; private: std::experimental::optional _kind; @@ -131,11 +135,15 @@ public: static thread_local shared_ptr double_; static thread_local shared_ptr float_; static thread_local shared_ptr int_; + static thread_local shared_ptr smallint; static thread_local shared_ptr text; static thread_local shared_ptr timestamp; + static thread_local shared_ptr tinyint; static thread_local shared_ptr uuid; static thread_local shared_ptr varchar; static thread_local shared_ptr timeuuid; + static thread_local shared_ptr date; + static thread_local shared_ptr time; static thread_local shared_ptr inet; static thread_local shared_ptr varint; static thread_local shared_ptr decimal; diff --git a/cql3/query_processor.cc b/cql3/query_processor.cc index 8ce70ba6facd..5985d583f79d 100644 --- a/cql3/query_processor.cc +++ b/cql3/query_processor.cc @@ -59,7 +59,7 @@ logging::logger log("query_processor"); distributed _the_query_processor; -const sstring query_processor::CQL_VERSION = "3.2.1"; +const sstring query_processor::CQL_VERSION = "3.3.1"; class query_processor::internal_state { service::query_state _qs; diff --git a/cql_serialization_format.hh b/cql_serialization_format.hh index 50a12a53f240..87d5d47940f2 100644 --- a/cql_serialization_format.hh +++ b/cql_serialization_format.hh @@ -35,7 +35,7 @@ using cql_protocol_version_type = uint8_t; class cql_serialization_format { cql_protocol_version_type _version; public: - static constexpr cql_protocol_version_type latest_version = 3; + static constexpr cql_protocol_version_type latest_version = 4; explicit cql_serialization_format(cql_protocol_version_type version) : _version(version) {} static cql_serialization_format latest() { return cql_serialization_format{latest_version}; } static cql_serialization_format internal() { return latest(); } diff --git a/db/schema_tables.cc b/db/schema_tables.cc index 5a2289121fac..3ef2795b39eb 100644 --- a/db/schema_tables.cc +++ b/db/schema_tables.cc @@ -2175,7 +2175,7 @@ data_type parse_type(sstring str) std::vector all_tables() { return { - keyspaces(), columnfamilies(), columns(), triggers(), usertypes(), /* Not in 2.1.8 functions(), aggregates() */ + keyspaces(), columnfamilies(), columns(), triggers(), usertypes(), functions(), aggregates(), views(), }; } diff --git a/licenses/date-license.txt b/licenses/date-license.txt new file mode 100644 index 000000000000..922125a6f07a --- /dev/null +++ b/licenses/date-license.txt @@ -0,0 +1,32 @@ +The license for utils/date.h is: + + The MIT License (MIT) + + Copyright (c) 2015, 2016 Howard Hinnant + Copyright (c) 2016 Adrian Colomitchi + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + Our apologies. When the previous paragraph was written, lowercase had not yet + been invented (that woud involve another several millennia of evolution). + We did not mean to shout. + +Source code was imported from the following repository: + + https://github.com/HowardHinnant/date diff --git a/tests/cql_query_test.cc b/tests/cql_query_test.cc index a3069cd71833..657417f5c54c 100644 --- a/tests/cql_query_test.cc +++ b/tests/cql_query_test.cc @@ -1358,11 +1358,15 @@ SEASTAR_TEST_CASE(test_types) { " m varchar," " n varint," " o decimal," + " p tinyint," + " q smallint," + " r date," + " s time," ");").discard_result(); }).then([&e] { e.require_table_exists("ks", "all_types"); return e.execute_cql( - "INSERT INTO all_types (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) VALUES (" + "INSERT INTO all_types (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) VALUES (" " 'ascii'," " 123456789," " 0xdeadbeef," @@ -1377,7 +1381,11 @@ SEASTAR_TEST_CASE(test_types) { " d2177dd0-eaa2-11de-a572-001b779c76e3," " 'varchar'," " 123," - " 1.23" + " 1.23," + " 3," + " 3," + " '1970-01-02'," + " '00:00:00.000000001'" ");").discard_result(); }).then([&e] { return e.execute_cql("SELECT * FROM all_types WHERE a = 'ascii'"); @@ -1401,11 +1409,15 @@ SEASTAR_TEST_CASE(test_types) { timeuuid_type->decompose(utils::UUID(sstring("d2177dd0-eaa2-11de-a572-001b779c76e3"))), uuid_type->decompose(utils::UUID(sstring("d2177dd0-eaa2-11de-a572-001b779c76e3"))), utf8_type->decompose(sstring("varchar")), varint_type->decompose(boost::multiprecision::cpp_int(123)), - decimal_type->decompose(big_decimal { 2, boost::multiprecision::cpp_int(123) }) + decimal_type->decompose(big_decimal { 2, boost::multiprecision::cpp_int(123) }), + byte_type->decompose(int8_t(3)), + short_type->decompose(int16_t(3)), + simple_date_type->decompose(int32_t(0x80000001)), + time_type->decompose(int64_t(0x0000000000000001)), } }); return e.execute_cql( - "INSERT INTO all_types (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) VALUES (" + "INSERT INTO all_types (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) VALUES (" " blobAsAscii(asciiAsBlob('ascii2'))," " blobAsBigint(bigintAsBlob(123456789))," " bigintAsBlob(12)," @@ -1419,7 +1431,11 @@ SEASTAR_TEST_CASE(test_types) { " blobAsTimeuuid(timeuuidAsBlob(d2177dd0-eaa2-11de-a572-001b779c76e3))," " blobAsUuid(uuidAsBlob(d2177dd0-eaa2-11de-a572-001b779c76e3))," " blobAsVarchar(varcharAsBlob('varchar')), blobAsVarint(varintAsBlob(123))," - " blobAsDecimal(decimalAsBlob(1.23))" + " blobAsDecimal(decimalAsBlob(1.23))," + " blobAsTinyint(tinyintAsBlob(3))," + " blobAsSmallint(smallintAsBlob(3))," + " blobAsDate(dateAsBlob('1970-01-02'))," + " blobAsTime(timeAsBlob('00:00:00.000000001'))" ");").discard_result(); }).then([&e] { return e.execute_cql("SELECT * FROM all_types WHERE a = 'ascii2'"); @@ -1443,7 +1459,11 @@ SEASTAR_TEST_CASE(test_types) { timeuuid_type->decompose(utils::UUID(sstring("d2177dd0-eaa2-11de-a572-001b779c76e3"))), uuid_type->decompose(utils::UUID(sstring("d2177dd0-eaa2-11de-a572-001b779c76e3"))), utf8_type->decompose(sstring("varchar")), varint_type->decompose(boost::multiprecision::cpp_int(123)), - decimal_type->decompose(big_decimal { 2, boost::multiprecision::cpp_int(123) }) + decimal_type->decompose(big_decimal { 2, boost::multiprecision::cpp_int(123) }), + byte_type->decompose(int8_t(3)), + short_type->decompose(int16_t(3)), + simple_date_type->decompose(int32_t(0x80000001)), + time_type->decompose(int64_t(0x0000000000000001)), } }); }); diff --git a/tests/types_test.cc b/tests/types_test.cc index 81ab09932529..bcf6572e0f6b 100644 --- a/tests/types_test.cc +++ b/tests/types_test.cc @@ -51,6 +51,64 @@ BOOST_AUTO_TEST_CASE(test_bytes_type_string_conversions) { BOOST_REQUIRE(bytes_type->equal(bytes_type->from_string("616263646566"), bytes_type->decompose(data_value(bytes{"abcdef"})))); } +BOOST_AUTO_TEST_CASE(test_byte_type_string_conversions) { + BOOST_REQUIRE(byte_type->equal(byte_type->from_string("123"), byte_type->decompose(int8_t(123)))); + BOOST_REQUIRE_EQUAL(byte_type->to_string(byte_type->decompose(int8_t(123))), "123"); + + BOOST_REQUIRE(byte_type->equal(byte_type->from_string("12"), byte_type->decompose(int8_t(12)))); + BOOST_REQUIRE(byte_type->equal(byte_type->from_string("0012"), byte_type->decompose(int8_t(12)))); + BOOST_REQUIRE(byte_type->equal(byte_type->from_string("+12"), byte_type->decompose(int8_t(12)))); + BOOST_REQUIRE_EQUAL(byte_type->to_string(byte_type->decompose(int8_t(12))), "12"); + BOOST_REQUIRE(byte_type->equal(byte_type->from_string("-12"), byte_type->decompose(int8_t(-12)))); + BOOST_REQUIRE_EQUAL(byte_type->to_string(byte_type->decompose(int8_t(-12))), "-12"); + + BOOST_REQUIRE(byte_type->equal(byte_type->from_string("0"), byte_type->decompose(int8_t(0)))); + BOOST_REQUIRE(byte_type->equal(byte_type->from_string("-0"), byte_type->decompose(int8_t(0)))); + BOOST_REQUIRE(byte_type->equal(byte_type->from_string("+0"), byte_type->decompose(int8_t(0)))); + BOOST_REQUIRE_EQUAL(byte_type->to_string(byte_type->decompose(int8_t(0))), "0"); + + BOOST_REQUIRE(byte_type->equal(byte_type->from_string("-128"), byte_type->decompose(int8_t(-128)))); + BOOST_REQUIRE_EQUAL(byte_type->to_string(byte_type->decompose((int8_t(-128)))), "-128"); + + BOOST_REQUIRE(byte_type->equal(byte_type->from_string("127"), byte_type->decompose(int8_t(127)))); + BOOST_REQUIRE_EQUAL(byte_type->to_string(byte_type->decompose(int8_t(127))), "127"); + + test_parsing_fails(byte_type, "asd"); + test_parsing_fails(byte_type, "-129"); + test_parsing_fails(byte_type, "128"); + + BOOST_REQUIRE_EQUAL(byte_type->to_string(bytes()), ""); +} + +BOOST_AUTO_TEST_CASE(test_short_type_string_conversions) { + BOOST_REQUIRE(short_type->equal(short_type->from_string("12345"), short_type->decompose(int16_t(12345)))); + BOOST_REQUIRE_EQUAL(short_type->to_string(short_type->decompose(int16_t(12345))), "12345"); + + BOOST_REQUIRE(short_type->equal(short_type->from_string("12"), short_type->decompose(int16_t(12)))); + BOOST_REQUIRE(short_type->equal(short_type->from_string("0012"), short_type->decompose(int16_t(12)))); + BOOST_REQUIRE(short_type->equal(short_type->from_string("+12"), short_type->decompose(int16_t(12)))); + BOOST_REQUIRE_EQUAL(short_type->to_string(short_type->decompose(int16_t(12))), "12"); + BOOST_REQUIRE(short_type->equal(short_type->from_string("-12"), short_type->decompose(int16_t(-12)))); + BOOST_REQUIRE_EQUAL(short_type->to_string(short_type->decompose(int16_t(-12))), "-12"); + + BOOST_REQUIRE(short_type->equal(short_type->from_string("0"), short_type->decompose(int16_t(0)))); + BOOST_REQUIRE(short_type->equal(short_type->from_string("-0"), short_type->decompose(int16_t(0)))); + BOOST_REQUIRE(short_type->equal(short_type->from_string("+0"), short_type->decompose(int16_t(0)))); + BOOST_REQUIRE_EQUAL(short_type->to_string(short_type->decompose(int16_t(0))), "0"); + + BOOST_REQUIRE(short_type->equal(short_type->from_string("-32768"), short_type->decompose(int16_t(-32768)))); + BOOST_REQUIRE_EQUAL(short_type->to_string(short_type->decompose((int16_t(-32768)))), "-32768"); + + BOOST_REQUIRE(short_type->equal(short_type->from_string("32677"), short_type->decompose(int16_t(32677)))); + BOOST_REQUIRE_EQUAL(short_type->to_string(short_type->decompose(int16_t(32677))), "32677"); + + test_parsing_fails(short_type, "asd"); + test_parsing_fails(short_type, "-32769"); + test_parsing_fails(short_type, "32768"); + + BOOST_REQUIRE_EQUAL(short_type->to_string(bytes()), ""); +} + BOOST_AUTO_TEST_CASE(test_int32_type_string_conversions) { BOOST_REQUIRE(int32_type->equal(int32_type->from_string("1234567890"), int32_type->decompose(1234567890))); BOOST_REQUIRE_EQUAL(int32_type->to_string(int32_type->decompose(1234567890)), "1234567890"); @@ -94,6 +152,49 @@ BOOST_AUTO_TEST_CASE(test_timeuuid_type_string_conversions) { test_parsing_fails(timeuuid_type, utils::make_random_uuid().to_sstring()); } +BOOST_AUTO_TEST_CASE(test_simple_date_type_string_conversions) { + BOOST_REQUIRE(simple_date_type->equal(simple_date_type->from_string("1970-01-01"), simple_date_type->decompose(int32_t(0x80000000)))); + BOOST_REQUIRE_EQUAL(simple_date_type->to_string(simple_date_type->decompose(int32_t(0x80000000))), "1970-01-01"); + + BOOST_REQUIRE(simple_date_type->equal(simple_date_type->from_string("-5877641-06-23"), simple_date_type->decompose(int32_t(0x00000000)))); + BOOST_REQUIRE_EQUAL(simple_date_type->to_string(simple_date_type->decompose(int32_t(0x00000000))), "-5877641-06-23"); + + BOOST_REQUIRE(simple_date_type->equal(simple_date_type->from_string("5881580-07-11"), simple_date_type->decompose(int32_t(0xffffffff)))); + BOOST_REQUIRE_EQUAL(simple_date_type->to_string(simple_date_type->decompose(int32_t(0xffffffff))), "5881580-07-11"); + + test_parsing_fails(simple_date_type, "something"); + test_parsing_fails(simple_date_type, "-5877641-06-22"); + test_parsing_fails(simple_date_type, "5881580-07-12"); +} + +BOOST_AUTO_TEST_CASE(test_time_type_string_conversions) { + BOOST_REQUIRE(time_type->equal(time_type->from_string("12:34:56"), time_type->decompose(int64_t(45296000000000)))); + BOOST_REQUIRE_EQUAL(time_type->to_string(time_type->decompose(int64_t(45296000000000))), "12:34:56.000000000"); + + BOOST_REQUIRE(time_type->equal(time_type->from_string("12:34:56.000000000"), time_type->decompose(int64_t(45296000000000)))); + BOOST_REQUIRE_EQUAL(time_type->to_string(time_type->decompose(int64_t(45296000000000))), "12:34:56.000000000"); + + BOOST_REQUIRE(time_type->equal(time_type->from_string("12:34:56.123456789"), time_type->decompose(int64_t(45296123456789)))); + BOOST_REQUIRE_EQUAL(time_type->to_string(time_type->decompose(int64_t(45296123456789))), "12:34:56.123456789"); + + BOOST_REQUIRE(time_type->equal(time_type->from_string("00:00:00.000000000"), time_type->decompose(int64_t(0x00000000)))); + BOOST_REQUIRE_EQUAL(time_type->to_string(time_type->decompose(int64_t(0x00000000))), "00:00:00.000000000"); + + BOOST_REQUIRE(time_type->equal(time_type->from_string("23:59:59.999999999"), time_type->decompose(int64_t(86399999999999)))); + BOOST_REQUIRE_EQUAL(time_type->to_string(time_type->decompose(int64_t(86399999999999))), "23:59:59.999999999"); + + BOOST_REQUIRE(time_type->equal(time_type->from_string("-00:00:00.000000000"), time_type->decompose(int64_t(0x00000000)))); + BOOST_REQUIRE(time_type->equal(time_type->from_string("-00:00:00.000000001"), time_type->decompose(int64_t(0x00000001)))); + + test_parsing_fails(time_type, "something"); + test_parsing_fails(time_type, "00:00"); + test_parsing_fails(time_type, "24:00:00.000000000"); + test_parsing_fails(time_type, "-01:00:00.0000000000"); + test_parsing_fails(time_type, "00:-01:00.0000000000"); + test_parsing_fails(time_type, "00:00:-10.0000000000"); + test_parsing_fails(time_type, "00:00:00.-0000000001"); +} + BOOST_AUTO_TEST_CASE(test_uuid_type_comparison) { auto uuid1 = uuid_type->decompose(utils::UUID(sstring("ad4d3770-7a50-11e6-ac4d-000000000003"))); auto uuid2 = uuid_type->decompose(utils::UUID(sstring("c512ba10-7a50-11e6-ac4d-000000000003"))); diff --git a/transport/server.cc b/transport/server.cc index 2282ee99984f..7f56a036837b 100644 --- a/transport/server.cc +++ b/transport/server.cc @@ -1724,6 +1724,10 @@ class type_codec { VARINT = 0x000E, TIMEUUID = 0x000F, INET = 0x0010, + DATE = 0x0011, + TIME = 0x0012, + SMALLINT = 0x0013, + TINYINT = 0x0014, LIST = 0x0020, MAP = 0x0021, SET = 0x0022, @@ -1809,11 +1813,15 @@ thread_local const type_codec::type_id_to_type_type type_codec::type_id_to_type (type_id::DOUBLE , double_type) (type_id::FLOAT , float_type) (type_id::INT , int32_type) + (type_id::TINYINT , byte_type) + (type_id::SMALLINT , short_type) (type_id::TIMESTAMP , timestamp_type) (type_id::UUID , uuid_type) (type_id::VARCHAR , utf8_type) (type_id::VARINT , varint_type) (type_id::TIMEUUID , timeuuid_type) + (type_id::DATE , simple_date_type) + (type_id::TIME , time_type) (type_id::INET , inet_addr_type); void cql_server::response::write(const cql3::metadata& m) { diff --git a/types.cc b/types.cc index 846afe8c93f7..d58f8c4fc20e 100644 --- a/types.cc +++ b/types.cc @@ -32,7 +32,9 @@ #include "utils/serialization.hh" #include "combine.hh" #include +#include #include +#include #include #include #include @@ -42,6 +44,7 @@ #include #include #include "utils/big_decimal.hh" +#include "utils/date.h" template sstring time_point_to_string(const T& tp) @@ -51,6 +54,8 @@ sstring time_point_to_string(const T& tp) return boost::posix_time::to_iso_extended_string(time); } +static const char* byte_type_name = "org.apache.cassandra.db.marshal.ByteType"; +static const char* short_type_name = "org.apache.cassandra.db.marshal.ShortType"; static const char* int32_type_name = "org.apache.cassandra.db.marshal.Int32Type"; static const char* long_type_name = "org.apache.cassandra.db.marshal.LongType"; static const char* ascii_type_name = "org.apache.cassandra.db.marshal.AsciiType"; @@ -60,6 +65,8 @@ static const char* boolean_type_name = "org.apache.cassandra.db.marshal.Boolea static const char* timeuuid_type_name = "org.apache.cassandra.db.marshal.TimeUUIDType"; static const char* timestamp_type_name = "org.apache.cassandra.db.marshal.TimestampType"; static const char* date_type_name = "org.apache.cassandra.db.marshal.DateType"; +static const char* simple_date_type_name = "org.apache.cassandra.db.marshal.SimpleDateType"; +static const char* time_type_name = "org.apache.cassandra.db.marshal.TimeType"; static const char* uuid_type_name = "org.apache.cassandra.db.marshal.UUIDType"; static const char* inet_addr_type_name = "org.apache.cassandra.db.marshal.InetAddressType"; static const char* double_type_name = "org.apache.cassandra.db.marshal.DoubleType"; @@ -172,7 +179,12 @@ struct integer_type_impl : simple_type_impl { } T parse_int(sstring_view s) const { try { - return boost::lexical_cast(s.begin(), s.size()); + auto value64 = boost::lexical_cast(s.begin(), s.size()); + auto value = static_cast(value64); + if (value != value64) { + throw marshal_exception(sprint("Value out of range for type %s: '%s'", this->name(), s)); + } + return static_cast(value); } catch (const boost::bad_lexical_cast& e) { throw marshal_exception(sprint("Invalid number format '%s'", s)); } @@ -188,6 +200,36 @@ struct integer_type_impl : simple_type_impl { } }; +struct byte_type_impl : integer_type_impl { + byte_type_impl() : integer_type_impl{byte_type_name} + { } + + virtual void validate(bytes_view v) const override { + if (v.size() != 0 && v.size() != 1) { + throw marshal_exception(sprint("Expected 1 byte for a tinyint (%d)", v.size())); + } + } + + virtual ::shared_ptr as_cql3_type() const override { + return cql3::cql3_type::tinyint; + } +}; + +struct short_type_impl : integer_type_impl { + short_type_impl() : integer_type_impl{short_type_name} + { } + + virtual void validate(bytes_view v) const override { + if (v.size() != 0 && v.size() != 2) { + throw marshal_exception(sprint("Expected 2 bytes for a smallint (%d)", v.size())); + } + } + + virtual ::shared_ptr as_cql3_type() const override { + return cql3::cql3_type::smallint; + } +}; + struct int32_type_impl : integer_type_impl { int32_type_impl() : integer_type_impl{int32_type_name} { } @@ -732,6 +774,197 @@ class timestamp_type_impl : public simple_type_impl { }; logging::logger timestamp_type_impl::_logger(timestamp_type_name); +struct simple_date_type_impl : public simple_type_impl { + simple_date_type_impl() : simple_type_impl{simple_date_type_name} + { } + virtual void serialize(const void* value, bytes::iterator& out) const override { + if (!value) { + return; + } + auto&& v1 = from_value(value); + if (v1.empty()) { + return; + } + uint32_t v = v1.get(); + v = net::hton(v); + out = std::copy_n(reinterpret_cast(&v), sizeof(v), out); + + } + virtual size_t serialized_size(const void* value) const override { + if (!value || from_value(value).empty()) { + return 0; + } + return 4; + } + virtual data_value deserialize(bytes_view in) const override { + if (in.empty()) { + return make_empty(); + } + auto v = read_simple_exactly(in); + return make_value(v); + } + virtual void validate(bytes_view v) const override { + if (v.size() != 0 && v.size() != 4) { + throw marshal_exception(sprint("Expected 4 byte long for date (%d)", v.size())); + } + } + virtual bytes from_string(sstring_view s) const override { + if (s.empty()) { + return bytes(); + } + uint32_t ts = net::hton(days_from_string(s)); + bytes b(bytes::initialized_later(), sizeof(int32_t)); + std::copy_n(reinterpret_cast(&ts), sizeof(ts), b.begin()); + return b; + } + static uint32_t days_from_string(sstring_view s) { + std::string str; + str.resize(s.size()); + std::transform(s.begin(), s.end(), str.begin(), ::tolower); + char* end; + auto v = std::strtoll(s.begin(), &end, 10); + if (end == s.begin() + s.size()) { + return v; + } + static std::regex date_re("^(-?\\d+)-(\\d+)-(\\d+)"); + std::smatch dsm; + if (!std::regex_match(str, dsm, date_re)) { + throw marshal_exception(sprint("Unable to coerce '%s' to a formatted date (long)", str)); + } + auto t = get_time(dsm); + return serialize(str, date::local_days(t).time_since_epoch().count()); + } + static date::year_month_day get_time(const std::smatch& sm) { + auto year = boost::lexical_cast(sm[1]); + auto month = boost::lexical_cast(sm[2]); + auto day = boost::lexical_cast(sm[3]); + return date::year_month_day{date::year{year}, date::month{month}, date::day{day}}; + } + static uint32_t serialize(const std::string& input, int64_t days) { + if (days < std::numeric_limits::min()) { + throw marshal_exception(sprint("Input date %s is less than min supported date -5877641-06-23", input)); + } + if (days > std::numeric_limits::max()) { + throw marshal_exception(sprint("Input date %s is greater than max supported date 5881580-07-11", input)); + } + days += 1UL << 31; + return static_cast(days); + } + virtual sstring to_string(const bytes& b) const override { + auto v = deserialize(b); + if (v.is_null()) { + return ""; + } + date::days days{from_value(v).get() - (1UL << 31)}; + date::year_month_day ymd{date::local_days{days}}; + std::ostringstream str; + str << ymd; + return str.str(); + } + virtual ::shared_ptr as_cql3_type() const override { + return cql3::cql3_type::date; + } +}; + +struct time_type_impl : public simple_type_impl { + time_type_impl() : simple_type_impl{time_type_name} + { } + virtual void serialize(const void* value, bytes::iterator& out) const override { + if (!value) { + return; + } + auto&& v1 = from_value(value); + if (v1.empty()) { + return; + } + uint64_t v = v1.get(); + v = net::hton(v); + out = std::copy_n(reinterpret_cast(&v), sizeof(v), out); + } + virtual size_t serialized_size(const void* value) const override { + if (!value || from_value(value).empty()) { + return 0; + } + return 8; + } + virtual data_value deserialize(bytes_view in) const override { + if (in.empty()) { + return make_empty(); + } + auto v = read_simple_exactly(in); + return make_value(v); + } + virtual void validate(bytes_view v) const override { + if (v.size() != 0 && v.size() != 8) { + throw marshal_exception(sprint("Expected 8 byte long for time (%d)", v.size())); + } + } + virtual bytes from_string(sstring_view s) const override { + if (s.empty()) { + return bytes(); + } + int64_t ts = net::hton(parse_time(s)); + bytes b(bytes::initialized_later(), sizeof(int64_t)); + std::copy_n(reinterpret_cast(&ts), sizeof(ts), b.begin()); + return b; + } + static int64_t parse_time(sstring_view s) { + static auto format_error = "Timestamp format must be hh:mm:ss[.fffffffff]"; + auto hours_end = s.find(':'); + if (hours_end == std::string::npos) { + throw marshal_exception(format_error); + } + int64_t hours = std::stol(s.substr(0, hours_end).to_string()); + if (hours < 0 || hours >= 24) { + throw marshal_exception("Hour out of bounds."); + } + auto minutes_end = s.find(':', hours_end+1); + if (minutes_end == std::string::npos) { + throw marshal_exception(format_error); + } + int64_t minutes = std::stol(s.substr(hours_end + 1, hours_end-minutes_end).to_string()); + if (minutes < 0 || minutes >= 60) { + throw marshal_exception("Minute out of bounds."); + } + auto seconds_end = s.find('.', minutes_end+1); + if (seconds_end == std::string::npos) { + seconds_end = s.length(); + } + int64_t seconds = std::stol(s.substr(minutes_end + 1, minutes_end-seconds_end).to_string()); + if (seconds < 0 || seconds >= 60) { + throw marshal_exception("Second out of bounds."); + } + int64_t nanoseconds = 0; + if (seconds_end < s.length()) { + nanoseconds = std::stol(s.substr(seconds_end + 1).to_string()); + nanoseconds *= std::pow(10, 9-(s.length() - (seconds_end + 1))); + if (nanoseconds < 0 || nanoseconds >= 1000 * 1000 * 1000) { + throw marshal_exception("Nanosecond out of bounds."); + } + } + std::chrono::nanoseconds result; + result += std::chrono::hours(hours); + result += std::chrono::minutes(minutes); + result += std::chrono::seconds(seconds); + result += std::chrono::nanoseconds(nanoseconds); + return result.count(); + } + virtual sstring to_string(const bytes& b) const override { + auto v = deserialize(b); + if (v.is_null()) { + return ""; + } + std::chrono::nanoseconds nanoseconds{from_value(v).get()}; + auto time = date::make_time(nanoseconds); + std::ostringstream str; + str << time; + return str.str(); + } + virtual ::shared_ptr as_cql3_type() const override { + return cql3::cql3_type::time; + } +}; + struct uuid_type_impl : concrete_type { uuid_type_impl() : concrete_type(uuid_type_name) {} virtual void serialize(const void* value, bytes::iterator& out) const override { @@ -2811,6 +3044,8 @@ reversed_type_impl::native_typeid() const { return _underlying_type->native_typeid(); } +thread_local const shared_ptr byte_type(make_shared()); +thread_local const shared_ptr short_type(make_shared()); thread_local const shared_ptr int32_type(make_shared()); thread_local const shared_ptr long_type(make_shared()); thread_local const shared_ptr ascii_type(make_shared()); @@ -2820,6 +3055,8 @@ thread_local const shared_ptr boolean_type(make_shared date_type(make_shared()); thread_local const shared_ptr timeuuid_type(make_shared()); thread_local const shared_ptr timestamp_type(make_shared()); +thread_local const shared_ptr simple_date_type(make_shared()); +thread_local const shared_ptr time_type(make_shared()); thread_local const shared_ptr uuid_type(make_shared()); thread_local const shared_ptr inet_addr_type(make_shared()); thread_local const shared_ptr float_type(make_shared()); @@ -2832,6 +3069,8 @@ thread_local const data_type empty_type(make_shared()); data_type abstract_type::parse_type(const sstring& name) { static thread_local const std::unordered_map types = { + { byte_type_name, byte_type }, + { short_type_name, short_type }, { int32_type_name, int32_type }, { long_type_name, long_type }, { ascii_type_name, ascii_type }, @@ -2841,6 +3080,8 @@ data_type abstract_type::parse_type(const sstring& name) { date_type_name, date_type }, { timeuuid_type_name, timeuuid_type }, { timestamp_type_name, timestamp_type }, + { simple_date_type_name, simple_date_type }, + { time_type_name, time_type }, { uuid_type_name, uuid_type }, { inet_addr_type_name, inet_addr_type }, { float_type_name, float_type }, @@ -2889,6 +3130,12 @@ data_value::data_value(const char* v) : data_value(make_new(utf8_type, sstring(v data_value::data_value(bool v) : data_value(make_new(boolean_type, v)) { } +data_value::data_value(int8_t v) : data_value(make_new(byte_type, v)) { +} + +data_value::data_value(int16_t v) : data_value(make_new(short_type, v)) { +} + data_value::data_value(int32_t v) : data_value(make_new(int32_type, v)) { } diff --git a/types.hh b/types.hh index 3a7ffd0432f6..35a0be6a4e9c 100644 --- a/types.hh +++ b/types.hh @@ -310,6 +310,8 @@ public: data_value(sstring); data_value(const char*); data_value(bool); + data_value(int8_t); + data_value(int16_t); data_value(int32_t); data_value(int64_t); data_value(utils::UUID); @@ -1082,6 +1084,8 @@ abstract_type::as_tri_comparator() const { using key_compare = serialized_compare; // Remember to update type_codec in transport/server.cc and cql3/cql3_type.cc +extern thread_local const shared_ptr byte_type; +extern thread_local const shared_ptr short_type; extern thread_local const shared_ptr int32_type; extern thread_local const shared_ptr long_type; extern thread_local const shared_ptr ascii_type; @@ -1091,6 +1095,8 @@ extern thread_local const shared_ptr boolean_type; extern thread_local const shared_ptr date_type; extern thread_local const shared_ptr timeuuid_type; extern thread_local const shared_ptr timestamp_type; +extern thread_local const shared_ptr simple_date_type; +extern thread_local const shared_ptr time_type; extern thread_local const shared_ptr uuid_type; extern thread_local const shared_ptr inet_addr_type; extern thread_local const shared_ptr float_type; @@ -1100,6 +1106,18 @@ extern thread_local const shared_ptr decimal_type; extern thread_local const shared_ptr counter_type; extern thread_local const data_type empty_type; +template <> +inline +shared_ptr data_type_for() { + return byte_type; +} + +template <> +inline +shared_ptr data_type_for() { + return short_type; +} + template <> inline shared_ptr data_type_for() { diff --git a/utils/date.h b/utils/date.h new file mode 100644 index 000000000000..f85e83691c79 --- /dev/null +++ b/utils/date.h @@ -0,0 +1,6528 @@ +#ifndef DATE_H +#define DATE_H + +// The MIT License (MIT) +// +// Copyright (c) 2015, 2016 Howard Hinnant +// Copyright (c) 2016 Adrian Colomitchi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that woud involve another several millennia of evolution). +// We did not mean to shout. + +#include +#include +#include +#if !(__cplusplus >= 201402) +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace date +{ + +//---------------+ +// Configuration | +//---------------+ + +// MSVC's constexpr support is still a WIP, even in VS2015. +// Fall back to a lesser mode to support it. +// TODO: Remove this or retest later once MSVC's constexpr improves. +#if defined(_MSC_VER) && _MSC_VER <= 1900 && ! defined(__clang__) +// MS cl compiler pre VS2017 +# define CONSTDATA const +# define CONSTCD11 +# define CONSTCD14 +# define NOEXCEPT _NOEXCEPT +#elif __cplusplus >= 201402 +// C++14 +# define CONSTDATA constexpr const +# define CONSTCD11 constexpr +# define CONSTCD14 constexpr +# define NOEXCEPT noexcept +#else +// C++11 +# define CONSTDATA constexpr const +# define CONSTCD11 constexpr +# define CONSTCD14 +# define NOEXCEPT noexcept +#endif + +//-----------+ +// Interface | +//-----------+ + +// durations + +using days = std::chrono::duration + , std::chrono::hours::period>>; + +using weeks = std::chrono::duration + , days::period>>; + +using years = std::chrono::duration + , days::period>>; + +using months = std::chrono::duration + >>; + +// time_point + +template + using sys_time = std::chrono::time_point; + +using sys_days = sys_time; +using sys_seconds = sys_time; + +struct local_t {}; + +template + using local_time = std::chrono::time_point; + +using local_seconds = local_time; +using local_days = local_time; + +// types + +struct last_spec +{ + explicit last_spec() = default; +}; + +class day; +class month; +class year; + +class weekday; +class weekday_indexed; +class weekday_last; + +class month_day; +class month_day_last; +class month_weekday; +class month_weekday_last; + +class year_month; + +class year_month_day; +class year_month_day_last; +class year_month_weekday; +class year_month_weekday_last; + +// date composition operators + +CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; +CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; + +CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; +CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; +CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; + +CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; + +CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; + +CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; + +CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; + +CONSTCD11 + year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; + +// Detailed interface + +// day + +class day +{ + unsigned char d_; + +public: + day() = default; + explicit CONSTCD11 day(unsigned d) NOEXCEPT; + + CONSTCD14 day& operator++() NOEXCEPT; + CONSTCD14 day operator++(int) NOEXCEPT; + CONSTCD14 day& operator--() NOEXCEPT; + CONSTCD14 day operator--(int) NOEXCEPT; + + CONSTCD14 day& operator+=(const days& d) NOEXCEPT; + CONSTCD14 day& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; + +CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; +CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; +CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; +CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const day& d); + +// month + +class month +{ + unsigned char m_; + +public: + month() = default; + explicit CONSTCD11 month(unsigned m) NOEXCEPT; + + CONSTCD14 month& operator++() NOEXCEPT; + CONSTCD14 month operator++(int) NOEXCEPT; + CONSTCD14 month& operator--() NOEXCEPT; + CONSTCD14 month operator--(int) NOEXCEPT; + + CONSTCD14 month& operator+=(const months& m) NOEXCEPT; + CONSTCD14 month& operator-=(const months& m) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; + +CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; +CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; +CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; +CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month& m); + +// year + +class year +{ + int64_t y_; + +public: + year() = default; + explicit CONSTCD11 year(int64_t y) NOEXCEPT; + + CONSTCD14 year& operator++() NOEXCEPT; + CONSTCD14 year operator++(int) NOEXCEPT; + CONSTCD14 year& operator--() NOEXCEPT; + CONSTCD14 year operator--(int) NOEXCEPT; + + CONSTCD14 year& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 year operator-() const NOEXCEPT; + CONSTCD11 year operator+() const NOEXCEPT; + + CONSTCD11 bool is_leap() const NOEXCEPT; + + CONSTCD11 explicit operator int64_t() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + static CONSTCD11 year min() NOEXCEPT; + static CONSTCD11 year max() NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; + +CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; +CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; +CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; +CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year& y); + +// weekday + +class weekday +{ + unsigned char wd_; +public: + weekday() = default; + explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; + explicit weekday(int) = delete; + CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; + CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 weekday& operator++() NOEXCEPT; + CONSTCD14 weekday operator++(int) NOEXCEPT; + CONSTCD14 weekday& operator--() NOEXCEPT; + CONSTCD14 weekday operator--(int) NOEXCEPT; + + CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; + CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; + CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; + +private: + static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; + +CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; +CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday& wd); + +// weekday_indexed + +class weekday_indexed +{ + unsigned char wd_ : 4; + unsigned char index_ : 4; + +public: + CONSTCD11 weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT; + + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_indexed& wdi); + +// weekday_last + +class weekday_last +{ + date::weekday wd_; + +public: + explicit CONSTCD11 weekday_last(const date::weekday& wd) NOEXCEPT; + + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_last& wdl); + +// year_month + +class year_month +{ + date::year y_; + date::month m_; + +public: + year_month() = default; + CONSTCD11 year_month(const date::year& y, const date::month& m) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + + CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; + CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; + +CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; +CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; +CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; + +CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; +CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; +CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month& ym); + +// month_day + +class month_day +{ + date::month m_; + date::day d_; + +public: + month_day() = default; + CONSTCD11 month_day(const date::month& m, const date::day& d) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::day day() const NOEXCEPT; + + CONSTCD14 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day& md); + +// month_day_last + +class month_day_last +{ + date::month m_; + +public: + CONSTCD11 explicit month_day_last(const date::month& m) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day_last& mdl); + +// month_weekday + +class month_weekday +{ + date::month m_; + date::weekday_indexed wdi_; +public: + CONSTCD11 month_weekday(const date::month& m, + const date::weekday_indexed& wdi) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday& mwd); + +// month_weekday_last + +class month_weekday_last +{ + date::month m_; + date::weekday_last wdl_; + +public: + CONSTCD11 month_weekday_last(const date::month& m, + const date::weekday_last& wd) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday_last& mwdl); + +// class year_month_day + +class year_month_day +{ + date::year y_; + date::month m_; + date::day d_; + +public: + year_month_day() = default; + CONSTCD11 year_month_day(const date::year& y, const date::month& m, + const date::day& d) NOEXCEPT; + CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; + + CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; + CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; + + CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; + +CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; +CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; +CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; +CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day& ymd); + +// year_month_day_last + +class year_month_day_last +{ + date::year y_; + date::month_day_last mdl_; + +public: + CONSTCD11 year_month_day_last(const date::year& y, + const date::month_day_last& mdl) NOEXCEPT; + + CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::month_day_last month_day_last() const NOEXCEPT; + CONSTCD14 date::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day_last& ymdl); + +// year_month_weekday + +class year_month_weekday +{ + date::year y_; + date::month m_; + date::weekday_indexed wdi_; + +public: + year_month_weekday() = default; + CONSTCD11 year_month_weekday(const date::year& y, const date::month& m, + const date::weekday_indexed& wdi) NOEXCEPT; + CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; + CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi); + +// year_month_weekday_last + +class year_month_weekday_last +{ + date::year y_; + date::month m_; + date::weekday_last wdl_; + +public: + CONSTCD11 year_month_weekday_last(const date::year& y, const date::month& m, + const date::weekday_last& wdl) NOEXCEPT; + + CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + +private: + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD11 +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl); + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 date::day operator "" _d(unsigned long long d) NOEXCEPT; +CONSTCD11 date::year operator "" _y(unsigned long long y) NOEXCEPT; + +// CONSTDATA date::month jan{1}; +// CONSTDATA date::month feb{2}; +// CONSTDATA date::month mar{3}; +// CONSTDATA date::month apr{4}; +// CONSTDATA date::month may{5}; +// CONSTDATA date::month jun{6}; +// CONSTDATA date::month jul{7}; +// CONSTDATA date::month aug{8}; +// CONSTDATA date::month sep{9}; +// CONSTDATA date::month oct{10}; +// CONSTDATA date::month nov{11}; +// CONSTDATA date::month dec{12}; +// +// CONSTDATA date::weekday sun{0u}; +// CONSTDATA date::weekday mon{1u}; +// CONSTDATA date::weekday tue{2u}; +// CONSTDATA date::weekday wed{3u}; +// CONSTDATA date::weekday thu{4u}; +// CONSTDATA date::weekday fri{5u}; +// CONSTDATA date::weekday sat{6u}; + +} // inline namespace literals +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +//----------------+ +// Implementation | +//----------------+ + +// utilities +namespace detail { + +template> +class save_stream +{ + std::basic_ostream& os_; + CharT fill_; + std::ios::fmtflags flags_; + std::locale loc_; + +public: + ~save_stream() + { + os_.fill(fill_); + os_.flags(flags_); + os_.imbue(loc_); + } + + save_stream(const save_stream&) = delete; + save_stream& operator=(const save_stream&) = delete; + + explicit save_stream(std::basic_ostream& os) + : os_(os) + , fill_(os.fill()) + , flags_(os.flags()) + , loc_(os.getloc()) + {} +}; + +#ifdef __GNUC__ +// GCC complains about __int128 with -pedantic or -pedantic-errors +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#endif + +template +struct choose_trunc_type +{ + static const int digits = std::numeric_limits::digits; + using type = typename std::conditional + < + digits < 32, + std::int32_t, + typename std::conditional + < + digits < 64, + std::int64_t, +#ifdef __SIZEOF_INT128__ + __int128 +#else + std::int64_t +#endif + >::type + >::type; +}; + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +template +CONSTCD11 +inline +typename std::enable_if +< + !std::chrono::treat_as_floating_point::value, + T +>::type +trunc(T t) NOEXCEPT +{ + return t; +} + +template +CONSTCD14 +inline +typename std::enable_if +< + std::chrono::treat_as_floating_point::value, + T +>::type +trunc(T t) NOEXCEPT +{ + using namespace std; + using I = typename choose_trunc_type::type; + CONSTDATA auto digits = numeric_limits::digits; + static_assert(digits < numeric_limits::digits, ""); + CONSTDATA auto max = I{1} << (digits-1); + CONSTDATA auto min = -max; + const auto negative = t < T{0}; + if (min <= t && t <= max && t != 0 && t == t) + { + t = static_cast(static_cast(t)); + if (t == 0 && negative) + t = -t; + } + return t; +} + +} // detail + +// trunc towards zero +template +CONSTCD11 +inline +To +trunc(const std::chrono::duration& d) +{ + return To{detail::trunc(std::chrono::duration_cast(d).count())}; +} + +#ifndef HAS_CHRONO_ROUNDING +# if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 +# define HAS_CHRONO_ROUNDING 1 +# elif defined(__cpp_lib_chrono) && __cplusplus > 201402 && __cpp_lib_chrono >= 201510 +# define HAS_CHRONO_ROUNDING 1 +# elif defined(_LIBCPP_VERSION) && __cplusplus > 201402 && _LIBCPP_VERSION >= 3800 +# define HAS_CHRONO_ROUNDING 1 +# else +# define HAS_CHRONO_ROUNDING 0 +# endif +#endif // HAS_CHRONO_ROUNDING + +#if HAS_CHRONO_ROUNDING == 0 + +// round down +template +CONSTCD14 +inline +To +floor(const std::chrono::duration& d) +{ + auto t = trunc(d); + if (t > d) + return t - To{1}; + return t; +} + +// round to nearest, to even on tie +template +CONSTCD14 +inline +To +round(const std::chrono::duration& d) +{ + auto t0 = floor(d); + auto t1 = t0 + To{1}; + if (t1 == To{0} && t0 < To{0}) + t1 = -t1; + auto diff0 = d - t0; + auto diff1 = t1 - d; + if (diff0 == diff1) + { + if (t0 - trunc(t0/2)*2 == To{0}) + return t0; + return t1; + } + if (diff0 < diff1) + return t0; + return t1; +} + +// round up +template +CONSTCD14 +inline +To +ceil(const std::chrono::duration& d) +{ + auto t = trunc(d); + if (t < d) + return t + To{1}; + return t; +} + +template ::is_signed + >::type> +CONSTCD11 +std::chrono::duration +abs(std::chrono::duration d) +{ + return d >= d.zero() ? d : -d; +} + +// round down +template +CONSTCD11 +inline +std::chrono::time_point +floor(const std::chrono::time_point& tp) +{ + using std::chrono::time_point; + return time_point{floor(tp.time_since_epoch())}; +} + +// round to nearest, to even on tie +template +CONSTCD11 +inline +std::chrono::time_point +round(const std::chrono::time_point& tp) +{ + using std::chrono::time_point; + return time_point{round(tp.time_since_epoch())}; +} + +// round up +template +CONSTCD11 +inline +std::chrono::time_point +ceil(const std::chrono::time_point& tp) +{ + using std::chrono::time_point; + return time_point{ceil(tp.time_since_epoch())}; +} + +#else // HAS_CHRONO_ROUNDING == 1 + +using std::chrono::floor; +using std::chrono::ceil; +using std::chrono::round; +using std::chrono::abs; + +#endif // HAS_CHRONO_ROUNDING + +// trunc towards zero +template +CONSTCD11 +inline +std::chrono::time_point +trunc(const std::chrono::time_point& tp) +{ + using std::chrono::time_point; + return time_point{trunc(tp.time_since_epoch())}; +} + +// day + +CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast(d)) {} +CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} +CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} +CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} +CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} +CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} +CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 31;} + +CONSTCD11 +inline +bool +operator==(const day& x, const day& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const day& x, const day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const day& x, const day& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const day& x, const day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const day& x, const day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const day& x, const day& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +days +operator-(const day& x, const day& y) NOEXCEPT +{ + return days{static_cast(static_cast(x) + - static_cast(y))}; +} + +CONSTCD11 +inline +day +operator+(const day& x, const days& y) NOEXCEPT +{ + return day{static_cast(x) + static_cast(y.count())}; +} + +CONSTCD11 +inline +day +operator+(const days& x, const day& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +day +operator-(const day& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const day& d) +{ + detail::save_stream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast(d); + return os; +} + +// month + +CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast(m)) {} +CONSTCD14 inline month& month::operator++() NOEXCEPT {if (++m_ == 13) m_ = 1; return *this;} +CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline month& month::operator--() NOEXCEPT {if (--m_ == 0) m_ = 12; return *this;} +CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +month& +month::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +month& +month::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} + +CONSTCD11 +inline +bool +operator==(const month& x, const month& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const month& x, const month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month& x, const month& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const month& x, const month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month& x, const month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month& x, const month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +months +operator-(const month& x, const month& y) NOEXCEPT +{ + auto const d = static_cast(x) - static_cast(y); + return months(d <= 11 ? d : d + 12); +} + +CONSTCD14 +inline +month +operator+(const month& x, const months& y) NOEXCEPT +{ + auto const mu = static_cast(static_cast(x)) - 1 + y.count(); + auto const yr = (mu >= 0 ? mu : mu-11) / 12; + return month{static_cast(mu - yr * 12 + 1)}; +} + +CONSTCD14 +inline +month +operator+(const months& x, const month& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +month +operator-(const month& x, const months& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month& m) +{ + switch (static_cast(m)) + { + case 1: + os << "Jan"; + break; + case 2: + os << "Feb"; + break; + case 3: + os << "Mar"; + break; + case 4: + os << "Apr"; + break; + case 5: + os << "May"; + break; + case 6: + os << "Jun"; + break; + case 7: + os << "Jul"; + break; + case 8: + os << "Aug"; + break; + case 9: + os << "Sep"; + break; + case 10: + os << "Oct"; + break; + case 11: + os << "Nov"; + break; + case 12: + os << "Dec"; + break; + default: + os << static_cast(m) << " is not a valid month"; + break; + } + return os; +} + +// year + +CONSTCD11 inline year::year(int64_t y) NOEXCEPT : y_(static_cast(y)) {} +CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} +CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} +CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} +CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} +CONSTCD11 inline year year::operator-() const NOEXCEPT {return year{-y_};} +CONSTCD11 inline year year::operator+() const NOEXCEPT {return *this;} + +CONSTCD11 +inline +bool +year::is_leap() const NOEXCEPT +{ + return y_ % 4 == 0 && (y_ % 100 != 0 || y_ % 400 == 0); +} + +CONSTCD11 inline year::operator int64_t() const NOEXCEPT {return y_;} +CONSTCD11 inline bool year::ok() const NOEXCEPT {return true;} + +CONSTCD11 +inline +year +year::min() NOEXCEPT +{ + return year{std::numeric_limits::min()}; +} + +CONSTCD11 +inline +year +year::max() NOEXCEPT +{ + return year{std::numeric_limits::max()}; +} + +CONSTCD11 +inline +bool +operator==(const year& x, const year& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const year& x, const year& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year& x, const year& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const year& x, const year& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year& x, const year& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year& x, const year& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +years +operator-(const year& x, const year& y) NOEXCEPT +{ + return years{static_cast(x) - static_cast(y)}; +} + +CONSTCD11 +inline +year +operator+(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast(x) + y.count()}; +} + +CONSTCD11 +inline +year +operator+(const years& x, const year& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +year +operator-(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast(x) - y.count()}; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year& y) +{ + detail::save_stream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::internal); + os.width(4 + (y < year{0})); + os << static_cast(y); + return os; +} + +// weekday + +CONSTCD11 +inline +unsigned char +weekday::weekday_from_days(int z) NOEXCEPT +{ + return static_cast(static_cast( + z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6)); +} + +CONSTCD11 +inline +weekday::weekday(unsigned wd) NOEXCEPT + : wd_(static_cast(wd)) + {} + +CONSTCD11 +inline +weekday::weekday(const sys_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD11 +inline +weekday::weekday(const local_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 7) wd_ = 0; return *this;} +CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 0) wd_ = 6; return *this;} +CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +weekday& +weekday::operator+=(const days& d) NOEXCEPT +{ + *this = *this + d; + return *this; +} + +CONSTCD14 +inline +weekday& +weekday::operator-=(const days& d) NOEXCEPT +{ + *this = *this - d; + return *this; +} + +CONSTCD11 +inline +weekday::operator unsigned() const NOEXCEPT +{ + return static_cast(wd_); +} + +CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} + +CONSTCD11 +inline +bool +operator==(const weekday& x, const weekday& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const weekday& x, const weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD14 +inline +days +operator-(const weekday& x, const weekday& y) NOEXCEPT +{ + auto const diff = static_cast(x) - static_cast(y); + return days{diff <= 6 ? diff : diff + 7}; +} + +CONSTCD14 +inline +weekday +operator+(const weekday& x, const days& y) NOEXCEPT +{ + auto const wdu = static_cast(static_cast(x)) + y.count(); + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; + return weekday{static_cast(wdu - wk * 7)}; +} + +CONSTCD14 +inline +weekday +operator+(const days& x, const weekday& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +weekday +operator-(const weekday& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday& wd) +{ + switch (static_cast(wd)) + { + case 0: + os << "Sun"; + break; + case 1: + os << "Mon"; + break; + case 2: + os << "Tue"; + break; + case 3: + os << "Wed"; + break; + case 4: + os << "Thu"; + break; + case 5: + os << "Fri"; + break; + case 6: + os << "Sat"; + break; + default: + os << static_cast(wd) << " is not a valid weekday"; + break; + } + return os; +} + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 +inline +date::day +operator "" _d(unsigned long long d) NOEXCEPT +{ + return date::day{static_cast(d)}; +} + +CONSTCD11 +inline +date::year +operator "" _y(unsigned long long y) NOEXCEPT +{ + return date::year(static_cast(y)); +} +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +CONSTDATA date::last_spec last{}; + +CONSTDATA date::month jan{1}; +CONSTDATA date::month feb{2}; +CONSTDATA date::month mar{3}; +CONSTDATA date::month apr{4}; +CONSTDATA date::month may{5}; +CONSTDATA date::month jun{6}; +CONSTDATA date::month jul{7}; +CONSTDATA date::month aug{8}; +CONSTDATA date::month sep{9}; +CONSTDATA date::month oct{10}; +CONSTDATA date::month nov{11}; +CONSTDATA date::month dec{12}; + +CONSTDATA date::weekday sun{0u}; +CONSTDATA date::weekday mon{1u}; +CONSTDATA date::weekday tue{2u}; +CONSTDATA date::weekday wed{3u}; +CONSTDATA date::weekday thu{4u}; +CONSTDATA date::weekday fri{5u}; +CONSTDATA date::weekday sat{6u}; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +} // inline namespace literals +#endif + +// weekday_indexed + +CONSTCD11 +inline +weekday +weekday_indexed::weekday() const NOEXCEPT +{ + return date::weekday{static_cast(wd_)}; +} + +CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} + +CONSTCD11 +inline +bool +weekday_indexed::ok() const NOEXCEPT +{ + return weekday().ok() && 1 <= index_ && index_ <= 5; +} + +CONSTCD11 +inline +weekday_indexed::weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT + : wd_(static_cast(static_cast(wd))) + , index_(static_cast(index)) + {} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_indexed& wdi) +{ + return os << wdi.weekday() << '[' << wdi.index() << ']'; +} + +CONSTCD11 +inline +weekday_indexed +weekday::operator[](unsigned index) const NOEXCEPT +{ + return {*this, index}; +} + +CONSTCD11 +inline +bool +operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return x.weekday() == y.weekday() && x.index() == y.index(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return !(x == y); +} + +// weekday_last + +CONSTCD11 inline date::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} +CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} +CONSTCD11 inline weekday_last::weekday_last(const date::weekday& wd) NOEXCEPT : wd_(wd) {} + +CONSTCD11 +inline +bool +operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_last& wdl) +{ + return os << wdl.weekday() << "[last]"; +} + +CONSTCD11 +inline +weekday_last +weekday::operator[](last_spec) const NOEXCEPT +{ + return weekday_last{*this}; +} + +// year_month + +CONSTCD11 +inline +year_month::year_month(const date::year& y, const date::month& m) NOEXCEPT + : y_(y) + , m_(m) + {} + +CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const months& dm) NOEXCEPT +{ + *this = *this + dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const months& dm) NOEXCEPT +{ + *this = *this - dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const years& dy) NOEXCEPT +{ + *this = *this + dy; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const years& dy) NOEXCEPT +{ + *this = *this - dy; + return *this; +} + +CONSTCD11 +inline +bool +operator==(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month())); +} + +CONSTCD11 +inline +bool +operator>(const year_month& x, const year_month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +year_month +operator+(const year_month& ym, const months& dm) NOEXCEPT +{ + auto dmi = static_cast(static_cast(ym.month())) - 1 + dm.count(); + auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; + dmi = dmi - dy * 12 + 1; + return (ym.year() + years(dy)) / month(static_cast(dmi)); +} + +CONSTCD14 +inline +year_month +operator+(const months& dm, const year_month& ym) NOEXCEPT +{ + return ym + dm; +} + +CONSTCD14 +inline +year_month +operator-(const year_month& ym, const months& dm) NOEXCEPT +{ + return ym + -dm; +} + +CONSTCD11 +inline +months +operator-(const year_month& x, const year_month& y) NOEXCEPT +{ + return (x.year() - y.year()) + + months(static_cast(x.month()) - static_cast(y.month())); +} + +CONSTCD11 +inline +year_month +operator+(const year_month& ym, const years& dy) NOEXCEPT +{ + return (ym.year() + dy) / ym.month(); +} + +CONSTCD11 +inline +year_month +operator+(const years& dy, const year_month& ym) NOEXCEPT +{ + return ym + dy; +} + +CONSTCD11 +inline +year_month +operator-(const year_month& ym, const years& dy) NOEXCEPT +{ + return ym + -dy; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month& ym) +{ + return os << ym.year() << '/' << ym.month(); +} + +// month_day + +CONSTCD11 +inline +month_day::month_day(const date::month& m, const date::day& d) NOEXCEPT + : m_(m) + , d_(d) + {} + +CONSTCD11 inline date::month month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline date::day month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +bool +month_day::ok() const NOEXCEPT +{ + CONSTDATA date::day d[] = + { + date::day(31), date::day(29), date::day(31), + date::day(30), date::day(31), date::day(30), + date::day(31), date::day(31), date::day(30), + date::day(31), date::day(30), date::day(31) + }; + return m_.ok() && date::day{1} <= d_ && d_ <= d[static_cast(m_)-1]; +} + +CONSTCD11 +inline +bool +operator==(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())); +} + +CONSTCD11 +inline +bool +operator>(const month_day& x, const month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day& md) +{ + return os << md.month() << '/' << md.day(); +} + +// month_day_last + +CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} +CONSTCD11 inline month_day_last::month_day_last(const date::month& m) NOEXCEPT : m_(m) {} + +CONSTCD11 +inline +bool +operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() < y.month(); +} + +CONSTCD11 +inline +bool +operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day_last& mdl) +{ + return os << mdl.month() << "/last"; +} + +// month_weekday + +CONSTCD11 +inline +month_weekday::month_weekday(const date::month& m, + const date::weekday_indexed& wdi) NOEXCEPT + : m_(m) + , wdi_(wdi) + {} + +CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_indexed +month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD11 +inline +bool +month_weekday::ok() const NOEXCEPT +{ + return m_.ok() && wdi_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday& mwd) +{ + return os << mwd.month() << '/' << mwd.weekday_indexed(); +} + +// month_weekday_last + +CONSTCD11 +inline +month_weekday_last::month_weekday_last(const date::month& m, + const date::weekday_last& wdl) NOEXCEPT + : m_(m) + , wdl_(wdl) + {} + +CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_last +month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD11 +inline +bool +month_weekday_last::ok() const NOEXCEPT +{ + return m_.ok() && wdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday_last& mwdl) +{ + return os << mwdl.month() << '/' << mwdl.weekday_last(); +} + +// year_month_day_last + +CONSTCD11 +inline +year_month_day_last::year_month_day_last(const date::year& y, + const date::month_day_last& mdl) NOEXCEPT + : y_(y) + , mdl_(mdl) + {} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} + +CONSTCD11 +inline +month_day_last +year_month_day_last::month_day_last() const NOEXCEPT +{ + return mdl_; +} + +CONSTCD14 +inline +day +year_month_day_last::day() const NOEXCEPT +{ + CONSTDATA date::day d[] = + { + date::day(31), date::day(28), date::day(31), + date::day(30), date::day(31), date::day(30), + date::day(31), date::day(31), date::day(30), + date::day(31), date::day(30), date::day(31) + }; + return month() != feb || !y_.is_leap() ? + d[static_cast(month()) - 1] : date::day{29}; +} + +CONSTCD14 +inline +year_month_day_last::operator sys_days() const NOEXCEPT +{ + return sys_days(year()/month()/day()); +} + +CONSTCD14 +inline +year_month_day_last::operator local_days() const NOEXCEPT +{ + return local_days(year()/month()/day()); +} + +CONSTCD11 +inline +bool +year_month_day_last::ok() const NOEXCEPT +{ + return y_.ok() && mdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month_day_last() == y.month_day_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month_day_last() < y.month_day_last())); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day_last& ymdl) +{ + return os << ymdl.year() << '/' << ymdl.month_day_last(); +} + +CONSTCD14 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return (ymdl.year() / ymdl.month() + dm) / last; +} + +CONSTCD14 +inline +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dm; +} + +CONSTCD14 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return ymdl + (-dm); +} + +CONSTCD11 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return {ymdl.year()+dy, ymdl.month_day_last()}; +} + +CONSTCD11 +inline +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dy; +} + +CONSTCD11 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return ymdl + (-dy); +} + +// year_month_day + +CONSTCD11 +inline +year_month_day::year_month_day(const date::year& y, const date::month& m, + const date::day& d) NOEXCEPT + : y_(y) + , m_(m) + , d_(d) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT + : y_(ymdl.year()) + , m_(ymdl.month()) + , d_(ymdl.day()) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(sys_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(local_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD14 +inline +days +year_month_day::to_days() const NOEXCEPT +{ + static_assert(std::numeric_limits::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const y = static_cast(y_) - (m_ <= feb); + auto const m = static_cast(m_); + auto const d = static_cast(d_); + auto const era = (y >= 0 ? y : y-399) / 400; + auto const yoe = static_cast(y - era * 400); // [0, 399] + auto const doy = (153*(m > 2 ? m-3 : m+9) + 2)/5 + d-1; // [0, 365] + auto const doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096] + return days{era * 146097 + static_cast(doe) - 719468}; +} + +CONSTCD14 +inline +year_month_day::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_day::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_day::ok() const NOEXCEPT +{ + if (!(y_.ok() && m_.ok())) + return false; + return date::day{1} <= d_ && d_ <= (y_ / m_ / last).day(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())))); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day& ymd) +{ + detail::save_stream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os << ymd.year() << '-'; + os.width(2); + os << static_cast(ymd.month()) << '-'; + os << ymd.day(); + return os; +} + +CONSTCD14 +inline +year_month_day +year_month_day::from_days(days dp) NOEXCEPT +{ + static_assert(std::numeric_limits::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const z = dp.count() + 719468; + auto const era = (z >= 0 ? z : z - 146096) / 146097; + auto const doe = static_cast(z - era * 146097); // [0, 146096] + auto const yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399] + auto const y = static_cast(yoe) + era * 400; + auto const doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365] + auto const mp = (5*doy + 2)/153; // [0, 11] + auto const d = doy - (153*mp+2)/5 + 1; // [1, 31] + auto const m = mp < 10 ? mp+3 : mp-9; // [1, 12] + return year_month_day{date::year{y + (m <= 2)}, date::month(m), date::day(d)}; +} + +CONSTCD14 +inline +year_month_day +operator+(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return (ymd.year() / ymd.month() + dm) / ymd.day(); +} + +CONSTCD14 +inline +year_month_day +operator+(const months& dm, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dm; +} + +CONSTCD14 +inline +year_month_day +operator-(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return ymd + (-dm); +} + +CONSTCD11 +inline +year_month_day +operator+(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return (ymd.year() + dy) / ymd.month() / ymd.day(); +} + +CONSTCD11 +inline +year_month_day +operator+(const years& dy, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dy; +} + +CONSTCD11 +inline +year_month_day +operator-(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return ymd + (-dy); +} + +// year_month_weekday + +CONSTCD11 +inline +year_month_weekday::year_month_weekday(const date::year& y, const date::month& m, + const date::weekday_indexed& wdi) + NOEXCEPT + : y_(y) + , m_(m) + , wdi_(wdi) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday::weekday() const NOEXCEPT +{ + return wdi_.weekday(); +} + +CONSTCD11 +inline +unsigned +year_month_weekday::index() const NOEXCEPT +{ + return wdi_.index(); +} + +CONSTCD11 +inline +weekday_indexed +year_month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD14 +inline +year_month_weekday::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_weekday::ok() const NOEXCEPT +{ + if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) + return false; + if (wdi_.index() <= 4) + return true; + auto d2 = wdi_.weekday() - date::weekday(static_cast(y_/m_/1)) + days((wdi_.index()-1)*7 + 1); + return static_cast(d2.count()) <= static_cast((y_/m_/last).day()); +} + +CONSTCD14 +inline +year_month_weekday +year_month_weekday::from_days(days d) NOEXCEPT +{ + sys_days dp{d}; + auto const wd = date::weekday(dp); + auto const ymd = year_month_day(dp); + return {ymd.year(), ymd.month(), wd[(static_cast(ymd.day())-1)/7+1]}; +} + +CONSTCD14 +inline +days +year_month_weekday::to_days() const NOEXCEPT +{ + auto d = sys_days(y_/m_/1); + return (d + (wdi_.weekday() - date::weekday(d) + days{(wdi_.index()-1)*7}) + ).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi) +{ + return os << ymwdi.year() << '/' << ymwdi.month() + << '/' << ymwdi.weekday_indexed(); +} + +CONSTCD14 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); +} + +CONSTCD14 +inline +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dm; +} + +CONSTCD14 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return ymwd + (-dm); +} + +CONSTCD11 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dy; +} + +CONSTCD11 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return ymwd + (-dy); +} + +// year_month_weekday_last + +CONSTCD11 +inline +year_month_weekday_last::year_month_weekday_last(const date::year& y, + const date::month& m, + const date::weekday_last& wdl) NOEXCEPT + : y_(y) + , m_(m) + , wdl_(wdl) + {} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday_last::weekday() const NOEXCEPT +{ + return wdl_.weekday(); +} + +CONSTCD11 +inline +weekday_last +year_month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD14 +inline +year_month_weekday_last::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday_last::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD11 +inline +bool +year_month_weekday_last::ok() const NOEXCEPT +{ + return y_.ok() && m_.ok() && wdl_.ok(); +} + +CONSTCD14 +inline +days +year_month_weekday_last::to_days() const NOEXCEPT +{ + auto const d = sys_days(y_/m_/last); + return (d - (date::weekday{d} - wdl_.weekday())).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl) +{ + return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last(); +} + +CONSTCD14 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); +} + +CONSTCD14 +inline +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dm; +} + +CONSTCD14 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return ymwdl + (-dm); +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dy; +} + +CONSTCD11 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return ymwdl + (-dy); +} + +// year_month from operator/() + +CONSTCD11 +inline +year_month +operator/(const year& y, const month& m) NOEXCEPT +{ + return {y, m}; +} + +CONSTCD11 +inline +year_month +operator/(const year& y, int m) NOEXCEPT +{ + return y / month(static_cast(m)); +} + +// month_day from operator/() + +CONSTCD11 +inline +month_day +operator/(const month& m, const day& d) NOEXCEPT +{ + return {m, d}; +} + +CONSTCD11 +inline +month_day +operator/(const day& d, const month& m) NOEXCEPT +{ + return m / d; +} + +CONSTCD11 +inline +month_day +operator/(const month& m, int d) NOEXCEPT +{ + return m / day(static_cast(d)); +} + +CONSTCD11 +inline +month_day +operator/(int m, const day& d) NOEXCEPT +{ + return month(static_cast(m)) / d; +} + +CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} + +// month_day_last from operator/() + +CONSTCD11 +inline +month_day_last +operator/(const month& m, last_spec) NOEXCEPT +{ + return month_day_last{m}; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, const month& m) NOEXCEPT +{ + return m/last; +} + +CONSTCD11 +inline +month_day_last +operator/(int m, last_spec) NOEXCEPT +{ + return month(static_cast(m))/last; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, int m) NOEXCEPT +{ + return m/last; +} + +// month_weekday from operator/() + +CONSTCD11 +inline +month_weekday +operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT +{ + return {m, wdi}; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT +{ + return m / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(int m, const weekday_indexed& wdi) NOEXCEPT +{ + return month(static_cast(m)) / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, int m) NOEXCEPT +{ + return m / wdi; +} + +// month_weekday_last from operator/() + +CONSTCD11 +inline +month_weekday_last +operator/(const month& m, const weekday_last& wdl) NOEXCEPT +{ + return {m, wdl}; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, const month& m) NOEXCEPT +{ + return m / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(int m, const weekday_last& wdl) NOEXCEPT +{ + return month(static_cast(m)) / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, int m) NOEXCEPT +{ + return m / wdl; +} + +// year_month_day from operator/() + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, const day& d) NOEXCEPT +{ + return {ym.year(), ym.month(), d}; +} + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, int d) NOEXCEPT +{ + return ym / day(static_cast(d)); +} + +CONSTCD11 +inline +year_month_day +operator/(const year& y, const month_day& md) NOEXCEPT +{ + return y / md.month() / md.day(); +} + +CONSTCD11 +inline +year_month_day +operator/(int y, const month_day& md) NOEXCEPT +{ + return year(y) / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, const year& y) NOEXCEPT +{ + return y / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, int y) NOEXCEPT +{ + return year(y) / md; +} + +// year_month_day_last from operator/() + +CONSTCD11 +inline +year_month_day_last +operator/(const year_month& ym, last_spec) NOEXCEPT +{ + return {ym.year(), month_day_last{ym.month()}}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const year& y, const month_day_last& mdl) NOEXCEPT +{ + return {y, mdl}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(int y, const month_day_last& mdl) NOEXCEPT +{ + return year(y) / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, const year& y) NOEXCEPT +{ + return y / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, int y) NOEXCEPT +{ + return year(y) / mdl; +} + +// year_month_weekday from operator/() + +CONSTCD11 +inline +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT +{ + return {ym.year(), ym.month(), wdi}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT +{ + return {y, mwd.month(), mwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT +{ + return year(y) / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT +{ + return y / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT +{ + return year(y) / mwd; +} + +// year_month_weekday_last from operator/() + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT +{ + return {ym.year(), ym.month(), wdl}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT +{ + return {y, mwdl.month(), mwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT +{ + return year(y) / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT +{ + return y / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT +{ + return year(y) / mwdl; +} + +// time_of_day + +enum {am = 1, pm}; + +namespace detail +{ + +// width::value is the number of fractional decimal digits in 1/n +// width<0>::value and width<1>::value are defined to be 0 +// If 1/n takes more than 18 fractional decimal digits, +// the result is truncated to 19. +// Example: width<2>::value == 1 +// Example: width<3>::value == 19 +// Example: width<4>::value == 2 +// Example: width<10>::value == 1 +// Example: width<1000>::value == 3 +template +struct width +{ + static CONSTDATA unsigned value = 1 + width::value; +}; + +template +struct width +{ + static CONSTDATA unsigned value = 0; +}; + +template +struct static_pow10 +{ +private: + static CONSTDATA std::uint64_t h = static_pow10::value; +public: + static CONSTDATA std::uint64_t value = h * h * (exp % 2 ? 10 : 1); +}; + +template <> +struct static_pow10<0> +{ + static CONSTDATA std::uint64_t value = 1; +}; + +template +struct make_precision +{ + using type = std::chrono::duration::value>>; + static CONSTDATA unsigned width = w; +}; + +template +struct make_precision +{ + using type = std::chrono::microseconds; + static CONSTDATA unsigned width = 6; +}; + +template ::type::period::den>::value> +class decimal_format_seconds +{ +public: + using precision = typename make_precision::type; + static auto CONSTDATA width = make_precision::width; + +private: + std::chrono::seconds s_; + precision sub_s_; + +public: + CONSTCD11 explicit decimal_format_seconds(const Duration& d) NOEXCEPT + : s_(std::chrono::duration_cast(d)) + , sub_s_(std::chrono::duration_cast(d - s_)) + {} + + CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_;} + CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_;} + CONSTCD11 precision subseconds() const NOEXCEPT {return sub_s_;} + + CONSTCD14 precision to_duration() const NOEXCEPT + { + return s_ + sub_s_; + } + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, const decimal_format_seconds& x) + { + date::detail::save_stream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << x.s_.count() << + std::use_facet>(os.getloc()).decimal_point(); + os.width(width); + os << x.sub_s_.count(); + return os; + } +}; + +template +class decimal_format_seconds +{ + static CONSTDATA unsigned w = 0; +public: + using precision = std::chrono::seconds; + static auto CONSTDATA width = make_precision::width; +private: + + std::chrono::seconds s_; + +public: + CONSTCD11 explicit decimal_format_seconds(const precision& s) NOEXCEPT + : s_(s) + {} + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, const decimal_format_seconds& x) + { + date::detail::save_stream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << x.s_.count(); + return os; + } +}; + +enum class classify +{ + not_valid, + hour, + minute, + second, + subsecond +}; + +template +struct classify_duration +{ + static CONSTDATA classify value = + std::is_convertible::value + ? classify::hour : + std::is_convertible::value + ? classify::minute : + std::is_convertible::value + ? classify::second : + std::chrono::treat_as_floating_point::value + ? classify::not_valid : + classify::subsecond; +}; + +class time_of_day_base +{ +protected: + std::chrono::hours h_; + unsigned char mode_; + bool neg_; + + enum {is24hr}; + + CONSTCD11 time_of_day_base(std::chrono::hours h, bool neg, unsigned m) NOEXCEPT + : h_(abs(h)) + , mode_(static_cast(m)) + , neg_(neg) + {} + + CONSTCD14 void make24() NOEXCEPT; + CONSTCD14 void make12() NOEXCEPT; + + CONSTCD14 std::chrono::hours to24hr() const; +}; + +CONSTCD14 +inline +std::chrono::hours +time_of_day_base::to24hr() const +{ + auto h = h_; + if (mode_ == am || mode_ == pm) + { + CONSTDATA auto h12 = std::chrono::hours(12); + if (mode_ == pm) + { + if (h != h12) + h = h + h12; + } + else if (h == h12) + h = std::chrono::hours(0); + } + return h; +} + +CONSTCD14 +inline +void +time_of_day_base::make24() NOEXCEPT +{ + h_ = to24hr(); + mode_ = is24hr; +} + +CONSTCD14 +inline +void +time_of_day_base::make12() NOEXCEPT +{ + if (mode_ == is24hr) + { + CONSTDATA auto h12 = std::chrono::hours(12); + if (h_ >= h12) + { + if (h_ > h12) + h_ = h_ - h12; + mode_ = pm; + } + else + { + if (h_ == std::chrono::hours(0)) + h_ = h12; + mode_ = am; + } + } +} + +template ::value> +class time_of_day_storage; + +template +class time_of_day_storage, detail::classify::hour> + : private detail::time_of_day_base +{ + using base = detail::time_of_day_base; + +public: + using precision = std::chrono::hours; + + CONSTCD11 explicit time_of_day_storage(std::chrono::hours since_midnight) NOEXCEPT + : base(since_midnight, since_midnight < std::chrono::hours{0}, is24hr) + {} + + CONSTCD11 explicit time_of_day_storage(std::chrono::hours h, unsigned md) NOEXCEPT + : base(h, h < std::chrono::hours{0}, md) + {} + + CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} + CONSTCD11 unsigned mode() const NOEXCEPT {return mode_;} + + CONSTCD14 explicit operator precision() const NOEXCEPT + { + auto p = to24hr(); + if (neg_) + p = -p; + return p; + } + + CONSTCD14 precision to_duration() const NOEXCEPT + { + return static_cast(*this); + } + + CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} + CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, const time_of_day_storage& t) + { + using namespace std; + detail::save_stream _(os); + if (t.neg_) + os << '-'; + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + if (t.mode_ != am && t.mode_ != pm) + os.width(2); + os << t.h_.count(); + switch (t.mode_) + { + case time_of_day_storage::is24hr: + os << "00"; + break; + case am: + os << "am"; + break; + case pm: + os << "pm"; + break; + } + return os; + } +}; + +template +class time_of_day_storage, detail::classify::minute> + : private detail::time_of_day_base +{ + using base = detail::time_of_day_base; + + std::chrono::minutes m_; + +public: + using precision = std::chrono::minutes; + + CONSTCD11 explicit time_of_day_storage(std::chrono::minutes since_midnight) NOEXCEPT + : base(std::chrono::duration_cast(since_midnight), + since_midnight < std::chrono::minutes{0}, is24hr) + , m_(abs(since_midnight) - h_) + {} + + CONSTCD11 explicit time_of_day_storage(std::chrono::hours h, std::chrono::minutes m, + unsigned md) NOEXCEPT + : base(h, false, md) + , m_(m) + {} + + CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} + CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;} + CONSTCD11 unsigned mode() const NOEXCEPT {return mode_;} + + CONSTCD14 explicit operator precision() const NOEXCEPT + { + auto p = to24hr() + m_; + if (neg_) + p = -p; + return p; + } + + CONSTCD14 precision to_duration() const NOEXCEPT + { + return static_cast(*this); + } + + CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} + CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, const time_of_day_storage& t) + { + using namespace std; + detail::save_stream _(os); + if (t.neg_) + os << '-'; + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + if (t.mode_ != am && t.mode_ != pm) + os.width(2); + os << t.h_.count() << ':'; + os.width(2); + os << t.m_.count(); + switch (t.mode_) + { + case am: + os << "am"; + break; + case pm: + os << "pm"; + break; + } + return os; + } +}; + +template +class time_of_day_storage, detail::classify::second> + : private detail::time_of_day_base +{ + using base = detail::time_of_day_base; + + std::chrono::minutes m_; + std::chrono::seconds s_; + +public: + using precision = std::chrono::seconds; + + CONSTCD11 explicit time_of_day_storage(std::chrono::seconds since_midnight) NOEXCEPT + : base(std::chrono::duration_cast(since_midnight), + since_midnight < std::chrono::seconds{0}, is24hr) + , m_(std::chrono::duration_cast(abs(since_midnight) - h_)) + , s_(abs(since_midnight) - h_ - m_) + {} + + CONSTCD11 explicit time_of_day_storage(std::chrono::hours h, std::chrono::minutes m, + std::chrono::seconds s, unsigned md) NOEXCEPT + : base(h, false, md) + , m_(m) + , s_(s) + {} + + CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} + CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;} + CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_;} + CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_;} + CONSTCD11 unsigned mode() const NOEXCEPT {return mode_;} + + CONSTCD14 explicit operator precision() const NOEXCEPT + { + auto p = to24hr() + s_ + m_; + if (neg_) + p = -p; + return p; + } + + CONSTCD14 precision to_duration() const NOEXCEPT + { + return static_cast(*this); + } + + CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} + CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, const time_of_day_storage& t) + { + using namespace std; + detail::save_stream _(os); + if (t.neg_) + os << '-'; + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + if (t.mode_ != am && t.mode_ != pm) + os.width(2); + os << t.h_.count() << ':'; + os.width(2); + os << t.m_.count() << ':'; + os.width(2); + os << t.s_.count(); + switch (t.mode_) + { + case am: + os << "am"; + break; + case pm: + os << "pm"; + break; + } + return os; + } +}; + +template +class time_of_day_storage, detail::classify::subsecond> + : private detail::time_of_day_base +{ +public: + using Duration = std::chrono::duration; + using dfs = decimal_format_seconds::type>; + using precision = typename dfs::precision; + +private: + using base = detail::time_of_day_base; + + std::chrono::minutes m_; + dfs s_; + +public: + CONSTCD11 explicit time_of_day_storage(Duration since_midnight) NOEXCEPT + : base(std::chrono::duration_cast(since_midnight), + since_midnight < Duration{0}, is24hr) + , m_(std::chrono::duration_cast(abs(since_midnight) - h_)) + , s_(abs(since_midnight) - h_ - m_) + {} + + CONSTCD11 explicit time_of_day_storage(std::chrono::hours h, std::chrono::minutes m, + std::chrono::seconds s, precision sub_s, + unsigned md) NOEXCEPT + : base(h, false, md) + , m_(m) + , s_(s + sub_s) + {} + + CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} + CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;} + CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_.seconds();} + CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_.seconds();} + CONSTCD11 precision subseconds() const NOEXCEPT {return s_.subseconds();} + CONSTCD11 unsigned mode() const NOEXCEPT {return mode_;} + + CONSTCD14 explicit operator precision() const NOEXCEPT + { + auto p = to24hr() + s_.to_duration() + m_; + if (neg_) + p = -p; + return p; + } + + CONSTCD14 precision to_duration() const NOEXCEPT + { + return static_cast(*this); + } + + CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} + CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, const time_of_day_storage& t) + { + using namespace std; + detail::save_stream _(os); + if (t.neg_) + os << '-'; + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + if (t.mode_ != am && t.mode_ != pm) + os.width(2); + os << t.h_.count() << ':'; + os.width(2); + os << t.m_.count() << ':' << t.s_; + switch (t.mode_) + { + case am: + os << "am"; + break; + case pm: + os << "pm"; + break; + } + return os; + } +}; + +} // namespace detail + +template +class time_of_day + : public detail::time_of_day_storage +{ + using base = detail::time_of_day_storage; +public: +#if !(defined(_MSC_VER) && !defined(__clang__)) + // C++11 + using base::base; +#else + // MS cl compiler workaround. + template + CONSTCD11 + explicit time_of_day(Args&& ...args) NOEXCEPT + : base(std::forward(args)...) + {} +#endif +}; + +template ::value>::type> +CONSTCD11 +inline +time_of_day> +make_time(const std::chrono::duration& d) +{ + return time_of_day>(d); +} + +CONSTCD11 +inline +time_of_day +make_time(const std::chrono::hours& h, unsigned md) +{ + return time_of_day(h, md); +} + +CONSTCD11 +inline +time_of_day +make_time(const std::chrono::hours& h, const std::chrono::minutes& m, + unsigned md) +{ + return time_of_day(h, m, md); +} + +CONSTCD11 +inline +time_of_day +make_time(const std::chrono::hours& h, const std::chrono::minutes& m, + const std::chrono::seconds& s, unsigned md) +{ + return time_of_day(h, m, s, md); +} + +template >::value>::type> +CONSTCD11 +inline +time_of_day> +make_time(const std::chrono::hours& h, const std::chrono::minutes& m, + const std::chrono::seconds& s, const std::chrono::duration& sub_s, + unsigned md) +{ + return time_of_day>(h, m, s, sub_s, md); +} + +template +inline +typename std::enable_if +< + !std::chrono::treat_as_floating_point::value && + std::ratio_less::value + , std::basic_ostream& +>::type +operator<<(std::basic_ostream& os, const sys_time& tp) +{ + auto const dp = floor(tp); + return os << year_month_day(dp) << ' ' << make_time(tp-dp); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const sys_days& dp) +{ + return os << year_month_day(dp); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const local_time& ut) +{ + return os << sys_time{ut.time_since_epoch()}; +} + +// to_stream + +template +void +to_stream(std::basic_ostream& os, const CharT* fmt, + const local_time& tp, const std::string* abbrev = nullptr, + const std::chrono::seconds* offset_sec = nullptr) +{ + using namespace std; + using namespace std::chrono; + tm tm; + auto& facet = use_facet>(os.getloc()); + auto command = false; + CharT modified = CharT{}; + for (; *fmt; ++fmt) + { + if (!command && modified != CharT{}) + throw std::logic_error("loop invariant broken: !command && modified"); + else if (modified != CharT{} && modified != CharT{'E'} && modified != CharT{'O'}) + throw std::logic_error(std::string("bad value for modified: ") + char(modified)); + switch (*fmt) + { + case 'a': + case 'A': + if (command) + { + if (modified == CharT{}) + { + tm.tm_wday = static_cast(static_cast( + weekday{floor(tp)})); + const CharT f[] = {'%', *fmt}; + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'b': + case 'B': + case 'h': + if (command) + { + if (modified == CharT{}) + { + tm.tm_mon = static_cast(static_cast( + year_month_day{floor(tp)}.month())) - 1; + const CharT f[] = {'%', *fmt}; + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'c': + case 'x': + case 'X': + if (command) + { + if (modified == CharT{'O'}) + os << CharT{'%'} << modified << *fmt; + else + { + tm = std::tm{}; + auto ld = floor(tp); + auto ymd = year_month_day{ld}; + auto hms = make_time(floor(tp - ld)); + tm.tm_sec = static_cast(hms.seconds().count()); + tm.tm_min = static_cast(hms.minutes().count()); + tm.tm_hour = static_cast(hms.hours().count()); + tm.tm_mday = static_cast(static_cast(ymd.day())); + tm.tm_mon = static_cast(static_cast(ymd.month()) - 1); + tm.tm_year = static_cast(ymd.year()) - 1900; + tm.tm_wday = static_cast(static_cast(weekday{ld})); + tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); + CharT f[3] = {'%'}; + auto fe = begin(f) + 1; + if (modified == CharT{'E'}) + *fe++ = modified; + *fe++ = *fmt; + facet.put(os, os, os.fill(), &tm, begin(f), fe); + } + command = false; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'C': + if (command) + { + auto y = static_cast(year_month_day{floor(tp)}.year()); + if (modified == CharT{'E'}) + { + tm.tm_year = y - 1900; + CharT f[3] = {'%', 'E', 'C'}; + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } + else if (modified == CharT{}) + { + detail::save_stream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + if (y >= 0) + { + os.width(2); + os << y/100; + } + else + { + os << CharT{'-'}; + os.width(2); + os << -(y-99)/100; + } + } + else + { + os << CharT{'%'} << modified << *fmt; + } + command = false; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'd': + case 'e': + if (command) + { + auto d = static_cast(static_cast( + year_month_day{floor(tp)}.day())); + if (modified == CharT{'O'}) + { + tm.tm_mday = d; + CharT f[3] = {'%', 'O', *fmt}; + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } + else if (modified == CharT{}) + { + detail::save_stream _(os); + if (*fmt == CharT{'d'}) + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << d; + } + else + { + os << CharT{'%'} << modified << *fmt; + } + command = false; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'D': + if (command) + { + if (modified == CharT{}) + { + auto ymd = year_month_day{floor(tp)}; + detail::save_stream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast(ymd.month()) << CharT{'/'}; + os.width(2); + os << static_cast(ymd.day()) << CharT{'/'}; + os.width(2); + os << static_cast(ymd.year()) % 100; + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'F': + if (command) + { + if (modified == CharT{}) + os << floor(tp); + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'g': + case 'G': + if (command) + { + if (modified == CharT{}) + { + auto ld = floor(tp); + auto y = year_month_day{ld + days{3}}.year(); + auto start = local_days{(y - years{1})/date::dec/thu[last]} + (mon-thu); + if (ld < start) + --y; + if (*fmt == CharT{'G'}) + os << y; + else + { + detail::save_stream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast(y) % 100; + } + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'H': + case 'I': + if (command) + { + auto hms = make_time(floor(tp - floor(tp))); + if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_hour = static_cast(hms.hours().count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + modified = CharT{}; + } + else if (modified == CharT{}) + { + if (*fmt == CharT{'I'}) + hms.make12(); + if (hms.hours() < hours{10}) + os << CharT{'0'}; + os << hms.hours().count(); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'j': + if (command) + { + if (modified == CharT{}) + { + auto ld = floor(tp); + auto y = year_month_day{ld}.year(); + auto doy = ld - local_days{y/jan/1} + days{1}; + detail::save_stream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(3); + os << doy.count(); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'm': + if (command) + { + auto m = static_cast(year_month_day{floor(tp)}.month()); + if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_mon = static_cast(m-1); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + modified = CharT{}; + } + else if (modified == CharT{}) + { + if (m < 10) + os << CharT{'0'}; + os << m; + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'M': + if (command) + { + auto hms = make_time(floor(tp - floor(tp))); + if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_min = static_cast(hms.minutes().count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + modified = CharT{}; + } + else if (modified == CharT{}) + { + if (hms.minutes() < minutes{10}) + os << CharT{'0'}; + os << hms.minutes().count(); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'n': + if (command) + { + if (modified == CharT{}) + os << CharT{'\n'}; + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'p': + if (command) + { + if (modified == CharT{}) + { + auto h = floor(tp - floor(tp)); + const CharT f[] = {'%', *fmt}; + tm.tm_hour = static_cast(h.count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'r': + if (command) + { + if (modified == CharT{}) + { + auto hms = make_time(floor(tp - floor(tp))); + const CharT f[] = {'%', *fmt}; + tm.tm_hour = static_cast(hms.hours().count()); + tm.tm_min = static_cast(hms.minutes().count()); + tm.tm_sec = static_cast(hms.seconds().count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'R': + if (command) + { + if (modified == CharT{}) + { + auto hms = make_time(floor(tp - floor(tp))); + if (hms.hours() < hours{10}) + os << CharT{'0'}; + os << hms.hours().count() << CharT{':'}; + if (hms.minutes() < minutes{10}) + os << CharT{'0'}; + os << hms.minutes().count(); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'S': + if (command) + { + if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + auto hms = make_time(floor(tp - floor(tp))); + tm.tm_sec = static_cast(hms.seconds().count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + modified = CharT{}; + } + else if (modified == CharT{}) + { + auto fs = (tp - floor(tp)) % minutes{1}; + os << detail::decimal_format_seconds(fs); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 't': + if (command) + { + if (modified == CharT{}) + os << CharT{'\t'}; + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'T': + if (command) + { + if (modified == CharT{}) + { + using CT = typename common_type::type; + os << time_of_day{tp - floor(tp)}; + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'u': + if (command) + { + auto wd = static_cast(weekday{floor(tp)}); + if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_wday = static_cast(wd); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + modified = CharT{}; + } + else if (modified == CharT{}) + { + os << (wd != 0 ? wd : 7u); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'U': + if (command) + { + auto ld = floor(tp); + auto ymd = year_month_day{ld}; + if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = static_cast(ymd.year()) - 1900; + tm.tm_wday = static_cast(static_cast(weekday{ld})); + tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + modified = CharT{}; + } + else if (modified == CharT{}) + { + auto st = local_days{sun[1]/jan/ymd.year()}; + if (ld < st) + os << CharT{'0'} << CharT{'0'}; + else + { + auto wn = duration_cast(ld - st).count() + 1; + if (wn < 10) + os << CharT{'0'}; + os << wn; + } + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'V': + if (command) + { + auto ld = floor(tp); + if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + auto ymd = year_month_day{ld}; + tm.tm_year = static_cast(ymd.year()) - 1900; + tm.tm_wday = static_cast(static_cast(weekday{ld})); + tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + modified = CharT{}; + } + else if (modified == CharT{}) + { + auto y = year_month_day{ld + days{3}}.year(); + auto st = local_days{(y - years{1})/12/thu[last]} + (mon-thu); + if (ld < st) + { + --y; + st = local_days{(y - years{1})/12/thu[last]} + (mon-thu); + } + auto wn = duration_cast(ld - st).count() + 1; + if (wn < 10) + os << CharT{'0'}; + os << wn; + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'w': + if (command) + { + auto wd = static_cast(weekday{floor(tp)}); + if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_wday = static_cast(wd); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + modified = CharT{}; + } + else if (modified == CharT{}) + { + os << wd; + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'W': + if (command) + { + auto ld = floor(tp); + auto ymd = year_month_day{ld}; + if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = static_cast(ymd.year()) - 1900; + tm.tm_wday = static_cast(static_cast(weekday{ld})); + tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + modified = CharT{}; + } + else if (modified == CharT{}) + { + auto st = local_days{mon[1]/jan/ymd.year()}; + if (ld < st) + os << CharT{'0'} << CharT{'0'}; + else + { + auto wn = duration_cast(ld - st).count() + 1; + if (wn < 10) + os << CharT{'0'}; + os << wn; + } + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'y': + if (command) + { + auto y = static_cast(year_month_day{floor(tp)}.year()); + if (modified != CharT{}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = y - 1900; + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + modified = CharT{}; + } + else if (modified == CharT{}) + { + y = std::abs(y) % 100; + if (y < 10) + os << CharT{'0'}; + os << y; + } + command = false; + } + else + os << *fmt; + break; + case 'Y': + if (command) + { + auto y = year_month_day{floor(tp)}.year(); + if (modified == CharT{'E'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = static_cast(y) - 1900; + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + modified = CharT{}; + } + else if (modified == CharT{}) + { + os << y; + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'z': + if (command) + { + if (offset_sec == nullptr) + throw std::runtime_error("Can not format local_time with %z"); + auto m = duration_cast(*offset_sec); + auto neg = m < minutes{0}; + m = abs(m); + auto h = duration_cast(m); + m -= h; + if (neg) + os << CharT{'-'}; + else + os << CharT{'+'}; + if (h < hours{10}) + os << CharT{'0'}; + os << h.count(); + if (modified != CharT{}) + os << CharT{':'}; + if (m < minutes{10}) + os << CharT{'0'}; + os << m.count(); + command = false; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'Z': + if (command) + { + if (modified == CharT{}) + { + if (abbrev == nullptr) + throw std::runtime_error("Can not format local_time with %Z"); + for (auto c : *abbrev) + os << CharT{c}; + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = false; + } + else + os << *fmt; + break; + case 'E': + case 'O': + if (command) + { + if (modified == CharT{}) + { + modified = *fmt; + } + else + { + os << CharT{'%'} << modified << *fmt; + command = false; + modified = CharT{}; + } + } + else + os << *fmt; + break; + case '%': + if (command) + { + if (modified == CharT{}) + { + os << CharT{'%'}; + command = false; + } + else + { + os << CharT{'%'} << modified << CharT{'%'}; + command = false; + modified = CharT{}; + } + } + else + command = true; + break; + default: + if (command) + { + os << CharT{'%'}; + command = false; + } + if (modified != CharT{}) + { + os << modified; + modified = CharT{}; + } + os << *fmt; + break; + } + } + if (command) + os << CharT{'%'}; + if (modified != CharT{}) + os << modified; +} + +template +void +to_stream(std::basic_ostream& os, const CharT* fmt, + const sys_time& tp) +{ + const std::string abbrev("UTC"); + CONSTDATA std::chrono::seconds offset{0}; + to_stream(os, fmt, local_time{tp.time_since_epoch()}, &abbrev, &offset); +} + +// format + +// const CharT* formats + +template +std::basic_string +format(const std::locale& loc, const CharT* fmt, const local_time& tp) +{ + std::basic_ostringstream os; + os.imbue(loc); + to_stream(os, fmt, tp); + return os.str(); +} + +template +std::basic_string +format(const CharT* fmt, const local_time& tp) +{ + std::basic_ostringstream os; + to_stream(os, fmt, tp); + return os.str(); +} + +template +std::basic_string +format(const std::locale& loc, const CharT* fmt, const sys_time& tp) +{ + std::basic_ostringstream os; + os.imbue(loc); + to_stream(os, fmt, tp); + return os.str(); +} + +template +std::basic_string +format(const CharT* fmt, const sys_time& tp) +{ + std::basic_ostringstream os; + to_stream(os, fmt, tp); + return os.str(); +} + +// basic_string formats + +template +std::basic_string +format(const std::locale& loc, const std::basic_string& fmt, + const local_time& tp) +{ + std::basic_ostringstream os; + os.imbue(loc); + to_stream(os, fmt.c_str(), tp); + return os.str(); +} + +template +std::basic_string +format(const std::basic_string& fmt, const local_time& tp) +{ + std::basic_ostringstream os; + to_stream(os, fmt.c_str(), tp); + return os.str(); +} + +template +std::basic_string +format(const std::locale& loc, const std::basic_string& fmt, + const sys_time& tp) +{ + std::basic_ostringstream os; + os.imbue(loc); + to_stream(os, fmt.c_str(), tp); + return os.str(); +} + +template +std::basic_string +format(const std::basic_string& fmt, const sys_time& tp) +{ + std::basic_ostringstream os; + to_stream(os, fmt.c_str(), tp); + return os.str(); +} + + +// parse + +namespace detail +{ + +template +bool +read_char(std::basic_istream& is, CharT fmt, std::ios::iostate& err) +{ + auto ic = is.get(); + if (Traits::eq_int_type(ic, Traits::eof()) || + !Traits::eq(Traits::to_char_type(ic), fmt)) + { + err |= std::ios::failbit; + is.setstate(std::ios::failbit); + return false; + } + return true; +} + +template +unsigned +read_unsigned(std::basic_istream& is, unsigned m = 1, unsigned M = 10) +{ + unsigned x = 0; + unsigned count = 0; + while (true) + { + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + break; + auto c = static_cast(Traits::to_char_type(ic)); + if (!('0' <= c && c <= '9')) + break; + (void)is.get(); + ++count; + x = 10*x + (c - '0'); + if (count == M) + break; + } + if (count < m) + is.setstate(std::ios::failbit); + return x; +} + +template +int +read_signed(std::basic_istream& is, unsigned m = 1, unsigned M = 10) +{ + auto ic = is.peek(); + if (!Traits::eq_int_type(ic, Traits::eof())) + { + auto c = static_cast(Traits::to_char_type(ic)); + if (('0' <= c && c <= '9') || c == '-' || c == '+') + { + if (c == '-' || c == '+') + (void)is.get(); + auto x = static_cast(read_unsigned(is, m, M)); + if (!is.fail()) + { + if (c == '-') + x = -x; + return x; + } + } + } + is.setstate(std::ios::failbit); + return 0; +} + +template +long double +read_long_double(std::basic_istream& is, unsigned m = 1, unsigned M = 10) +{ + using namespace std; + unsigned count = 0; + auto decimal_point = Traits::to_int_type( + use_facet>(is.getloc()).decimal_point()); + string buf; + while (true) + { + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + break; + if (Traits::eq_int_type(ic, decimal_point)) + { + buf += '.'; + decimal_point = Traits::eof(); + is.get(); + } + else + { + auto c = static_cast(Traits::to_char_type(ic)); + if (!('0' <= c && c <= '9')) + break; + buf += c; + (void)is.get(); + ++count; + } + if (count == M) + break; + } + if (count < m) + is.setstate(std::ios::failbit); + return std::stold(buf); +} + +struct rs +{ + int& i; + unsigned m; + unsigned M; +}; + +struct ru +{ + int& i; + unsigned m; + unsigned M; +}; + +struct rld +{ + long double& i; + unsigned m; + unsigned M; +}; + +template +void +read(std::basic_istream&) +{ +} + +template +void +read(std::basic_istream& is, CharT a0, Args&& ...args); + +template +void +read(std::basic_istream& is, rs a0, Args&& ...args); + +template +void +read(std::basic_istream& is, ru a0, Args&& ...args); + +template +void +read(std::basic_istream& is, int a0, Args&& ...args); + +template +void +read(std::basic_istream& is, rld a0, Args&& ...args); + +template +void +read(std::basic_istream& is, CharT a0, Args&& ...args) +{ + if (a0 != CharT{}) + { + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + return; + if (!Traits::eq(Traits::to_char_type(ic), a0)) + { + is.setstate(std::ios::failbit); + return; + } + (void)is.get(); + } + else + { + while (isspace(is.peek())) + (void)is.get(); + } + read(is, std::forward(args)...); +} + +template +void +read(std::basic_istream& is, rs a0, Args&& ...args) +{ + auto x = read_signed(is, a0.m, a0.M); + if (is.fail()) + return; + a0.i = x; + read(is, std::forward(args)...); +} + +template +void +read(std::basic_istream& is, ru a0, Args&& ...args) +{ + auto x = read_unsigned(is, a0.m, a0.M); + if (is.fail()) + return; + a0.i = static_cast(x); + read(is, std::forward(args)...); +} + +template +void +read(std::basic_istream& is, int a0, Args&& ...args) +{ + if (a0 != -1) + { + auto u = static_cast(a0); + CharT buf[std::numeric_limits::digits10+2] = {}; + auto e = buf; + do + { + *e++ = CharT(u % 10) + CharT{'0'}; + u /= 10; + } while (u > 0); + std::reverse(buf, e); + for (auto p = buf; p != e && is.rdstate() == std::ios::goodbit; ++p) + read(is, *p); + } + if (is.rdstate() == std::ios::goodbit) + read(is, std::forward(args)...); +} + +template +void +read(std::basic_istream& is, rld a0, Args&& ...args) +{ + auto x = read_long_double(is, a0.m, a0.M); + if (is.fail()) + return; + a0.i = x; + read(is, std::forward(args)...); +} + +template +void +parse(std::basic_istream& is, + const CharT* fmt, local_time& tp, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using namespace std; + using namespace std::chrono; + typename basic_istream::sentry ok{is}; + if (ok) + { + auto& f = use_facet>(is.getloc()); + std::tm tm{}; + std::basic_string temp_abbrev; + minutes temp_offset{}; + const CharT* command = nullptr; + auto modified = CharT{}; + auto width = -1; + constexpr int not_a_year = 33000; + int Y = not_a_year; + constexpr int not_a_century = not_a_year / 100; + int C = not_a_century; + constexpr int not_a_2digit_year = 100; + int y = not_a_2digit_year; + int m{}; + int d{}; + int j{}; + constexpr int not_a_weekday = 7; + int wd = not_a_weekday; + constexpr int not_a_hour_12_value = 0; + int I = not_a_hour_12_value; + hours h{}; + minutes min{}; + Duration s{}; + int g = not_a_2digit_year; + int G = not_a_year; + constexpr int not_a_week_num = 100; + int V = not_a_week_num; + int U = not_a_week_num; + int W = not_a_week_num; + using detail::read; + using detail::rs; + using detail::ru; + using detail::rld; + for (; *fmt && is.rdstate() == std::ios::goodbit; ++fmt) + { + if (isspace(*fmt)) + { + // space matches 0 or more white space characters + ws(is); + continue; + } + switch (*fmt) + { + case 'a': + case 'A': + if (command) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + command = nullptr; + width = -1; + modified = CharT{}; + if ((err & ios::failbit) == 0) + wd = tm.tm_wday; + is.setstate(err); + } + else + read(is, *fmt); + break; + case 'b': + case 'B': + case 'h': + if (command) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + command = nullptr; + width = -1; + modified = CharT{}; + if ((err & ios::failbit) == 0) + m = tm.tm_mon + 1; + is.setstate(err); + } + else + read(is, *fmt); + break; + case 'c': + if (command) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + command = nullptr; + width = -1; + modified = CharT{}; + if ((err & ios::failbit) == 0) + { + Y = tm.tm_year + 1900; + m = tm.tm_mon + 1; + d = tm.tm_mday; + h = hours{tm.tm_hour}; + min = minutes{tm.tm_min}; + s = duration_cast(seconds{tm.tm_sec}); + } + is.setstate(err); + } + else + read(is, *fmt); + break; + case 'x': + if (command) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + command = nullptr; + width = -1; + modified = CharT{}; + if ((err & ios::failbit) == 0) + { + Y = tm.tm_year + 1900; + m = tm.tm_mon + 1; + d = tm.tm_mday; + } + is.setstate(err); + } + else + read(is, *fmt); + break; + case 'X': + if (command) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + command = nullptr; + width = -1; + modified = CharT{}; + if ((err & ios::failbit) == 0) + { + h = hours{tm.tm_hour}; + min = minutes{tm.tm_min}; + s = duration_cast(seconds{tm.tm_sec}); + } + is.setstate(err); + } + else + read(is, *fmt); + break; + case 'C': + if (command) + { + if (modified == CharT{}) + { + read(is, rs{C, 1, width == -1 ? 2u : width}); + } + else + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + auto tY = tm.tm_year + 1900; + C = (tY >= 0 ? tY : tY-99) / 100; + } + is.setstate(err); + } + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'D': + if (command) + { + if (modified == CharT{}) + read(is, ru{m, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, + ru{d, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, + rs{y, 1, 2}); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'F': + if (command) + { + if (modified == CharT{}) + read(is, rs{Y, 1, width == -1 ? 4u : width}, CharT{'-'}, + ru{m, 1, 2}, CharT{'-'}, ru{d, 1, 2}); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'd': + case 'e': + if (command) + { + if (modified == CharT{}) + read(is, rs{d, 1, width == -1 ? 2u : width}); + else if (modified == CharT{'O'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + command = nullptr; + width = -1; + modified = CharT{}; + if ((err & ios::failbit) == 0) + d = tm.tm_mday; + is.setstate(err); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'H': + if (command) + { + if (modified == CharT{}) + { + int H; + read(is, ru{H, 1, width == -1 ? 2u : width}); + if (!is.fail()) + h = hours{H}; + } + else if (modified == CharT{'O'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + h = hours{tm.tm_hour}; + is.setstate(err); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'I': + if (command) + { + if (modified == CharT{}) + { + // reads in an hour into I, but most be in [1, 12] + read(is, rs{I, 1, width == -1 ? 2u : width}); + if (I != not_a_hour_12_value) + { + if (!(1 <= I && I <= 12)) + { + I = not_a_hour_12_value; + goto broken; + } + } + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'j': + if (command) + { + if (modified == CharT{}) + read(is, ru{j, 1, width == -1 ? 3u : width}); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'M': + if (command) + { + if (modified == CharT{}) + { + int M; + read(is, ru{M, 1, width == -1 ? 2u : width}); + if (!is.fail()) + min = minutes{M}; + } + else if (modified == CharT{'O'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + min = minutes{tm.tm_min}; + is.setstate(err); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'm': + if (command) + { + if (modified == CharT{}) + read(is, rs{m, 1, width == -1 ? 2u : width}); + else if (modified == CharT{'O'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + command = nullptr; + width = -1; + modified = CharT{}; + if ((err & ios::failbit) == 0) + m = tm.tm_mon + 1; + is.setstate(err); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'n': + case 't': + if (command) + { + // %n and %t match 1 or more white space characters + // consecutive %n and %t count as one + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + break; + if (!isspace(ic)) + { + is.setstate(ios::failbit); + break; + } + ws(is); + for (++fmt; *fmt == 'n' || *fmt == 't'; ++fmt) + ; + --fmt; + } + else + read(is, *fmt); + break; + case 'p': + // Error if haven't yet seen %I + if (command) + { + if (modified == CharT{}) + { + if (I == not_a_hour_12_value) + goto broken; + tm.tm_hour = I; + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + if (!(err & ios::failbit)) + { + h = hours{tm.tm_hour}; + I = not_a_hour_12_value; + } + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + + break; + case 'r': + if (command) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + command = nullptr; + width = -1; + modified = CharT{}; + if ((err & ios::failbit) == 0) + { + h = hours{tm.tm_hour}; + min = minutes{tm.tm_min}; + s = duration_cast(seconds{tm.tm_sec}); + } + is.setstate(err); + } + else + read(is, *fmt); + break; + case 'R': + if (command) + { + if (modified == CharT{}) + { + int H, M; + read(is, ru{H, 1, 2}, CharT{'\0'}, CharT{':'}, CharT{'\0'}, + ru{M, 1, 2}, CharT{'\0'}); + if (!is.fail()) + { + h = hours{H}; + min = minutes{M}; + } + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'S': + if (command) + { + if (modified == CharT{}) + { + using dfs = detail::decimal_format_seconds; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + long double S; + read(is, rld{S, 1, width == -1 ? w : width}); + if (!is.fail()) + s = round(duration{S}); + } + else if (modified == CharT{'O'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + s = duration_cast(seconds{tm.tm_sec}); + is.setstate(err); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'T': + if (command) + { + if (modified == CharT{}) + { + using dfs = detail::decimal_format_seconds; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + int H; + int M; + long double S; + read(is, ru{H, 1, 2}, CharT{':'}, ru{M, 1, 2}, + CharT{':'}, rld{S, 1, w}); + if (!is.fail()) + { + h = hours{H}; + min = minutes{M}; + s = round(duration{S}); + } + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'Y': + if (command) + { + if (modified == CharT{}) + read(is, rs{Y, 1, width == -1 ? 4u : width}); + else if (modified == CharT{'E'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + command = nullptr; + width = -1; + modified = CharT{}; + if ((err & ios::failbit) == 0) + Y = tm.tm_year + 1900; + is.setstate(err); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'y': + if (command) + { + if (modified == CharT{}) + read(is, ru{y, 1, width == -1 ? 2u : width}); + else + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + Y = tm.tm_year + 1900; + is.setstate(err); + } + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'g': + if (command) + { + if (modified == CharT{}) + read(is, ru{g, 1, width == -1 ? 2u : width}); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'G': + if (command) + { + if (modified == CharT{}) + read(is, rs{G, 1, width == -1 ? 4u : width}); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'U': + if (command) + { + if (modified == CharT{}) + read(is, ru{U, 1, width == -1 ? 2u : width}); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'V': + if (command) + { + if (modified == CharT{}) + read(is, ru{V, 1, width == -1 ? 2u : width}); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'W': + if (command) + { + if (modified == CharT{}) + read(is, ru{W, 1, width == -1 ? 2u : width}); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'u': + case 'w': + if (command) + { + if (modified == CharT{}) + { + read(is, ru{wd, 1, width == -1 ? 1u : width}); + if (!is.fail() && *fmt == 'u') + { + if (wd == 7) + wd = 0; + } + } + else if (modified == CharT{'O'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, 0, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + wd = tm.tm_wday; + is.setstate(err); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'E': + case 'O': + if (command) + { + if (modified == CharT{}) + { + modified = *fmt; + } + else + { + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + } + else + read(is, *fmt); + break; + case '%': + if (command) + { + if (modified == CharT{}) + read(is, *fmt); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + command = fmt; + break; + case 'z': + if (command) + { + int H, M; + if (modified == CharT{}) + read(is, rs{H, 2, 2}, ru{M, 2, 2}); + else + read(is, rs{H, 2, 2}, CharT{':'}, ru{M, 2, 2}); + if (!is.fail()) + temp_offset = hours{H} + minutes{M}; + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'Z': + if (command) + { + if (modified == CharT{}) + is >> temp_abbrev; + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + default: + if (command) + { + if (width == -1 && modified == CharT{} && '0' <= *fmt && *fmt <= '9') + { + width = static_cast(*fmt) - '0'; + while ('0' <= fmt[1] && fmt[1] <= '9') + width = 10*width + static_cast(*++fmt) - '0'; + } + else + { + if (modified == CharT{}) + read(is, CharT{'%'}, width, *fmt); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + } + else + read(is, *fmt); + break; + } + } + // is.rdstate() != ios::goodbit || *fmt == CharT{} + if (is.rdstate() == ios::goodbit && command) + { + if (modified == CharT{}) + read(is, CharT{'%'}, width); + else + read(is, CharT{'%'}, width, modified); + } + if (!is.fail()) + { + if (y != not_a_2digit_year) + { + if (!(0 <= y && y <= 99)) + goto broken; + if (C == not_a_century) + { + if (Y == not_a_year) + { + if (y >= 69) + C = 19; + else + C = 20; + } + else + { + C = (Y >= 0 ? Y : Y-100) / 100; + } + } + int tY; + if (C >= 0) + tY = 100*C + y; + else + tY = 100*(C+1) - (y == 0 ? 100 : y); + if (Y != not_a_year && Y != tY) + goto broken; + Y = tY; + } + if (g != not_a_2digit_year) + { + if (!(0 <= g && g <= 99)) + goto broken; + if (C == not_a_century) + { + if (G == not_a_year) + { + if (g >= 69) + C = 19; + else + C = 20; + } + else + { + C = (G >= 0 ? G : G-100) / 100; + } + } + int tG; + if (C >= 0) + tG = 100*C + g; + else + tG = 100*(C+1) - (g == 0 ? 100 : g); + if (G != not_a_year && G != tG) + goto broken; + G = tG; + } + if (G != not_a_year) + { + if (V == not_a_week_num || wd == not_a_weekday) + goto broken; + auto ymd = year_month_day{local_days{year{G-1}/dec/thu[last]} + + (mon-thu) + weeks{V-1} + + (weekday{static_cast(wd)}-mon)}; + if (Y == not_a_year) + Y = static_cast(ymd.year()); + else if (year{Y} != ymd.year()) + goto broken; + if (m == 0) + m = static_cast(static_cast(ymd.month())); + else if (month(m) != ymd.month()) + goto broken; + if (d == 0) + d = static_cast(static_cast(ymd.day())); + else if (day(d) != ymd.day()) + goto broken; + } + if (Y != not_a_year) + { + if (!(static_cast(year::min()) <= Y && + Y <= static_cast(year::max()))) + goto broken; + if (j != 0) + { + auto ymd = year_month_day{local_days{year{Y}/1/1} + days{j-1}}; + if (m == 0) + m = static_cast(static_cast(ymd.month())); + else if (month(m) != ymd.month()) + goto broken; + if (d == 0) + d = static_cast(static_cast(ymd.day())); + else if (day(d) != ymd.day()) + goto broken; + } + if (U != not_a_week_num) + { + if (wd == not_a_weekday) + goto broken; + sys_days sd; + if (U == 0) + sd = year{Y-1}/dec/weekday{static_cast(wd)}[last]; + else + sd = sys_days{year{Y}/jan/sun[1]} + weeks{U-1} + + (weekday{static_cast(wd)} - sun); + year_month_day ymd = sd; + if (year{Y} != ymd.year()) + goto broken; + if (m == 0) + m = static_cast(static_cast(ymd.month())); + else if (month(m) != ymd.month()) + goto broken; + if (d == 0) + d = static_cast(static_cast(ymd.day())); + else if (day(d) != ymd.day()) + goto broken; + } + if (W != not_a_week_num) + { + if (wd == not_a_weekday) + goto broken; + sys_days sd; + if (W == 0) + sd = year{Y-1}/dec/weekday{static_cast(wd)}[last]; + else + sd = sys_days{year{Y}/jan/mon[1]} + weeks{W-1} + + (weekday{static_cast(wd)} - mon); + year_month_day ymd = sd; + if (year{Y} != ymd.year()) + goto broken; + if (m == 0) + m = static_cast(static_cast(ymd.month())); + else if (month(m) != ymd.month()) + goto broken; + if (d == 0) + d = static_cast(static_cast(ymd.day())); + else if (day(d) != ymd.day()) + goto broken; + } + if (m != 0 && d != 0) + { + auto ymd = year{Y}/m/d; + if (!ymd.ok()) + goto broken; + auto ld = local_days{ymd}; + if (wd != not_a_weekday && + weekday{static_cast(wd)} != weekday{ld}) + goto broken; + tp = local_time{floor(ld + h + min + s)}; + } + else + goto broken; + } + else // did not parse a year + { + goto broken; + } + if (abbrev != nullptr) + *abbrev = std::move(temp_abbrev); + if (offset != nullptr) + *offset = temp_offset; + } + return; + } +broken: + is.setstate(ios_base::failbit); +} + +template +inline +void +parse(std::basic_istream& is, const CharT* fmt, local_time& tp, + std::chrono::minutes* offset) +{ + parse(is, fmt, tp, static_cast*>(nullptr), offset); +} + +template > +struct parse_local_manip +{ + const std::basic_string format_; + local_time& tp_; + std::basic_string* abbrev_; + std::chrono::minutes* offset_; + +public: + parse_local_manip(std::basic_string format, + local_time& tp, std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) + : format_(std::move(format)) + , tp_(tp) + , abbrev_(abbrev) + , offset_(offset) + {} + +}; + +template +std::basic_istream& +operator>>(std::basic_istream& is, + const parse_local_manip& x) +{ + parse(is, x.format_.c_str(), x.tp_, x.abbrev_, x.offset_); + return is; +} + +template > +struct parse_sys_manip +{ + const std::basic_string format_; + sys_time& tp_; + std::basic_string* abbrev_; + std::chrono::minutes* offset_; + +public: + parse_sys_manip(std::basic_string format, + sys_time& tp, std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) + : format_(std::move(format)) + , tp_(tp) + , abbrev_(abbrev) + , offset_(offset) + {} + +}; + +template +std::basic_istream& +operator>>(std::basic_istream& is, + const parse_sys_manip& x) +{ + std::chrono::minutes offset{}; + auto offptr = x.offset_ ? x.offset_ : &offset; + local_time lt; + parse(is, x.format_.c_str(), lt, x.abbrev_, offptr); + if (!is.fail()) + x.tp_ = sys_time{floor(lt - *offptr).time_since_epoch()}; + return is; +} + +} // namespace detail + +template +inline +detail::parse_sys_manip +parse(const std::basic_string& format, sys_time& tp) +{ + return {format, tp}; +} + +template +inline +detail::parse_sys_manip +parse(const std::basic_string& format, sys_time& tp, + std::basic_string& abbrev) +{ + return {format, tp, &abbrev}; +} + +template +inline +detail::parse_sys_manip +parse(const std::basic_string& format, sys_time& tp, + std::chrono::minutes& offset) +{ + return {format, tp, nullptr, &offset}; +} + +template +inline +detail::parse_sys_manip +parse(const std::basic_string& format, sys_time& tp, + std::basic_string& abbrev, std::chrono::minutes& offset) +{ + return {format, tp, &abbrev, &offset}; +} + +template +inline +detail::parse_sys_manip +parse(const std::basic_string& format, sys_time& tp, + std::chrono::minutes& offset, std::basic_string& abbrev) +{ + return {format, tp, &abbrev, &offset}; +} + +template +inline +detail::parse_local_manip +parse(const std::basic_string& format, local_time& tp) +{ + return {format, tp}; +} + +template +inline +detail::parse_local_manip +parse(const std::basic_string& format, local_time& tp, + std::basic_string& abbrev) +{ + return {format, tp, &abbrev}; +} + +template +inline +detail::parse_local_manip +parse(const std::basic_string& format, local_time& tp, + std::chrono::minutes& offset) +{ + return {format, tp, nullptr, &offset}; +} + +template +inline +detail::parse_local_manip +parse(const std::basic_string& format, local_time& tp, + std::basic_string& abbrev, std::chrono::minutes& offset) +{ + return {format, tp, &abbrev, &offset}; +} + +template +inline +detail::parse_local_manip +parse(const std::basic_string& format, local_time& tp, + std::chrono::minutes& offset, std::basic_string& abbrev) +{ + return {format, tp, &abbrev, &offset}; +} + +template +inline +void +parse(std::basic_istream& is, + const std::basic_string& format, sys_time& tp) +{ + std::chrono::minutes offset{}; + local_time lt; + detail::parse(is, format.c_str(), lt, &offset); + if (!is.fail()) + tp = sys_time{floor(lt - offset).time_since_epoch()}; +} + +template +inline +void +parse(std::basic_istream& is, + const std::basic_string& format, sys_time& tp, + std::basic_string& abbrev) +{ + std::chrono::minutes offset{}; + local_time lt; + detail::parse(is, format.c_str(), lt, &abbrev, &offset); + if (!is.fail()) + tp = sys_time{floor(lt - offset).time_since_epoch()}; +} + +template +inline +void +parse(std::basic_istream& is, + const std::basic_string& format, sys_time& tp, + std::chrono::minutes& offset) +{ + local_time lt; + detail::parse(is, format.c_str(), lt, &offset); + if (!is.fail()) + tp = sys_time{floor(lt - offset).time_since_epoch()}; +} + +template +inline +void +parse(std::basic_istream& is, + const std::basic_string& format, sys_time& tp, + std::basic_string& abbrev, std::chrono::minutes& offset) +{ + local_time lt; + detail::parse(is, format.c_str(), lt, &abbrev, &offset); + if (!is.fail()) + tp = sys_time{floor(lt - offset).time_since_epoch()}; +} + +template +inline +void +parse(std::basic_istream& is, + const std::basic_string& format, sys_time& tp, + std::chrono::minutes& offset, std::basic_string& abbrev) +{ + local_time lt; + detail::parse(is, format.c_str(), lt, &abbrev, &offset); + if (!is.fail()) + tp = sys_time{floor(lt - offset).time_since_epoch()}; +} + +template +inline +void +parse(std::basic_istream& is, + const std::basic_string& format, local_time& tp) +{ + detail::parse(is, format.c_str(), tp); +} + +template +inline +void +parse(std::basic_istream& is, + const std::basic_string& format, local_time& tp, + std::basic_string& abbrev) +{ + detail::parse(is, format.c_str(), tp, &abbrev); +} + +template +inline +void +parse(std::basic_istream& is, + const std::basic_string& format, local_time& tp, + std::chrono::minutes& offset) +{ + detail::parse(is, format.c_str(), tp, &offset); +} + +template +inline +void +parse(std::basic_istream& is, + const std::basic_string& format, local_time& tp, + std::basic_string& abbrev, std::chrono::minutes& offset) +{ + detail::parse(is, format.c_str(), tp, &abbrev, &offset); +} + +template +inline +void +parse(std::basic_istream& is, + const std::basic_string& format, local_time& tp, + std::chrono::minutes& offset, std::basic_string& abbrev) +{ + detail::parse(is, format.c_str(), tp, &abbrev, &offset); +} + +// const CharT* formats + +template +inline +detail::parse_sys_manip +parse(const CharT* format, sys_time& tp) +{ + return {format, tp}; +} + +template +inline +detail::parse_sys_manip +parse(const CharT* format, sys_time& tp, + std::basic_string& abbrev) +{ + return {format, tp, &abbrev}; +} + +template +inline +detail::parse_sys_manip +parse(const CharT* format, sys_time& tp, std::chrono::minutes& offset) +{ + return {format, tp, nullptr, &offset}; +} + +template +inline +detail::parse_sys_manip +parse(const CharT* format, sys_time& tp, + std::basic_string& abbrev, std::chrono::minutes& offset) +{ + return {format, tp, &abbrev, &offset}; +} + +template +inline +detail::parse_sys_manip +parse(const CharT* format, sys_time& tp, + std::chrono::minutes& offset, std::basic_string& abbrev) +{ + return {format, tp, &abbrev, &offset}; +} + +template +inline +detail::parse_local_manip +parse(const CharT* format, local_time& tp) +{ + return {format, tp}; +} + +template +inline +detail::parse_local_manip +parse(const CharT* format, local_time& tp, + std::basic_string& abbrev) +{ + return {format, tp, &abbrev}; +} + +template +inline +detail::parse_local_manip +parse(const CharT* format, local_time& tp, std::chrono::minutes& offset) +{ + return {format, tp, nullptr, &offset}; +} + +template +inline +detail::parse_local_manip +parse(const CharT* format, local_time& tp, + std::basic_string& abbrev, std::chrono::minutes& offset) +{ + return {format, tp, &abbrev, &offset}; +} + +template +inline +detail::parse_local_manip +parse(const CharT* format, local_time& tp, std::chrono::minutes& offset, + std::basic_string& abbrev) +{ + return {format, tp, &abbrev, &offset}; +} + +template +inline +void +parse(std::basic_istream& is, const CharT* format, sys_time& tp) +{ + std::chrono::minutes offset{}; + local_time lt; + detail::parse(is, format, lt, &offset); + if (!is.fail()) + tp = sys_time{floor(lt - offset).time_since_epoch()}; +} + +template +inline +void +parse(std::basic_istream& is, const CharT* format, sys_time& tp, + std::basic_string& abbrev) +{ + std::chrono::minutes offset{}; + local_time lt; + detail::parse(is, format, lt, &abbrev, &offset); + if (!is.fail()) + tp = sys_time{floor(lt - offset).time_since_epoch()}; +} + +template +inline +void +parse(std::basic_istream& is, const CharT* format, sys_time& tp, + std::chrono::minutes& offset) +{ + local_time lt; + detail::parse(is, format, lt, &offset); + if (!is.fail()) + tp = sys_time{floor(lt - offset).time_since_epoch()}; +} + +template +inline +void +parse(std::basic_istream& is, const CharT* format, sys_time& tp, + std::basic_string& abbrev, std::chrono::minutes& offset) +{ + local_time lt; + detail::parse(is, format, lt, &abbrev, &offset); + if (!is.fail()) + tp = sys_time{floor(lt - offset).time_since_epoch()}; +} + +template +inline +void +parse(std::basic_istream& is, const CharT* format, sys_time& tp, + std::chrono::minutes& offset, std::basic_string& abbrev) +{ + local_time lt; + detail::parse(is, format, lt, &abbrev, &offset); + if (!is.fail()) + tp = sys_time{floor(lt - offset).time_since_epoch()}; +} + +template +inline +void +parse(std::basic_istream& is, const CharT* format, + local_time& tp) +{ + detail::parse(is, format, tp); +} + +template +inline +void +parse(std::basic_istream& is, const CharT* format, + local_time& tp, std::basic_string& abbrev) +{ + detail::parse(is, format, tp, &abbrev); +} + +template +inline +void +parse(std::basic_istream& is, const CharT* format, + local_time& tp, std::chrono::minutes& offset) +{ + detail::parse(is, format, tp, &offset); +} + +template +inline +void +parse(std::basic_istream& is, const CharT* format, + local_time& tp, std::basic_string& abbrev, + std::chrono::minutes& offset) +{ + detail::parse(is, format, tp, &abbrev, &offset); +} + +template +inline +void +parse(std::basic_istream& is, const CharT* format, + local_time& tp, std::chrono::minutes& offset, + std::basic_string& abbrev) +{ + detail::parse(is, format, tp, &abbrev, &offset); +} + +} // namespace date + +#endif // DATE_H diff --git a/version.hh b/version.hh index fbed31be886f..685b87441688 100644 --- a/version.hh +++ b/version.hh @@ -37,7 +37,7 @@ public: } static version current() { - static version v(2, 1, 8); + static version v(2, 2, 8); return v; }