aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCara Salter <cara@devcara.com>2023-04-15 10:37:45 -0400
committerCara Salter <cara@devcara.com>2023-04-15 10:37:45 -0400
commit5058c8bbeff6b659787a8ab482ac89fc4a194076 (patch)
treeef768f7aa54482469c38e5bb818f96740dae85a0
parent8edf3bf0ca81b942a165cde3bdea11ca755da955 (diff)
download142bot-5058c8bbeff6b659787a8ab482ac89fc4a194076.tar.gz
142bot-5058c8bbeff6b659787a8ab482ac89fc4a194076.zip
init spotify
Change-Id: I6796976649ee59f4e647148e77d76effa7315f73
-rw-r--r--include/142bot/date.h8234
-rw-r--r--include/142bot/db.hpp70
-rw-r--r--include/142bot/iso_week.h1751
-rw-r--r--include/142bot/modules.hpp3
-rw-r--r--modules/spotify/spotify.cpp185
-rw-r--r--src/bot.cpp2
-rw-r--r--src/db.cpp65
-rw-r--r--src/modules.cpp35
8 files changed, 10325 insertions, 20 deletions
diff --git a/include/142bot/date.h b/include/142bot/date.h
new file mode 100644
index 0000000..6960e8c
--- /dev/null
+++ b/include/142bot/date.h
@@ -0,0 +1,8234 @@
+#ifndef DATE_H
+#define DATE_H
+
+// The MIT License (MIT)
+//
+// Copyright (c) 2015, 2016, 2017 Howard Hinnant
+// Copyright (c) 2016 Adrian Colomitchi
+// Copyright (c) 2017 Florian Dang
+// Copyright (c) 2017 Paul Thompson
+// Copyright (c) 2018, 2019 Tomasz KamiƄski
+// Copyright (c) 2019 Jiangang Zhuang
+//
+// 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 would involve another several millennia of evolution).
+// We did not mean to shout.
+
+#ifndef HAS_STRING_VIEW
+# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+# define HAS_STRING_VIEW 1
+# else
+# define HAS_STRING_VIEW 0
+# endif
+#endif // HAS_STRING_VIEW
+
+#include <cassert>
+#include <algorithm>
+#include <cctype>
+#include <chrono>
+#include <climits>
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <ctime>
+#include <ios>
+#include <istream>
+#include <iterator>
+#include <limits>
+#include <locale>
+#include <memory>
+#include <ostream>
+#include <ratio>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+#if HAS_STRING_VIEW
+# include <string_view>
+#endif
+#include <utility>
+#include <type_traits>
+
+#ifdef __GNUC__
+# pragma GCC diagnostic push
+# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 7)
+# pragma GCC diagnostic ignored "-Wpedantic"
+# endif
+# if __GNUC__ < 5
+ // GCC 4.9 Bug 61489 Wrong warning with -Wmissing-field-initializers
+# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+# endif
+#endif
+
+#ifdef _MSC_VER
+# pragma warning(push)
+// warning C4127: conditional expression is constant
+# pragma warning(disable : 4127)
+#endif
+
+namespace date
+{
+
+//---------------+
+// Configuration |
+//---------------+
+
+#ifndef ONLY_C_LOCALE
+# define ONLY_C_LOCALE 0
+#endif
+
+#if defined(_MSC_VER) && (!defined(__clang__) || (_MSC_VER < 1910))
+// MSVC
+# ifndef _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING
+# define _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING
+# endif
+# if _MSC_VER < 1910
+// before VS2017
+# define CONSTDATA const
+# define CONSTCD11
+# define CONSTCD14
+# define NOEXCEPT _NOEXCEPT
+# else
+// VS2017 and later
+# define CONSTDATA constexpr const
+# define CONSTCD11 constexpr
+# define CONSTCD14 constexpr
+# define NOEXCEPT noexcept
+# endif
+
+#elif defined(__SUNPRO_CC) && __SUNPRO_CC <= 0x5150
+// Oracle Developer Studio 12.6 and earlier
+# define CONSTDATA constexpr const
+# define CONSTCD11 constexpr
+# 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
+
+#ifndef HAS_UNCAUGHT_EXCEPTIONS
+# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+# define HAS_UNCAUGHT_EXCEPTIONS 1
+# else
+# define HAS_UNCAUGHT_EXCEPTIONS 0
+# endif
+#endif // HAS_UNCAUGHT_EXCEPTIONS
+
+#ifndef HAS_VOID_T
+# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+# define HAS_VOID_T 1
+# else
+# define HAS_VOID_T 0
+# endif
+#endif // HAS_VOID_T
+
+// Protect from Oracle sun macro
+#ifdef sun
+# undef sun
+#endif
+
+// Work around for a NVCC compiler bug which causes it to fail
+// to compile std::ratio_{multiply,divide} when used directly
+// in the std::chrono::duration template instantiations below
+namespace detail {
+template <typename R1, typename R2>
+using ratio_multiply = decltype(std::ratio_multiply<R1, R2>{});
+
+template <typename R1, typename R2>
+using ratio_divide = decltype(std::ratio_divide<R1, R2>{});
+} // namespace detail
+
+//-----------+
+// Interface |
+//-----------+
+
+// durations
+
+using days = std::chrono::duration
+ <int, detail::ratio_multiply<std::ratio<24>, std::chrono::hours::period>>;
+
+using weeks = std::chrono::duration
+ <int, detail::ratio_multiply<std::ratio<7>, days::period>>;
+
+using years = std::chrono::duration
+ <int, detail::ratio_multiply<std::ratio<146097, 400>, days::period>>;
+
+using months = std::chrono::duration
+ <int, detail::ratio_divide<years::period, std::ratio<12>>>;
+
+// time_point
+
+template <class Duration>
+ using sys_time = std::chrono::time_point<std::chrono::system_clock, Duration>;
+
+using sys_days = sys_time<days>;
+using sys_seconds = sys_time<std::chrono::seconds>;
+
+struct local_t {};
+
+template <class Duration>
+ using local_time = std::chrono::time_point<local_t, Duration>;
+
+using local_seconds = local_time<std::chrono::seconds>;
+using local_days = local_time<days>;
+
+// 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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& 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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const month& m);
+
+// year
+
+class year
+{
+ short y_;
+
+public:
+ year() = default;
+ explicit CONSTCD11 year(int 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 int() const NOEXCEPT;
+ CONSTCD11 bool ok() const NOEXCEPT;
+
+ static CONSTCD11 year min() NOEXCEPT { return year{-32767}; }
+ static CONSTCD11 year max() NOEXCEPT { return year{32767}; }
+};
+
+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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year& y);
+
+// weekday
+
+class weekday
+{
+ unsigned char wd_;
+public:
+ weekday() = default;
+ explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT;
+ CONSTCD14 weekday(const sys_days& dp) NOEXCEPT;
+ CONSTCD14 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 bool ok() const NOEXCEPT;
+
+ CONSTCD11 unsigned c_encoding() const NOEXCEPT;
+ CONSTCD11 unsigned iso_encoding() const NOEXCEPT;
+
+ CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT;
+ CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT;
+
+private:
+ static CONSTCD14 unsigned char weekday_from_days(int z) NOEXCEPT;
+
+ friend CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT;
+ friend CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT;
+ friend CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT;
+ template<class CharT, class Traits>
+ friend std::basic_ostream<CharT, Traits>&
+ operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd);
+ friend class weekday_indexed;
+};
+
+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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd);
+
+// weekday_indexed
+
+class weekday_indexed
+{
+ unsigned char wd_ : 4;
+ unsigned char index_ : 4;
+
+public:
+ weekday_indexed() = default;
+ 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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& 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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl);
+
+namespace detail
+{
+
+struct unspecified_month_disambiguator {};
+
+} // namespace detail
+
+// 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;
+
+ template<class = detail::unspecified_month_disambiguator>
+ CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT;
+ template<class = detail::unspecified_month_disambiguator>
+ 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;
+
+template<class = detail::unspecified_month_disambiguator>
+CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT;
+template<class = detail::unspecified_month_disambiguator>
+CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT;
+template<class = detail::unspecified_month_disambiguator>
+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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& 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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& 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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& 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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& 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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& 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;
+
+ template<class = detail::unspecified_month_disambiguator>
+ CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT;
+ template<class = detail::unspecified_month_disambiguator>
+ 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;
+
+template<class = detail::unspecified_month_disambiguator>
+CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT;
+template<class = detail::unspecified_month_disambiguator>
+CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT;
+template<class = detail::unspecified_month_disambiguator>
+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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& 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;
+
+ template<class = detail::unspecified_month_disambiguator>
+ CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT;
+ template<class = detail::unspecified_month_disambiguator>
+ 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;
+
+template<class = detail::unspecified_month_disambiguator>
+CONSTCD14
+year_month_day_last
+operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT;
+
+template<class = detail::unspecified_month_disambiguator>
+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;
+
+template<class = detail::unspecified_month_disambiguator>
+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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& 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;
+
+ template<class = detail::unspecified_month_disambiguator>
+ CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT;
+ template<class = detail::unspecified_month_disambiguator>
+ 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;
+
+template<class = detail::unspecified_month_disambiguator>
+CONSTCD14
+year_month_weekday
+operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT;
+
+template<class = detail::unspecified_month_disambiguator>
+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;
+
+template<class = detail::unspecified_month_disambiguator>
+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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& 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;
+
+ template<class = detail::unspecified_month_disambiguator>
+ CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT;
+ template<class = detail::unspecified_month_disambiguator>
+ 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;
+
+template<class = detail::unspecified_month_disambiguator>
+CONSTCD14
+year_month_weekday_last
+operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT;
+
+template<class = detail::unspecified_month_disambiguator>
+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;
+
+template<class = detail::unspecified_month_disambiguator>
+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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& 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;
+
+} // inline namespace literals
+#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900)
+
+// CONSTDATA date::month January{1};
+// CONSTDATA date::month February{2};
+// CONSTDATA date::month March{3};
+// CONSTDATA date::month April{4};
+// CONSTDATA date::month May{5};
+// CONSTDATA date::month June{6};
+// CONSTDATA date::month July{7};
+// CONSTDATA date::month August{8};
+// CONSTDATA date::month September{9};
+// CONSTDATA date::month October{10};
+// CONSTDATA date::month November{11};
+// CONSTDATA date::month December{12};
+//
+// CONSTDATA date::weekday Sunday{0u};
+// CONSTDATA date::weekday Monday{1u};
+// CONSTDATA date::weekday Tuesday{2u};
+// CONSTDATA date::weekday Wednesday{3u};
+// CONSTDATA date::weekday Thursday{4u};
+// CONSTDATA date::weekday Friday{5u};
+// CONSTDATA date::weekday Saturday{6u};
+
+#if HAS_VOID_T
+
+template <class T, class = std::void_t<>>
+struct is_clock
+ : std::false_type
+{};
+
+template <class T>
+struct is_clock<T, std::void_t<decltype(T::now()), typename T::rep, typename T::period,
+ typename T::duration, typename T::time_point,
+ decltype(T::is_steady)>>
+ : std::true_type
+{};
+
+template<class T> inline constexpr bool is_clock_v = is_clock<T>::value;
+
+#endif // HAS_VOID_T
+
+//----------------+
+// Implementation |
+//----------------+
+
+// utilities
+namespace detail {
+
+template<class CharT, class Traits = std::char_traits<CharT>>
+class save_istream
+{
+protected:
+ std::basic_ios<CharT, Traits>& is_;
+ CharT fill_;
+ std::ios::fmtflags flags_;
+ std::streamsize precision_;
+ std::streamsize width_;
+ std::basic_ostream<CharT, Traits>* tie_;
+ std::locale loc_;
+
+public:
+ ~save_istream()
+ {
+ is_.fill(fill_);
+ is_.flags(flags_);
+ is_.precision(precision_);
+ is_.width(width_);
+ is_.imbue(loc_);
+ is_.tie(tie_);
+ }
+
+ save_istream(const save_istream&) = delete;
+ save_istream& operator=(const save_istream&) = delete;
+
+ explicit save_istream(std::basic_ios<CharT, Traits>& is)
+ : is_(is)
+ , fill_(is.fill())
+ , flags_(is.flags())
+ , precision_(is.precision())
+ , width_(is.width(0))
+ , tie_(is.tie(nullptr))
+ , loc_(is.getloc())
+ {
+ if (tie_ != nullptr)
+ tie_->flush();
+ }
+};
+
+template<class CharT, class Traits = std::char_traits<CharT>>
+class save_ostream
+ : private save_istream<CharT, Traits>
+{
+public:
+ ~save_ostream()
+ {
+ if ((this->flags_ & std::ios::unitbuf) &&
+#if HAS_UNCAUGHT_EXCEPTIONS
+ std::uncaught_exceptions() == 0 &&
+#else
+ !std::uncaught_exception() &&
+#endif
+ this->is_.good())
+ this->is_.rdbuf()->pubsync();
+ }
+
+ save_ostream(const save_ostream&) = delete;
+ save_ostream& operator=(const save_ostream&) = delete;
+
+ explicit save_ostream(std::basic_ios<CharT, Traits>& os)
+ : save_istream<CharT, Traits>(os)
+ {
+ }
+};
+
+template <class T>
+struct choose_trunc_type
+{
+ static const int digits = std::numeric_limits<T>::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;
+};
+
+template <class T>
+CONSTCD11
+inline
+typename std::enable_if
+<
+ !std::chrono::treat_as_floating_point<T>::value,
+ T
+>::type
+trunc(T t) NOEXCEPT
+{
+ return t;
+}
+
+template <class T>
+CONSTCD14
+inline
+typename std::enable_if
+<
+ std::chrono::treat_as_floating_point<T>::value,
+ T
+>::type
+trunc(T t) NOEXCEPT
+{
+ using std::numeric_limits;
+ using I = typename choose_trunc_type<T>::type;
+ CONSTDATA auto digits = numeric_limits<T>::digits;
+ static_assert(digits < numeric_limits<I>::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<T>(static_cast<I>(t));
+ if (t == 0 && negative)
+ t = -t;
+ }
+ return t;
+}
+
+template <std::intmax_t Xp, std::intmax_t Yp>
+struct static_gcd
+{
+ static const std::intmax_t value = static_gcd<Yp, Xp % Yp>::value;
+};
+
+template <std::intmax_t Xp>
+struct static_gcd<Xp, 0>
+{
+ static const std::intmax_t value = Xp;
+};
+
+template <>
+struct static_gcd<0, 0>
+{
+ static const std::intmax_t value = 1;
+};
+
+template <class R1, class R2>
+struct no_overflow
+{
+private:
+ static const std::intmax_t gcd_n1_n2 = static_gcd<R1::num, R2::num>::value;
+ static const std::intmax_t gcd_d1_d2 = static_gcd<R1::den, R2::den>::value;
+ static const std::intmax_t n1 = R1::num / gcd_n1_n2;
+ static const std::intmax_t d1 = R1::den / gcd_d1_d2;
+ static const std::intmax_t n2 = R2::num / gcd_n1_n2;
+ static const std::intmax_t d2 = R2::den / gcd_d1_d2;
+#ifdef __cpp_constexpr
+ static const std::intmax_t max = std::numeric_limits<std::intmax_t>::max();
+#else
+ static const std::intmax_t max = LLONG_MAX;
+#endif
+
+ template <std::intmax_t Xp, std::intmax_t Yp, bool overflow>
+ struct mul // overflow == false
+ {
+ static const std::intmax_t value = Xp * Yp;
+ };
+
+ template <std::intmax_t Xp, std::intmax_t Yp>
+ struct mul<Xp, Yp, true>
+ {
+ static const std::intmax_t value = 1;
+ };
+
+public:
+ static const bool value = (n1 <= max / d2) && (n2 <= max / d1);
+ typedef std::ratio<mul<n1, d2, !value>::value,
+ mul<n2, d1, !value>::value> type;
+};
+
+} // detail
+
+// trunc towards zero
+template <class To, class Rep, class Period>
+CONSTCD11
+inline
+typename std::enable_if
+<
+ detail::no_overflow<Period, typename To::period>::value,
+ To
+>::type
+trunc(const std::chrono::duration<Rep, Period>& d)
+{
+ return To{detail::trunc(std::chrono::duration_cast<To>(d).count())};
+}
+
+template <class To, class Rep, class Period>
+CONSTCD11
+inline
+typename std::enable_if
+<
+ !detail::no_overflow<Period, typename To::period>::value,
+ To
+>::type
+trunc(const std::chrono::duration<Rep, Period>& d)
+{
+ using std::chrono::duration_cast;
+ using std::chrono::duration;
+ using rep = typename std::common_type<Rep, typename To::rep>::type;
+ return To{detail::trunc(duration_cast<To>(duration_cast<duration<rep>>(d)).count())};
+}
+
+#ifndef HAS_CHRONO_ROUNDING
+# if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 190023918 || (_MSC_FULL_VER >= 190000000 && defined (__clang__)))
+# 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 <class To, class Rep, class Period>
+CONSTCD14
+inline
+typename std::enable_if
+<
+ detail::no_overflow<Period, typename To::period>::value,
+ To
+>::type
+floor(const std::chrono::duration<Rep, Period>& d)
+{
+ auto t = trunc<To>(d);
+ if (t > d)
+ return t - To{1};
+ return t;
+}
+
+template <class To, class Rep, class Period>
+CONSTCD14
+inline
+typename std::enable_if
+<
+ !detail::no_overflow<Period, typename To::period>::value,
+ To
+>::type
+floor(const std::chrono::duration<Rep, Period>& d)
+{
+ using rep = typename std::common_type<Rep, typename To::rep>::type;
+ return floor<To>(floor<std::chrono::duration<rep>>(d));
+}
+
+// round to nearest, to even on tie
+template <class To, class Rep, class Period>
+CONSTCD14
+inline
+To
+round(const std::chrono::duration<Rep, Period>& d)
+{
+ auto t0 = floor<To>(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<To>(t0/2)*2 == To{0})
+ return t0;
+ return t1;
+ }
+ if (diff0 < diff1)
+ return t0;
+ return t1;
+}
+
+// round up
+template <class To, class Rep, class Period>
+CONSTCD14
+inline
+To
+ceil(const std::chrono::duration<Rep, Period>& d)
+{
+ auto t = trunc<To>(d);
+ if (t < d)
+ return t + To{1};
+ return t;
+}
+
+template <class Rep, class Period,
+ class = typename std::enable_if
+ <
+ std::numeric_limits<Rep>::is_signed
+ >::type>
+CONSTCD11
+std::chrono::duration<Rep, Period>
+abs(std::chrono::duration<Rep, Period> d)
+{
+ return d >= d.zero() ? d : static_cast<decltype(d)>(-d);
+}
+
+// round down
+template <class To, class Clock, class FromDuration>
+CONSTCD11
+inline
+std::chrono::time_point<Clock, To>
+floor(const std::chrono::time_point<Clock, FromDuration>& tp)
+{
+ using std::chrono::time_point;
+ return time_point<Clock, To>{date::floor<To>(tp.time_since_epoch())};
+}
+
+// round to nearest, to even on tie
+template <class To, class Clock, class FromDuration>
+CONSTCD11
+inline
+std::chrono::time_point<Clock, To>
+round(const std::chrono::time_point<Clock, FromDuration>& tp)
+{
+ using std::chrono::time_point;
+ return time_point<Clock, To>{round<To>(tp.time_since_epoch())};
+}
+
+// round up
+template <class To, class Clock, class FromDuration>
+CONSTCD11
+inline
+std::chrono::time_point<Clock, To>
+ceil(const std::chrono::time_point<Clock, FromDuration>& tp)
+{
+ using std::chrono::time_point;
+ return time_point<Clock, To>{ceil<To>(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
+
+namespace detail
+{
+
+template <class To, class Rep, class Period>
+CONSTCD14
+inline
+typename std::enable_if
+<
+ !std::chrono::treat_as_floating_point<typename To::rep>::value,
+ To
+>::type
+round_i(const std::chrono::duration<Rep, Period>& d)
+{
+ return round<To>(d);
+}
+
+template <class To, class Rep, class Period>
+CONSTCD14
+inline
+typename std::enable_if
+<
+ std::chrono::treat_as_floating_point<typename To::rep>::value,
+ To
+>::type
+round_i(const std::chrono::duration<Rep, Period>& d)
+{
+ return d;
+}
+
+template <class To, class Clock, class FromDuration>
+CONSTCD11
+inline
+std::chrono::time_point<Clock, To>
+round_i(const std::chrono::time_point<Clock, FromDuration>& tp)
+{
+ using std::chrono::time_point;
+ return time_point<Clock, To>{round_i<To>(tp.time_since_epoch())};
+}
+
+} // detail
+
+// trunc towards zero
+template <class To, class Clock, class FromDuration>
+CONSTCD11
+inline
+std::chrono::time_point<Clock, To>
+trunc(const std::chrono::time_point<Clock, FromDuration>& tp)
+{
+ using std::chrono::time_point;
+ return time_point<Clock, To>{trunc<To>(tp.time_since_epoch())};
+}
+
+// day
+
+CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast<decltype(d_)>(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<unsigned>(x) == static_cast<unsigned>(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<unsigned>(x) < static_cast<unsigned>(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<days::rep>(static_cast<unsigned>(x)
+ - static_cast<unsigned>(y))};
+}
+
+CONSTCD11
+inline
+day
+operator+(const day& x, const days& y) NOEXCEPT
+{
+ return day{static_cast<unsigned>(x) + static_cast<unsigned>(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;
+}
+
+namespace detail
+{
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+low_level_fmt(std::basic_ostream<CharT, Traits>& os, const day& d)
+{
+ detail::save_ostream<CharT, Traits> _(os);
+ os.fill('0');
+ os.flags(std::ios::dec | std::ios::right);
+ os.width(2);
+ os << static_cast<unsigned>(d);
+ return os;
+}
+
+} // namespace detail
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const day& d)
+{
+ detail::low_level_fmt(os, d);
+ if (!d.ok())
+ os << " is not a valid day";
+ return os;
+}
+
+// month
+
+CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast<decltype(m_)>(m)) {}
+CONSTCD14 inline month& month::operator++() NOEXCEPT {*this += months{1}; return *this;}
+CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;}
+CONSTCD14 inline month& month::operator--() NOEXCEPT {*this -= months{1}; 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<unsigned>(x) == static_cast<unsigned>(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<unsigned>(x) < static_cast<unsigned>(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<unsigned>(x) - static_cast<unsigned>(y);
+ return months(d <= 11 ? d : d + 12);
+}
+
+CONSTCD14
+inline
+month
+operator+(const month& x, const months& y) NOEXCEPT
+{
+ auto const mu = static_cast<long long>(static_cast<unsigned>(x)) + y.count() - 1;
+ auto const yr = (mu >= 0 ? mu : mu-11) / 12;
+ return month{static_cast<unsigned>(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;
+}
+
+namespace detail
+{
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month& m)
+{
+ if (m.ok())
+ {
+ CharT fmt[] = {'%', 'b', 0};
+ os << format(os.getloc(), fmt, m);
+ }
+ else
+ os << static_cast<unsigned>(m);
+ return os;
+}
+
+} // namespace detail
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const month& m)
+{
+ detail::low_level_fmt(os, m);
+ if (!m.ok())
+ os << " is not a valid month";
+ return os;
+}
+
+// year
+
+CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast<decltype(y_)>(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 int() const NOEXCEPT {return y_;}
+
+CONSTCD11
+inline
+bool
+year::ok() const NOEXCEPT
+{
+ return y_ != std::numeric_limits<short>::min();
+}
+
+CONSTCD11
+inline
+bool
+operator==(const year& x, const year& y) NOEXCEPT
+{
+ return static_cast<int>(x) == static_cast<int>(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<int>(x) < static_cast<int>(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<int>(x) - static_cast<int>(y)};
+}
+
+CONSTCD11
+inline
+year
+operator+(const year& x, const years& y) NOEXCEPT
+{
+ return year{static_cast<int>(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<int>(x) - y.count()};
+}
+
+namespace detail
+{
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year& y)
+{
+ detail::save_ostream<CharT, Traits> _(os);
+ os.fill('0');
+ os.flags(std::ios::dec | std::ios::internal);
+ os.width(4 + (y < year{0}));
+ os.imbue(std::locale::classic());
+ os << static_cast<int>(y);
+ return os;
+}
+
+} // namespace detail
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year& y)
+{
+ detail::low_level_fmt(os, y);
+ if (!y.ok())
+ os << " is not a valid year";
+ return os;
+}
+
+// weekday
+
+CONSTCD14
+inline
+unsigned char
+weekday::weekday_from_days(int z) NOEXCEPT
+{
+ auto u = static_cast<unsigned>(z);
+ return static_cast<unsigned char>(z >= -4 ? (u+4) % 7 : u % 7);
+}
+
+CONSTCD11
+inline
+weekday::weekday(unsigned wd) NOEXCEPT
+ : wd_(static_cast<decltype(wd_)>(wd != 7 ? wd : 0))
+ {}
+
+CONSTCD14
+inline
+weekday::weekday(const sys_days& dp) NOEXCEPT
+ : wd_(weekday_from_days(dp.time_since_epoch().count()))
+ {}
+
+CONSTCD14
+inline
+weekday::weekday(const local_days& dp) NOEXCEPT
+ : wd_(weekday_from_days(dp.time_since_epoch().count()))
+ {}
+
+CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {*this += days{1}; return *this;}
+CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;}
+CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {*this -= days{1}; 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 bool weekday::ok() const NOEXCEPT {return wd_ <= 6;}
+
+CONSTCD11
+inline
+unsigned weekday::c_encoding() const NOEXCEPT
+{
+ return unsigned{wd_};
+}
+
+CONSTCD11
+inline
+unsigned weekday::iso_encoding() const NOEXCEPT
+{
+ return unsigned{((wd_ == 0u) ? 7u : wd_)};
+}
+
+CONSTCD11
+inline
+bool
+operator==(const weekday& x, const weekday& y) NOEXCEPT
+{
+ return x.wd_ == y.wd_;
+}
+
+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 wdu = x.wd_ - y.wd_;
+ auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7;
+ return days{wdu - wk * 7};
+}
+
+CONSTCD14
+inline
+weekday
+operator+(const weekday& x, const days& y) NOEXCEPT
+{
+ auto const wdu = static_cast<long long>(static_cast<unsigned>(x.wd_)) + y.count();
+ auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7;
+ return weekday{static_cast<unsigned>(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;
+}
+
+namespace detail
+{
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
+{
+ if (wd.ok())
+ {
+ CharT fmt[] = {'%', 'a', 0};
+ os << format(fmt, wd);
+ }
+ else
+ os << wd.c_encoding();
+ return os;
+}
+
+} // namespace detail
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
+{
+ detail::low_level_fmt(os, wd);
+ if (!wd.ok())
+ os << " is not a valid weekday";
+ 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<unsigned>(d)};
+}
+
+CONSTCD11
+inline
+date::year
+operator "" _y(unsigned long long y) NOEXCEPT
+{
+ return date::year(static_cast<int>(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
+
+CONSTDATA date::month January{1};
+CONSTDATA date::month February{2};
+CONSTDATA date::month March{3};
+CONSTDATA date::month April{4};
+CONSTDATA date::month May{5};
+CONSTDATA date::month June{6};
+CONSTDATA date::month July{7};
+CONSTDATA date::month August{8};
+CONSTDATA date::month September{9};
+CONSTDATA date::month October{10};
+CONSTDATA date::month November{11};
+CONSTDATA date::month December{12};
+
+CONSTDATA date::weekday Monday{1};
+CONSTDATA date::weekday Tuesday{2};
+CONSTDATA date::weekday Wednesday{3};
+CONSTDATA date::weekday Thursday{4};
+CONSTDATA date::weekday Friday{5};
+CONSTDATA date::weekday Saturday{6};
+CONSTDATA date::weekday Sunday{7};
+
+// weekday_indexed
+
+CONSTCD11
+inline
+weekday
+weekday_indexed::weekday() const NOEXCEPT
+{
+ return date::weekday{static_cast<unsigned>(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;
+}
+
+#ifdef __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wconversion"
+#endif // __GNUC__
+
+CONSTCD11
+inline
+weekday_indexed::weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT
+ : wd_(static_cast<decltype(wd_)>(static_cast<unsigned>(wd.wd_)))
+ , index_(static_cast<decltype(index_)>(index))
+ {}
+
+#ifdef __GNUC__
+# pragma GCC diagnostic pop
+#endif // __GNUC__
+
+namespace detail
+{
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi)
+{
+ return low_level_fmt(os, wdi.weekday()) << '[' << wdi.index() << ']';
+}
+
+} // namespace detail
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi)
+{
+ detail::low_level_fmt(os, wdi);
+ if (!wdi.ok())
+ os << " is not a valid weekday_indexed";
+ return os;
+}
+
+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);
+}
+
+namespace detail
+{
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl)
+{
+ return low_level_fmt(os, wdl.weekday()) << "[last]";
+}
+
+} // namespace detail
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl)
+{
+ detail::low_level_fmt(os, wdl);
+ if (!wdl.ok())
+ os << " is not a valid weekday_last";
+ return os;
+}
+
+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();}
+
+template<class>
+CONSTCD14
+inline
+year_month&
+year_month::operator+=(const months& dm) NOEXCEPT
+{
+ *this = *this + dm;
+ return *this;
+}
+
+template<class>
+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);
+}
+
+template<class>
+CONSTCD14
+inline
+year_month
+operator+(const year_month& ym, const months& dm) NOEXCEPT
+{
+ auto dmi = static_cast<int>(static_cast<unsigned>(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<unsigned>(dmi));
+}
+
+template<class>
+CONSTCD14
+inline
+year_month
+operator+(const months& dm, const year_month& ym) NOEXCEPT
+{
+ return ym + dm;
+}
+
+template<class>
+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<unsigned>(x.month()) - static_cast<unsigned>(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;
+}
+
+namespace detail
+{
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year_month& ym)
+{
+ low_level_fmt(os, ym.year()) << '/';
+ return low_level_fmt(os, ym.month());
+}
+
+} // namespace detail
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year_month& ym)
+{
+ detail::low_level_fmt(os, ym);
+ if (!ym.ok())
+ os << " is not a valid year_month";
+ return os;
+}
+
+// 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<unsigned>(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);
+}
+
+namespace detail
+{
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_day& md)
+{
+ low_level_fmt(os, md.month()) << '/';
+ return low_level_fmt(os, md.day());
+}
+
+} // namespace detail
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const month_day& md)
+{
+ detail::low_level_fmt(os, md);
+ if (!md.ok())
+ os << " is not a valid month_day";
+ return os;
+}
+
+// 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);
+}
+
+namespace detail
+{
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl)
+{
+ return low_level_fmt(os, mdl.month()) << "/last";
+}
+
+} // namespace detail
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl)
+{
+ detail::low_level_fmt(os, mdl);
+ if (!mdl.ok())
+ os << " is not a valid month_day_last";
+ return os;
+}
+
+// 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);
+}
+
+namespace detail
+{
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd)
+{
+ low_level_fmt(os, mwd.month()) << '/';
+ return low_level_fmt(os, mwd.weekday_indexed());
+}
+
+} // namespace detail
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd)
+{
+ detail::low_level_fmt(os, mwd);
+ if (!mwd.ok())
+ os << " is not a valid month_weekday";
+ return os;
+}
+
+// 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);
+}
+
+namespace detail
+{
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl)
+{
+ low_level_fmt(os, mwdl.month()) << '/';
+ return low_level_fmt(os, mwdl.weekday_last());
+}
+
+} // namespace detail
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl)
+{
+ detail::low_level_fmt(os, mwdl);
+ if (!mwdl.ok())
+ os << " is not a valid month_weekday_last";
+ return os;
+}
+
+// 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)
+ {}
+
+template<class>
+CONSTCD14
+inline
+year_month_day_last&
+year_month_day_last::operator+=(const months& m) NOEXCEPT
+{
+ *this = *this + m;
+ return *this;
+}
+
+template<class>
+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() != February || !y_.is_leap()) && mdl_.ok() ?
+ d[static_cast<unsigned>(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);
+}
+
+namespace detail
+{
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl)
+{
+ low_level_fmt(os, ymdl.year()) << '/';
+ return low_level_fmt(os, ymdl.month_day_last());
+}
+
+} // namespace detail
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl)
+{
+ detail::low_level_fmt(os, ymdl);
+ if (!ymdl.ok())
+ os << " is not a valid year_month_day_last";
+ return os;
+}
+
+template<class>
+CONSTCD14
+inline
+year_month_day_last
+operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT
+{
+ return (ymdl.year() / ymdl.month() + dm) / last;
+}
+
+template<class>
+CONSTCD14
+inline
+year_month_day_last
+operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT
+{
+ return ymdl + dm;
+}
+
+template<class>
+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_;}
+
+template<class>
+CONSTCD14
+inline
+year_month_day&
+year_month_day::operator+=(const months& m) NOEXCEPT
+{
+ *this = *this + m;
+ return *this;
+}
+
+template<class>
+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<unsigned>::digits >= 18,
+ "This algorithm has not been ported to a 16 bit unsigned integer");
+ static_assert(std::numeric_limits<int>::digits >= 20,
+ "This algorithm has not been ported to a 16 bit signed integer");
+ auto const y = static_cast<int>(y_) - (m_ <= February);
+ auto const m = static_cast<unsigned>(m_);
+ auto const d = static_cast<unsigned>(d_);
+ auto const era = (y >= 0 ? y : y-399) / 400;
+ auto const yoe = static_cast<unsigned>(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<int>(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<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day& ymd)
+{
+ detail::save_ostream<CharT, Traits> _(os);
+ os.fill('0');
+ os.flags(std::ios::dec | std::ios::right);
+ os.imbue(std::locale::classic());
+ os << static_cast<int>(ymd.year()) << '-';
+ os.width(2);
+ os << static_cast<unsigned>(ymd.month()) << '-';
+ os.width(2);
+ os << static_cast<unsigned>(ymd.day());
+ if (!ymd.ok())
+ os << " is not a valid year_month_day";
+ return os;
+}
+
+CONSTCD14
+inline
+year_month_day
+year_month_day::from_days(days dp) NOEXCEPT
+{
+ static_assert(std::numeric_limits<unsigned>::digits >= 18,
+ "This algorithm has not been ported to a 16 bit unsigned integer");
+ static_assert(std::numeric_limits<int>::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<unsigned>(z - era * 146097); // [0, 146096]
+ auto const yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399]
+ auto const y = static_cast<days::rep>(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)};
+}
+
+template<class>
+CONSTCD14
+inline
+year_month_day
+operator+(const year_month_day& ymd, const months& dm) NOEXCEPT
+{
+ return (ymd.year() / ymd.month() + dm) / ymd.day();
+}
+
+template<class>
+CONSTCD14
+inline
+year_month_day
+operator+(const months& dm, const year_month_day& ymd) NOEXCEPT
+{
+ return ymd + dm;
+}
+
+template<class>
+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()))
+ {}
+
+template<class>
+CONSTCD14
+inline
+year_month_weekday&
+year_month_weekday::operator+=(const months& m) NOEXCEPT
+{
+ *this = *this + m;
+ return *this;
+}
+
+template<class>
+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<sys_days>(y_/m_/1)) +
+ days((wdi_.index()-1)*7 + 1);
+ return static_cast<unsigned>(d2.count()) <= static_cast<unsigned>((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<unsigned>(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<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday& ymwdi)
+{
+ detail::low_level_fmt(os, ymwdi.year()) << '/';
+ detail::low_level_fmt(os, ymwdi.month()) << '/';
+ detail::low_level_fmt(os, ymwdi.weekday_indexed());
+ if (!ymwdi.ok())
+ os << " is not a valid year_month_weekday";
+ return os;
+}
+
+template<class>
+CONSTCD14
+inline
+year_month_weekday
+operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT
+{
+ return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed();
+}
+
+template<class>
+CONSTCD14
+inline
+year_month_weekday
+operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT
+{
+ return ymwd + dm;
+}
+
+template<class>
+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)
+ {}
+
+template<class>
+CONSTCD14
+inline
+year_month_weekday_last&
+year_month_weekday_last::operator+=(const months& m) NOEXCEPT
+{
+ *this = *this + m;
+ return *this;
+}
+
+template<class>
+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<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last& ymwdl)
+{
+ detail::low_level_fmt(os, ymwdl.year()) << '/';
+ detail::low_level_fmt(os, ymwdl.month()) << '/';
+ detail::low_level_fmt(os, ymwdl.weekday_last());
+ if (!ymwdl.ok())
+ os << " is not a valid year_month_weekday_last";
+ return os;
+}
+
+template<class>
+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();
+}
+
+template<class>
+CONSTCD14
+inline
+year_month_weekday_last
+operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT
+{
+ return ymwdl + dm;
+}
+
+template<class>
+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<unsigned>(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<unsigned>(d));
+}
+
+CONSTCD11
+inline
+month_day
+operator/(int m, const day& d) NOEXCEPT
+{
+ return month(static_cast<unsigned>(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<unsigned>(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<unsigned>(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<unsigned>(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<unsigned>(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;
+}
+
+template <class Duration>
+struct fields;
+
+template <class CharT, class Traits, class Duration>
+std::basic_ostream<CharT, Traits>&
+to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
+ const fields<Duration>& fds, const std::string* abbrev = nullptr,
+ const std::chrono::seconds* offset_sec = nullptr);
+
+template <class CharT, class Traits, class Duration, class Alloc>
+std::basic_istream<CharT, Traits>&
+from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
+ fields<Duration>& fds, std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+ std::chrono::minutes* offset = nullptr);
+
+// hh_mm_ss
+
+namespace detail
+{
+
+struct undocumented {explicit undocumented() = default;};
+
+// width<n>::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 <std::uint64_t n, std::uint64_t d, unsigned w = 0,
+ bool should_continue = n%d != 0 && (w < 19)>
+struct width
+{
+ static_assert(d > 0, "width called with zero denominator");
+ static CONSTDATA unsigned value = 1 + width<n%d*10, d, w+1>::value;
+};
+
+template <std::uint64_t n, std::uint64_t d, unsigned w>
+struct width<n, d, w, false>
+{
+ static CONSTDATA unsigned value = 0;
+};
+
+template <unsigned exp>
+struct static_pow10
+{
+private:
+ static CONSTDATA std::uint64_t h = static_pow10<exp/2>::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 <class Duration>
+class decimal_format_seconds
+{
+ using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
+ using rep = typename CT::rep;
+ static unsigned CONSTDATA trial_width =
+ detail::width<CT::period::num, CT::period::den>::value;
+public:
+ static unsigned CONSTDATA width = trial_width < 19 ? trial_width : 6u;
+ using precision = std::chrono::duration<rep,
+ std::ratio<1, static_pow10<width>::value>>;
+
+private:
+ std::chrono::seconds s_;
+ precision sub_s_;
+
+public:
+ CONSTCD11 decimal_format_seconds()
+ : s_()
+ , sub_s_()
+ {}
+
+ CONSTCD11 explicit decimal_format_seconds(const Duration& d) NOEXCEPT
+ : s_(std::chrono::duration_cast<std::chrono::seconds>(d))
+ , sub_s_(std::chrono::duration_cast<precision>(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_;
+ }
+
+ CONSTCD11 bool in_conventional_range() const NOEXCEPT
+ {
+ return sub_s_ < std::chrono::seconds{1} && s_ < std::chrono::minutes{1};
+ }
+
+ template <class CharT, class Traits>
+ friend
+ std::basic_ostream<CharT, Traits>&
+ operator<<(std::basic_ostream<CharT, Traits>& os, const decimal_format_seconds& x)
+ {
+ return x.print(os, std::chrono::treat_as_floating_point<rep>{});
+ }
+
+ template <class CharT, class Traits>
+ std::basic_ostream<CharT, Traits>&
+ print(std::basic_ostream<CharT, Traits>& os, std::true_type) const
+ {
+ date::detail::save_ostream<CharT, Traits> _(os);
+ std::chrono::duration<rep> d = s_ + sub_s_;
+ if (d < std::chrono::seconds{10})
+ os << '0';
+ os.precision(width+6);
+ os << std::fixed << d.count();
+ return os;
+ }
+
+ template <class CharT, class Traits>
+ std::basic_ostream<CharT, Traits>&
+ print(std::basic_ostream<CharT, Traits>& os, std::false_type) const
+ {
+ date::detail::save_ostream<CharT, Traits> _(os);
+ os.fill('0');
+ os.flags(std::ios::dec | std::ios::right);
+ os.width(2);
+ os << s_.count();
+ if (width > 0)
+ {
+#if !ONLY_C_LOCALE
+ os << std::use_facet<std::numpunct<CharT>>(os.getloc()).decimal_point();
+#else
+ os << '.';
+#endif
+ date::detail::save_ostream<CharT, Traits> _s(os);
+ os.imbue(std::locale::classic());
+ os.width(width);
+ os << sub_s_.count();
+ }
+ return os;
+ }
+};
+
+template <class Rep, class Period>
+inline
+CONSTCD11
+typename std::enable_if
+ <
+ std::numeric_limits<Rep>::is_signed,
+ std::chrono::duration<Rep, Period>
+ >::type
+abs(std::chrono::duration<Rep, Period> d)
+{
+ return d >= d.zero() ? +d : -d;
+}
+
+template <class Rep, class Period>
+inline
+CONSTCD11
+typename std::enable_if
+ <
+ !std::numeric_limits<Rep>::is_signed,
+ std::chrono::duration<Rep, Period>
+ >::type
+abs(std::chrono::duration<Rep, Period> d)
+{
+ return d;
+}
+
+} // namespace detail
+
+template <class Duration>
+class hh_mm_ss
+{
+ using dfs = detail::decimal_format_seconds<typename std::common_type<Duration,
+ std::chrono::seconds>::type>;
+
+ std::chrono::hours h_;
+ std::chrono::minutes m_;
+ dfs s_;
+ bool neg_;
+
+public:
+ static unsigned CONSTDATA fractional_width = dfs::width;
+ using precision = typename dfs::precision;
+
+ CONSTCD11 hh_mm_ss() NOEXCEPT
+ : hh_mm_ss(Duration::zero())
+ {}
+
+ CONSTCD11 explicit hh_mm_ss(Duration d) NOEXCEPT
+ : h_(std::chrono::duration_cast<std::chrono::hours>(detail::abs(d)))
+ , m_(std::chrono::duration_cast<std::chrono::minutes>(detail::abs(d)) - h_)
+ , s_(detail::abs(d) - h_ - m_)
+ , neg_(d < Duration::zero())
+ {}
+
+ CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;}
+ CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;}
+ CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_.seconds();}
+ CONSTCD14 std::chrono::seconds&
+ seconds(detail::undocumented) NOEXCEPT {return s_.seconds();}
+ CONSTCD11 precision subseconds() const NOEXCEPT {return s_.subseconds();}
+ CONSTCD11 bool is_negative() const NOEXCEPT {return neg_;}
+
+ CONSTCD11 explicit operator precision() const NOEXCEPT {return to_duration();}
+ CONSTCD11 precision to_duration() const NOEXCEPT
+ {return (s_.to_duration() + m_ + h_) * (1-2*neg_);}
+
+ CONSTCD11 bool in_conventional_range() const NOEXCEPT
+ {
+ return !neg_ && h_ < days{1} && m_ < std::chrono::hours{1} &&
+ s_.in_conventional_range();
+ }
+
+private:
+
+ template <class charT, class traits>
+ friend
+ std::basic_ostream<charT, traits>&
+ operator<<(std::basic_ostream<charT, traits>& os, hh_mm_ss const& tod)
+ {
+ if (tod.is_negative())
+ os << '-';
+ if (tod.h_ < std::chrono::hours{10})
+ os << '0';
+ os << tod.h_.count() << ':';
+ if (tod.m_ < std::chrono::minutes{10})
+ os << '0';
+ os << tod.m_.count() << ':' << tod.s_;
+ return os;
+ }
+
+ template <class CharT, class Traits, class Duration2>
+ friend
+ std::basic_ostream<CharT, Traits>&
+ date::to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
+ const fields<Duration2>& fds, const std::string* abbrev,
+ const std::chrono::seconds* offset_sec);
+
+ template <class CharT, class Traits, class Duration2, class Alloc>
+ friend
+ std::basic_istream<CharT, Traits>&
+ date::from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
+ fields<Duration2>& fds,
+ std::basic_string<CharT, Traits, Alloc>* abbrev, std::chrono::minutes* offset);
+};
+
+inline
+CONSTCD14
+bool
+is_am(std::chrono::hours const& h) NOEXCEPT
+{
+ using std::chrono::hours;
+ return hours{0} <= h && h < hours{12};
+}
+
+inline
+CONSTCD14
+bool
+is_pm(std::chrono::hours const& h) NOEXCEPT
+{
+ using std::chrono::hours;
+ return hours{12} <= h && h < hours{24};
+}
+
+inline
+CONSTCD14
+std::chrono::hours
+make12(std::chrono::hours h) NOEXCEPT
+{
+ using std::chrono::hours;
+ if (h < hours{12})
+ {
+ if (h == hours{0})
+ h = hours{12};
+ }
+ else
+ {
+ if (h != hours{12})
+ h = h - hours{12};
+ }
+ return h;
+}
+
+inline
+CONSTCD14
+std::chrono::hours
+make24(std::chrono::hours h, bool is_pm) NOEXCEPT
+{
+ using std::chrono::hours;
+ if (is_pm)
+ {
+ if (h != hours{12})
+ h = h + hours{12};
+ }
+ else if (h == hours{12})
+ h = hours{0};
+ return h;
+}
+
+template <class Duration>
+using time_of_day = hh_mm_ss<Duration>;
+
+template <class Rep, class Period>
+CONSTCD11
+inline
+hh_mm_ss<std::chrono::duration<Rep, Period>>
+make_time(const std::chrono::duration<Rep, Period>& d)
+{
+ return hh_mm_ss<std::chrono::duration<Rep, Period>>(d);
+}
+
+template <class CharT, class Traits, class Duration>
+inline
+typename std::enable_if
+<
+ !std::is_convertible<Duration, days>::value,
+ std::basic_ostream<CharT, Traits>&
+>::type
+operator<<(std::basic_ostream<CharT, Traits>& os, const sys_time<Duration>& tp)
+{
+ auto const dp = date::floor<days>(tp);
+ return os << year_month_day(dp) << ' ' << make_time(tp-dp);
+}
+
+template <class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const sys_days& dp)
+{
+ return os << year_month_day(dp);
+}
+
+template <class CharT, class Traits, class Duration>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const local_time<Duration>& ut)
+{
+ return (os << sys_time<Duration>{ut.time_since_epoch()});
+}
+
+namespace detail
+{
+
+template <class CharT, std::size_t N>
+class string_literal;
+
+template <class CharT1, class CharT2, std::size_t N1, std::size_t N2>
+inline
+CONSTCD14
+string_literal<typename std::conditional<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>::type,
+ N1 + N2 - 1>
+operator+(const string_literal<CharT1, N1>& x, const string_literal<CharT2, N2>& y) NOEXCEPT;
+
+template <class CharT, std::size_t N>
+class string_literal
+{
+ CharT p_[N];
+
+ CONSTCD11 string_literal() NOEXCEPT
+ : p_{}
+ {}
+
+public:
+ using const_iterator = const CharT*;
+
+ string_literal(string_literal const&) = default;
+ string_literal& operator=(string_literal const&) = delete;
+
+ template <std::size_t N1 = 2,
+ class = typename std::enable_if<N1 == N>::type>
+ CONSTCD11 string_literal(CharT c) NOEXCEPT
+ : p_{c}
+ {
+ }
+
+ template <std::size_t N1 = 3,
+ class = typename std::enable_if<N1 == N>::type>
+ CONSTCD11 string_literal(CharT c1, CharT c2) NOEXCEPT
+ : p_{c1, c2}
+ {
+ }
+
+ template <std::size_t N1 = 4,
+ class = typename std::enable_if<N1 == N>::type>
+ CONSTCD11 string_literal(CharT c1, CharT c2, CharT c3) NOEXCEPT
+ : p_{c1, c2, c3}
+ {
+ }
+
+ CONSTCD14 string_literal(const CharT(&a)[N]) NOEXCEPT
+ : p_{}
+ {
+ for (std::size_t i = 0; i < N; ++i)
+ p_[i] = a[i];
+ }
+
+ template <class U = CharT,
+ class = typename std::enable_if<(1 < sizeof(U))>::type>
+ CONSTCD14 string_literal(const char(&a)[N]) NOEXCEPT
+ : p_{}
+ {
+ for (std::size_t i = 0; i < N; ++i)
+ p_[i] = a[i];
+ }
+
+ template <class CharT2,
+ class = typename std::enable_if<!std::is_same<CharT2, CharT>::value>::type>
+ CONSTCD14 string_literal(string_literal<CharT2, N> const& a) NOEXCEPT
+ : p_{}
+ {
+ for (std::size_t i = 0; i < N; ++i)
+ p_[i] = a[i];
+ }
+
+ CONSTCD11 const CharT* data() const NOEXCEPT {return p_;}
+ CONSTCD11 std::size_t size() const NOEXCEPT {return N-1;}
+
+ CONSTCD11 const_iterator begin() const NOEXCEPT {return p_;}
+ CONSTCD11 const_iterator end() const NOEXCEPT {return p_ + N-1;}
+
+ CONSTCD11 CharT const& operator[](std::size_t n) const NOEXCEPT
+ {
+ return p_[n];
+ }
+
+ template <class Traits>
+ friend
+ std::basic_ostream<CharT, Traits>&
+ operator<<(std::basic_ostream<CharT, Traits>& os, const string_literal& s)
+ {
+ return os << s.p_;
+ }
+
+ template <class CharT1, class CharT2, std::size_t N1, std::size_t N2>
+ friend
+ CONSTCD14
+ string_literal<typename std::conditional<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>::type,
+ N1 + N2 - 1>
+ operator+(const string_literal<CharT1, N1>& x, const string_literal<CharT2, N2>& y) NOEXCEPT;
+};
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 3>
+operator+(const string_literal<CharT, 2>& x, const string_literal<CharT, 2>& y) NOEXCEPT
+{
+ return string_literal<CharT, 3>(x[0], y[0]);
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 4>
+operator+(const string_literal<CharT, 3>& x, const string_literal<CharT, 2>& y) NOEXCEPT
+{
+ return string_literal<CharT, 4>(x[0], x[1], y[0]);
+}
+
+template <class CharT1, class CharT2, std::size_t N1, std::size_t N2>
+CONSTCD14
+inline
+string_literal<typename std::conditional<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>::type,
+ N1 + N2 - 1>
+operator+(const string_literal<CharT1, N1>& x, const string_literal<CharT2, N2>& y) NOEXCEPT
+{
+ using CT = typename std::conditional<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>::type;
+
+ string_literal<CT, N1 + N2 - 1> r;
+ std::size_t i = 0;
+ for (; i < N1-1; ++i)
+ r.p_[i] = CT(x.p_[i]);
+ for (std::size_t j = 0; j < N2; ++j, ++i)
+ r.p_[i] = CT(y.p_[j]);
+
+ return r;
+}
+
+
+template <class CharT, class Traits, class Alloc, std::size_t N>
+inline
+std::basic_string<CharT, Traits, Alloc>
+operator+(std::basic_string<CharT, Traits, Alloc> x, const string_literal<CharT, N>& y)
+{
+ x.append(y.data(), y.size());
+ return x;
+}
+
+#if __cplusplus >= 201402 && (!defined(__EDG_VERSION__) || __EDG_VERSION__ > 411) \
+ && (!defined(__SUNPRO_CC) || __SUNPRO_CC > 0x5150)
+
+template <class CharT,
+ class = std::enable_if_t<std::is_same<CharT, char>::value ||
+ std::is_same<CharT, wchar_t>::value ||
+ std::is_same<CharT, char16_t>::value ||
+ std::is_same<CharT, char32_t>::value>>
+CONSTCD14
+inline
+string_literal<CharT, 2>
+msl(CharT c) NOEXCEPT
+{
+ return string_literal<CharT, 2>{c};
+}
+
+CONSTCD14
+inline
+std::size_t
+to_string_len(std::intmax_t i)
+{
+ std::size_t r = 0;
+ do
+ {
+ i /= 10;
+ ++r;
+ } while (i > 0);
+ return r;
+}
+
+template <std::intmax_t N>
+CONSTCD14
+inline
+std::enable_if_t
+<
+ N < 10,
+ string_literal<char, to_string_len(N)+1>
+>
+msl() NOEXCEPT
+{
+ return msl(char(N % 10 + '0'));
+}
+
+template <std::intmax_t N>
+CONSTCD14
+inline
+std::enable_if_t
+<
+ 10 <= N,
+ string_literal<char, to_string_len(N)+1>
+>
+msl() NOEXCEPT
+{
+ return msl<N/10>() + msl(char(N % 10 + '0'));
+}
+
+template <class CharT, std::intmax_t N, std::intmax_t D>
+CONSTCD14
+inline
+std::enable_if_t
+<
+ std::ratio<N, D>::type::den != 1,
+ string_literal<CharT, to_string_len(std::ratio<N, D>::type::num) +
+ to_string_len(std::ratio<N, D>::type::den) + 4>
+>
+msl(std::ratio<N, D>) NOEXCEPT
+{
+ using R = typename std::ratio<N, D>::type;
+ return msl(CharT{'['}) + msl<R::num>() + msl(CharT{'/'}) +
+ msl<R::den>() + msl(CharT{']'});
+}
+
+template <class CharT, std::intmax_t N, std::intmax_t D>
+CONSTCD14
+inline
+std::enable_if_t
+<
+ std::ratio<N, D>::type::den == 1,
+ string_literal<CharT, to_string_len(std::ratio<N, D>::type::num) + 3>
+>
+msl(std::ratio<N, D>) NOEXCEPT
+{
+ using R = typename std::ratio<N, D>::type;
+ return msl(CharT{'['}) + msl<R::num>() + msl(CharT{']'});
+}
+
+
+#else // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411)
+
+inline
+std::string
+to_string(std::uint64_t x)
+{
+ return std::to_string(x);
+}
+
+template <class CharT>
+inline
+std::basic_string<CharT>
+to_string(std::uint64_t x)
+{
+ auto y = std::to_string(x);
+ return std::basic_string<CharT>(y.begin(), y.end());
+}
+
+template <class CharT, std::intmax_t N, std::intmax_t D>
+inline
+typename std::enable_if
+<
+ std::ratio<N, D>::type::den != 1,
+ std::basic_string<CharT>
+>::type
+msl(std::ratio<N, D>)
+{
+ using R = typename std::ratio<N, D>::type;
+ return std::basic_string<CharT>(1, '[') + to_string<CharT>(R::num) + CharT{'/'} +
+ to_string<CharT>(R::den) + CharT{']'};
+}
+
+template <class CharT, std::intmax_t N, std::intmax_t D>
+inline
+typename std::enable_if
+<
+ std::ratio<N, D>::type::den == 1,
+ std::basic_string<CharT>
+>::type
+msl(std::ratio<N, D>)
+{
+ using R = typename std::ratio<N, D>::type;
+ return std::basic_string<CharT>(1, '[') + to_string<CharT>(R::num) + CharT{']'};
+}
+
+#endif // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411)
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+msl(std::atto) NOEXCEPT
+{
+ return string_literal<CharT, 2>{'a'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+msl(std::femto) NOEXCEPT
+{
+ return string_literal<CharT, 2>{'f'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+msl(std::pico) NOEXCEPT
+{
+ return string_literal<CharT, 2>{'p'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+msl(std::nano) NOEXCEPT
+{
+ return string_literal<CharT, 2>{'n'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+typename std::enable_if
+<
+ std::is_same<CharT, char>::value,
+ string_literal<char, 3>
+>::type
+msl(std::micro) NOEXCEPT
+{
+ return string_literal<char, 3>{'\xC2', '\xB5'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+typename std::enable_if
+<
+ !std::is_same<CharT, char>::value,
+ string_literal<CharT, 2>
+>::type
+msl(std::micro) NOEXCEPT
+{
+ return string_literal<CharT, 2>{CharT{static_cast<unsigned char>('\xB5')}};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+msl(std::milli) NOEXCEPT
+{
+ return string_literal<CharT, 2>{'m'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+msl(std::centi) NOEXCEPT
+{
+ return string_literal<CharT, 2>{'c'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 3>
+msl(std::deca) NOEXCEPT
+{
+ return string_literal<CharT, 3>{'d', 'a'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+msl(std::deci) NOEXCEPT
+{
+ return string_literal<CharT, 2>{'d'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+msl(std::hecto) NOEXCEPT
+{
+ return string_literal<CharT, 2>{'h'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+msl(std::kilo) NOEXCEPT
+{
+ return string_literal<CharT, 2>{'k'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+msl(std::mega) NOEXCEPT
+{
+ return string_literal<CharT, 2>{'M'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+msl(std::giga) NOEXCEPT
+{
+ return string_literal<CharT, 2>{'G'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+msl(std::tera) NOEXCEPT
+{
+ return string_literal<CharT, 2>{'T'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+msl(std::peta) NOEXCEPT
+{
+ return string_literal<CharT, 2>{'P'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+msl(std::exa) NOEXCEPT
+{
+ return string_literal<CharT, 2>{'E'};
+}
+
+template <class CharT, class Period>
+CONSTCD11
+inline
+auto
+get_units(Period p)
+ -> decltype(msl<CharT>(p) + string_literal<CharT, 2>{'s'})
+{
+ return msl<CharT>(p) + string_literal<CharT, 2>{'s'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+get_units(std::ratio<1>)
+{
+ return string_literal<CharT, 2>{'s'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+get_units(std::ratio<3600>)
+{
+ return string_literal<CharT, 2>{'h'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 4>
+get_units(std::ratio<60>)
+{
+ return string_literal<CharT, 4>{'m', 'i', 'n'};
+}
+
+template <class CharT>
+CONSTCD11
+inline
+string_literal<CharT, 2>
+get_units(std::ratio<86400>)
+{
+ return string_literal<CharT, 2>{'d'};
+}
+
+template <class CharT, class Traits = std::char_traits<CharT>>
+struct make_string;
+
+template <>
+struct make_string<char>
+{
+ template <class Rep>
+ static
+ std::string
+ from(Rep n)
+ {
+ return std::to_string(n);
+ }
+};
+
+template <class Traits>
+struct make_string<char, Traits>
+{
+ template <class Rep>
+ static
+ std::basic_string<char, Traits>
+ from(Rep n)
+ {
+ auto s = std::to_string(n);
+ return std::basic_string<char, Traits>(s.begin(), s.end());
+ }
+};
+
+template <>
+struct make_string<wchar_t>
+{
+ template <class Rep>
+ static
+ std::wstring
+ from(Rep n)
+ {
+ return std::to_wstring(n);
+ }
+};
+
+template <class Traits>
+struct make_string<wchar_t, Traits>
+{
+ template <class Rep>
+ static
+ std::basic_string<wchar_t, Traits>
+ from(Rep n)
+ {
+ auto s = std::to_wstring(n);
+ return std::basic_string<wchar_t, Traits>(s.begin(), s.end());
+ }
+};
+
+} // namespace detail
+
+// to_stream
+
+CONSTDATA year nanyear{-32768};
+
+template <class Duration>
+struct fields
+{
+ year_month_day ymd{nanyear/0/0};
+ weekday wd{8u};
+ hh_mm_ss<Duration> tod{};
+ bool has_tod = false;
+
+#if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ <= 409)
+ fields() : ymd{nanyear/0/0}, wd{8u}, tod{}, has_tod{false} {}
+#else
+ fields() = default;
+#endif
+
+ fields(year_month_day ymd_) : ymd(ymd_) {}
+ fields(weekday wd_) : wd(wd_) {}
+ fields(hh_mm_ss<Duration> tod_) : tod(tod_), has_tod(true) {}
+
+ fields(year_month_day ymd_, weekday wd_) : ymd(ymd_), wd(wd_) {}
+ fields(year_month_day ymd_, hh_mm_ss<Duration> tod_) : ymd(ymd_), tod(tod_),
+ has_tod(true) {}
+
+ fields(weekday wd_, hh_mm_ss<Duration> tod_) : wd(wd_), tod(tod_), has_tod(true) {}
+
+ fields(year_month_day ymd_, weekday wd_, hh_mm_ss<Duration> tod_)
+ : ymd(ymd_)
+ , wd(wd_)
+ , tod(tod_)
+ , has_tod(true)
+ {}
+};
+
+namespace detail
+{
+
+template <class CharT, class Traits, class Duration>
+unsigned
+extract_weekday(std::basic_ostream<CharT, Traits>& os, const fields<Duration>& fds)
+{
+ if (!fds.ymd.ok() && !fds.wd.ok())
+ {
+ // fds does not contain a valid weekday
+ os.setstate(std::ios::failbit);
+ return 8;
+ }
+ weekday wd;
+ if (fds.ymd.ok())
+ {
+ wd = weekday{sys_days(fds.ymd)};
+ if (fds.wd.ok() && wd != fds.wd)
+ {
+ // fds.ymd and fds.wd are inconsistent
+ os.setstate(std::ios::failbit);
+ return 8;
+ }
+ }
+ else
+ wd = fds.wd;
+ return static_cast<unsigned>((wd - Sunday).count());
+}
+
+template <class CharT, class Traits, class Duration>
+unsigned
+extract_month(std::basic_ostream<CharT, Traits>& os, const fields<Duration>& fds)
+{
+ if (!fds.ymd.month().ok())
+ {
+ // fds does not contain a valid month
+ os.setstate(std::ios::failbit);
+ return 0;
+ }
+ return static_cast<unsigned>(fds.ymd.month());
+}
+
+} // namespace detail
+
+#if ONLY_C_LOCALE
+
+namespace detail
+{
+
+inline
+std::pair<const std::string*, const std::string*>
+weekday_names()
+{
+ static const std::string nm[] =
+ {
+ "Sunday",
+ "Monday",
+ "Tuesday",
+ "Wednesday",
+ "Thursday",
+ "Friday",
+ "Saturday",
+ "Sun",
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat"
+ };
+ return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0]));
+}
+
+inline
+std::pair<const std::string*, const std::string*>
+month_names()
+{
+ static const std::string nm[] =
+ {
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec"
+ };
+ return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0]));
+}
+
+inline
+std::pair<const std::string*, const std::string*>
+ampm_names()
+{
+ static const std::string nm[] =
+ {
+ "AM",
+ "PM"
+ };
+ return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0]));
+}
+
+template <class CharT, class Traits, class FwdIter>
+FwdIter
+scan_keyword(std::basic_istream<CharT, Traits>& is, FwdIter kb, FwdIter ke)
+{
+ size_t nkw = static_cast<size_t>(std::distance(kb, ke));
+ const unsigned char doesnt_match = '\0';
+ const unsigned char might_match = '\1';
+ const unsigned char does_match = '\2';
+ unsigned char statbuf[100];
+ unsigned char* status = statbuf;
+ std::unique_ptr<unsigned char, void(*)(void*)> stat_hold(0, free);
+ if (nkw > sizeof(statbuf))
+ {
+ status = (unsigned char*)std::malloc(nkw);
+ if (status == nullptr)
+ throw std::bad_alloc();
+ stat_hold.reset(status);
+ }
+ size_t n_might_match = nkw; // At this point, any keyword might match
+ size_t n_does_match = 0; // but none of them definitely do
+ // Initialize all statuses to might_match, except for "" keywords are does_match
+ unsigned char* st = status;
+ for (auto ky = kb; ky != ke; ++ky, ++st)
+ {
+ if (!ky->empty())
+ *st = might_match;
+ else
+ {
+ *st = does_match;
+ --n_might_match;
+ ++n_does_match;
+ }
+ }
+ // While there might be a match, test keywords against the next CharT
+ for (size_t indx = 0; is && n_might_match > 0; ++indx)
+ {
+ // Peek at the next CharT but don't consume it
+ auto ic = is.peek();
+ if (ic == EOF)
+ {
+ is.setstate(std::ios::eofbit);
+ break;
+ }
+ auto c = static_cast<char>(toupper(static_cast<unsigned char>(ic)));
+ bool consume = false;
+ // For each keyword which might match, see if the indx character is c
+ // If a match if found, consume c
+ // If a match is found, and that is the last character in the keyword,
+ // then that keyword matches.
+ // If the keyword doesn't match this character, then change the keyword
+ // to doesn't match
+ st = status;
+ for (auto ky = kb; ky != ke; ++ky, ++st)
+ {
+ if (*st == might_match)
+ {
+ if (c == static_cast<char>(toupper(static_cast<unsigned char>((*ky)[indx]))))
+ {
+ consume = true;
+ if (ky->size() == indx+1)
+ {
+ *st = does_match;
+ --n_might_match;
+ ++n_does_match;
+ }
+ }
+ else
+ {
+ *st = doesnt_match;
+ --n_might_match;
+ }
+ }
+ }
+ // consume if we matched a character
+ if (consume)
+ {
+ (void)is.get();
+ // If we consumed a character and there might be a matched keyword that
+ // was marked matched on a previous iteration, then such keywords
+ // are now marked as not matching.
+ if (n_might_match + n_does_match > 1)
+ {
+ st = status;
+ for (auto ky = kb; ky != ke; ++ky, ++st)
+ {
+ if (*st == does_match && ky->size() != indx+1)
+ {
+ *st = doesnt_match;
+ --n_does_match;
+ }
+ }
+ }
+ }
+ }
+ // We've exited the loop because we hit eof and/or we have no more "might matches".
+ // Return the first matching result
+ for (st = status; kb != ke; ++kb, ++st)
+ if (*st == does_match)
+ break;
+ if (kb == ke)
+ is.setstate(std::ios::failbit);
+ return kb;
+}
+
+} // namespace detail
+
+#endif // ONLY_C_LOCALE
+
+template <class CharT, class Traits, class Duration>
+std::basic_ostream<CharT, Traits>&
+to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
+ const fields<Duration>& fds, const std::string* abbrev,
+ const std::chrono::seconds* offset_sec)
+{
+#if ONLY_C_LOCALE
+ using detail::weekday_names;
+ using detail::month_names;
+ using detail::ampm_names;
+#endif
+ using detail::save_ostream;
+ using detail::get_units;
+ using detail::extract_weekday;
+ using detail::extract_month;
+ using std::ios;
+ using std::chrono::duration_cast;
+ using std::chrono::seconds;
+ using std::chrono::minutes;
+ using std::chrono::hours;
+ date::detail::save_ostream<CharT, Traits> ss(os);
+ os.fill(' ');
+ os.flags(std::ios::skipws | std::ios::dec);
+ os.width(0);
+ tm tm{};
+ bool insert_negative = fds.has_tod && fds.tod.to_duration() < Duration::zero();
+#if !ONLY_C_LOCALE
+ auto& facet = std::use_facet<std::time_put<CharT>>(os.getloc());
+#endif
+ const CharT* command = nullptr;
+ CharT modified = CharT{};
+ for (; *fmt; ++fmt)
+ {
+ switch (*fmt)
+ {
+ case 'a':
+ case 'A':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ tm.tm_wday = static_cast<int>(extract_weekday(os, fds));
+ if (os.fail())
+ return os;
+#if !ONLY_C_LOCALE
+ const CharT f[] = {'%', *fmt};
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+#else // ONLY_C_LOCALE
+ os << weekday_names().first[tm.tm_wday+7*(*fmt == 'a')];
+#endif // ONLY_C_LOCALE
+ }
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ modified = CharT{};
+ }
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'b':
+ case 'B':
+ case 'h':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ tm.tm_mon = static_cast<int>(extract_month(os, fds)) - 1;
+#if !ONLY_C_LOCALE
+ const CharT f[] = {'%', *fmt};
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+#else // ONLY_C_LOCALE
+ os << month_names().first[tm.tm_mon+12*(*fmt != 'B')];
+#endif // ONLY_C_LOCALE
+ }
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ modified = CharT{};
+ }
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'c':
+ case 'x':
+ if (command)
+ {
+ if (modified == CharT{'O'})
+ os << CharT{'%'} << modified << *fmt;
+ else
+ {
+ if (!fds.ymd.ok())
+ os.setstate(std::ios::failbit);
+ if (*fmt == 'c' && !fds.has_tod)
+ os.setstate(std::ios::failbit);
+#if !ONLY_C_LOCALE
+ tm = std::tm{};
+ auto const& ymd = fds.ymd;
+ auto ld = local_days(ymd);
+ if (*fmt == 'c')
+ {
+ tm.tm_sec = static_cast<int>(fds.tod.seconds().count());
+ tm.tm_min = static_cast<int>(fds.tod.minutes().count());
+ tm.tm_hour = static_cast<int>(fds.tod.hours().count());
+ }
+ tm.tm_mday = static_cast<int>(static_cast<unsigned>(ymd.day()));
+ tm.tm_mon = static_cast<int>(extract_month(os, fds) - 1);
+ tm.tm_year = static_cast<int>(ymd.year()) - 1900;
+ tm.tm_wday = static_cast<int>(extract_weekday(os, fds));
+ if (os.fail())
+ return os;
+ tm.tm_yday = static_cast<int>((ld - local_days(ymd.year()/1/1)).count());
+ CharT f[3] = {'%'};
+ auto fe = std::begin(f) + 1;
+ if (modified == CharT{'E'})
+ *fe++ = modified;
+ *fe++ = *fmt;
+ facet.put(os, os, os.fill(), &tm, std::begin(f), fe);
+#else // ONLY_C_LOCALE
+ if (*fmt == 'c')
+ {
+ auto wd = static_cast<int>(extract_weekday(os, fds));
+ os << weekday_names().first[static_cast<unsigned>(wd)+7]
+ << ' ';
+ os << month_names().first[extract_month(os, fds)-1+12] << ' ';
+ auto d = static_cast<int>(static_cast<unsigned>(fds.ymd.day()));
+ if (d < 10)
+ os << ' ';
+ os << d << ' '
+ << make_time(duration_cast<seconds>(fds.tod.to_duration()))
+ << ' ' << fds.ymd.year();
+
+ }
+ else // *fmt == 'x'
+ {
+ auto const& ymd = fds.ymd;
+ save_ostream<CharT, Traits> _(os);
+ os.fill('0');
+ os.flags(std::ios::dec | std::ios::right);
+ os.width(2);
+ os << static_cast<unsigned>(ymd.month()) << CharT{'/'};
+ os.width(2);
+ os << static_cast<unsigned>(ymd.day()) << CharT{'/'};
+ os.width(2);
+ os << static_cast<int>(ymd.year()) % 100;
+ }
+#endif // ONLY_C_LOCALE
+ }
+ command = nullptr;
+ modified = CharT{};
+ }
+ else
+ os << *fmt;
+ break;
+ case 'C':
+ if (command)
+ {
+ if (modified == CharT{'O'})
+ os << CharT{'%'} << modified << *fmt;
+ else
+ {
+ if (!fds.ymd.year().ok())
+ os.setstate(std::ios::failbit);
+ auto y = static_cast<int>(fds.ymd.year());
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#endif
+ {
+ save_ostream<CharT, Traits> _(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;
+ }
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'E'})
+ {
+ tm.tm_year = y - 1900;
+ CharT f[3] = {'%', 'E', 'C'};
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+ }
+#endif
+ }
+ command = nullptr;
+ modified = CharT{};
+ }
+ else
+ os << *fmt;
+ break;
+ case 'd':
+ case 'e':
+ if (command)
+ {
+ if (modified == CharT{'E'})
+ os << CharT{'%'} << modified << *fmt;
+ else
+ {
+ if (!fds.ymd.day().ok())
+ os.setstate(std::ios::failbit);
+ auto d = static_cast<int>(static_cast<unsigned>(fds.ymd.day()));
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#endif
+ {
+ save_ostream<CharT, Traits> _(os);
+ if (*fmt == CharT{'d'})
+ os.fill('0');
+ else
+ os.fill(' ');
+ os.flags(std::ios::dec | std::ios::right);
+ os.width(2);
+ os << d;
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ tm.tm_mday = d;
+ CharT f[3] = {'%', 'O', *fmt};
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+ }
+#endif
+ }
+ command = nullptr;
+ modified = CharT{};
+ }
+ else
+ os << *fmt;
+ break;
+ case 'D':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ if (!fds.ymd.ok())
+ os.setstate(std::ios::failbit);
+ auto const& ymd = fds.ymd;
+ save_ostream<CharT, Traits> _(os);
+ os.fill('0');
+ os.flags(std::ios::dec | std::ios::right);
+ os.width(2);
+ os << static_cast<unsigned>(ymd.month()) << CharT{'/'};
+ os.width(2);
+ os << static_cast<unsigned>(ymd.day()) << CharT{'/'};
+ os.width(2);
+ os << static_cast<int>(ymd.year()) % 100;
+ }
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ modified = CharT{};
+ }
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'F':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ if (!fds.ymd.ok())
+ os.setstate(std::ios::failbit);
+ auto const& ymd = fds.ymd;
+ save_ostream<CharT, Traits> _(os);
+ os.imbue(std::locale::classic());
+ os.fill('0');
+ os.flags(std::ios::dec | std::ios::right);
+ os.width(4);
+ os << static_cast<int>(ymd.year()) << CharT{'-'};
+ os.width(2);
+ os << static_cast<unsigned>(ymd.month()) << CharT{'-'};
+ os.width(2);
+ os << static_cast<unsigned>(ymd.day());
+ }
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ modified = CharT{};
+ }
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'g':
+ case 'G':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ if (!fds.ymd.ok())
+ os.setstate(std::ios::failbit);
+ auto ld = local_days(fds.ymd);
+ auto y = year_month_day{ld + days{3}}.year();
+ auto start = local_days((y-years{1})/December/Thursday[last]) +
+ (Monday-Thursday);
+ if (ld < start)
+ --y;
+ if (*fmt == CharT{'G'})
+ os << y;
+ else
+ {
+ save_ostream<CharT, Traits> _(os);
+ os.fill('0');
+ os.flags(std::ios::dec | std::ios::right);
+ os.width(2);
+ os << std::abs(static_cast<int>(y)) % 100;
+ }
+ }
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ modified = CharT{};
+ }
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'H':
+ case 'I':
+ if (command)
+ {
+ if (modified == CharT{'E'})
+ os << CharT{'%'} << modified << *fmt;
+ else
+ {
+ if (!fds.has_tod)
+ os.setstate(std::ios::failbit);
+ if (insert_negative)
+ {
+ os << '-';
+ insert_negative = false;
+ }
+ auto hms = fds.tod;
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#endif
+ {
+ auto h = *fmt == CharT{'I'} ? date::make12(hms.hours()) : hms.hours();
+ if (h < hours{10})
+ os << CharT{'0'};
+ os << h.count();
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ const CharT f[] = {'%', modified, *fmt};
+ tm.tm_hour = static_cast<int>(hms.hours().count());
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+ }
+#endif
+ }
+ modified = CharT{};
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'j':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ if (fds.ymd.ok() || fds.has_tod)
+ {
+ days doy;
+ if (fds.ymd.ok())
+ {
+ auto ld = local_days(fds.ymd);
+ auto y = fds.ymd.year();
+ doy = ld - local_days(y/January/1) + days{1};
+ }
+ else
+ {
+ doy = duration_cast<days>(fds.tod.to_duration());
+ }
+ save_ostream<CharT, Traits> _(os);
+ os.fill('0');
+ os.flags(std::ios::dec | std::ios::right);
+ os.width(3);
+ os << doy.count();
+ }
+ else
+ {
+ os.setstate(std::ios::failbit);
+ }
+ }
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ modified = CharT{};
+ }
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'm':
+ if (command)
+ {
+ if (modified == CharT{'E'})
+ os << CharT{'%'} << modified << *fmt;
+ else
+ {
+ if (!fds.ymd.month().ok())
+ os.setstate(std::ios::failbit);
+ auto m = static_cast<unsigned>(fds.ymd.month());
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#endif
+ {
+ if (m < 10)
+ os << CharT{'0'};
+ os << m;
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ const CharT f[] = {'%', modified, *fmt};
+ tm.tm_mon = static_cast<int>(m-1);
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+ }
+#endif
+ }
+ modified = CharT{};
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'M':
+ if (command)
+ {
+ if (modified == CharT{'E'})
+ os << CharT{'%'} << modified << *fmt;
+ else
+ {
+ if (!fds.has_tod)
+ os.setstate(std::ios::failbit);
+ if (insert_negative)
+ {
+ os << '-';
+ insert_negative = false;
+ }
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#endif
+ {
+ if (fds.tod.minutes() < minutes{10})
+ os << CharT{'0'};
+ os << fds.tod.minutes().count();
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ const CharT f[] = {'%', modified, *fmt};
+ tm.tm_min = static_cast<int>(fds.tod.minutes().count());
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+ }
+#endif
+ }
+ modified = CharT{};
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'n':
+ if (command)
+ {
+ if (modified == CharT{})
+ os << CharT{'\n'};
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ modified = CharT{};
+ }
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'p':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ if (!fds.has_tod)
+ os.setstate(std::ios::failbit);
+#if !ONLY_C_LOCALE
+ const CharT f[] = {'%', *fmt};
+ tm.tm_hour = static_cast<int>(fds.tod.hours().count());
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+#else
+ if (date::is_am(fds.tod.hours()))
+ os << ampm_names().first[0];
+ else
+ os << ampm_names().first[1];
+#endif
+ }
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ }
+ modified = CharT{};
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'Q':
+ case 'q':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ if (!fds.has_tod)
+ os.setstate(std::ios::failbit);
+ auto d = fds.tod.to_duration();
+ if (*fmt == 'q')
+ os << get_units<CharT>(typename decltype(d)::period::type{});
+ else
+ os << d.count();
+ }
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ }
+ modified = CharT{};
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'r':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ if (!fds.has_tod)
+ os.setstate(std::ios::failbit);
+#if !ONLY_C_LOCALE
+ const CharT f[] = {'%', *fmt};
+ tm.tm_hour = static_cast<int>(fds.tod.hours().count());
+ tm.tm_min = static_cast<int>(fds.tod.minutes().count());
+ tm.tm_sec = static_cast<int>(fds.tod.seconds().count());
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+#else
+ hh_mm_ss<seconds> tod(duration_cast<seconds>(fds.tod.to_duration()));
+ save_ostream<CharT, Traits> _(os);
+ os.fill('0');
+ os.width(2);
+ os << date::make12(tod.hours()).count() << CharT{':'};
+ os.width(2);
+ os << tod.minutes().count() << CharT{':'};
+ os.width(2);
+ os << tod.seconds().count() << CharT{' '};
+ if (date::is_am(tod.hours()))
+ os << ampm_names().first[0];
+ else
+ os << ampm_names().first[1];
+#endif
+ }
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ }
+ modified = CharT{};
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'R':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ if (!fds.has_tod)
+ os.setstate(std::ios::failbit);
+ if (fds.tod.hours() < hours{10})
+ os << CharT{'0'};
+ os << fds.tod.hours().count() << CharT{':'};
+ if (fds.tod.minutes() < minutes{10})
+ os << CharT{'0'};
+ os << fds.tod.minutes().count();
+ }
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ modified = CharT{};
+ }
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'S':
+ if (command)
+ {
+ if (modified == CharT{'E'})
+ os << CharT{'%'} << modified << *fmt;
+ else
+ {
+ if (!fds.has_tod)
+ os.setstate(std::ios::failbit);
+ if (insert_negative)
+ {
+ os << '-';
+ insert_negative = false;
+ }
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#endif
+ {
+ os << fds.tod.s_;
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ const CharT f[] = {'%', modified, *fmt};
+ tm.tm_sec = static_cast<int>(fds.tod.s_.seconds().count());
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+ }
+#endif
+ }
+ modified = CharT{};
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 't':
+ if (command)
+ {
+ if (modified == CharT{})
+ os << CharT{'\t'};
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ modified = CharT{};
+ }
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'T':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ if (!fds.has_tod)
+ os.setstate(std::ios::failbit);
+ os << fds.tod;
+ }
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ modified = CharT{};
+ }
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'u':
+ if (command)
+ {
+ if (modified == CharT{'E'})
+ os << CharT{'%'} << modified << *fmt;
+ else
+ {
+ auto wd = extract_weekday(os, fds);
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#endif
+ {
+ os << (wd != 0 ? wd : 7u);
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ const CharT f[] = {'%', modified, *fmt};
+ tm.tm_wday = static_cast<int>(wd);
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+ }
+#endif
+ }
+ modified = CharT{};
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'U':
+ if (command)
+ {
+ if (modified == CharT{'E'})
+ os << CharT{'%'} << modified << *fmt;
+ else
+ {
+ auto const& ymd = fds.ymd;
+ if (!ymd.ok())
+ os.setstate(std::ios::failbit);
+ auto ld = local_days(ymd);
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#endif
+ {
+ auto st = local_days(Sunday[1]/January/ymd.year());
+ if (ld < st)
+ os << CharT{'0'} << CharT{'0'};
+ else
+ {
+ auto wn = duration_cast<weeks>(ld - st).count() + 1;
+ if (wn < 10)
+ os << CharT{'0'};
+ os << wn;
+ }
+ }
+ #if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ const CharT f[] = {'%', modified, *fmt};
+ tm.tm_year = static_cast<int>(ymd.year()) - 1900;
+ tm.tm_wday = static_cast<int>(extract_weekday(os, fds));
+ if (os.fail())
+ return os;
+ tm.tm_yday = static_cast<int>((ld - local_days(ymd.year()/1/1)).count());
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+ }
+#endif
+ }
+ modified = CharT{};
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'V':
+ if (command)
+ {
+ if (modified == CharT{'E'})
+ os << CharT{'%'} << modified << *fmt;
+ else
+ {
+ if (!fds.ymd.ok())
+ os.setstate(std::ios::failbit);
+ auto ld = local_days(fds.ymd);
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#endif
+ {
+ auto y = year_month_day{ld + days{3}}.year();
+ auto st = local_days((y-years{1})/12/Thursday[last]) +
+ (Monday-Thursday);
+ if (ld < st)
+ {
+ --y;
+ st = local_days((y - years{1})/12/Thursday[last]) +
+ (Monday-Thursday);
+ }
+ auto wn = duration_cast<weeks>(ld - st).count() + 1;
+ if (wn < 10)
+ os << CharT{'0'};
+ os << wn;
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ const CharT f[] = {'%', modified, *fmt};
+ auto const& ymd = fds.ymd;
+ tm.tm_year = static_cast<int>(ymd.year()) - 1900;
+ tm.tm_wday = static_cast<int>(extract_weekday(os, fds));
+ if (os.fail())
+ return os;
+ tm.tm_yday = static_cast<int>((ld - local_days(ymd.year()/1/1)).count());
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+ }
+#endif
+ }
+ modified = CharT{};
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'w':
+ if (command)
+ {
+ auto wd = extract_weekday(os, fds);
+ if (os.fail())
+ return os;
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#else
+ if (modified != CharT{'E'})
+#endif
+ {
+ os << wd;
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ const CharT f[] = {'%', modified, *fmt};
+ tm.tm_wday = static_cast<int>(wd);
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+ }
+#endif
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ }
+ modified = CharT{};
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'W':
+ if (command)
+ {
+ if (modified == CharT{'E'})
+ os << CharT{'%'} << modified << *fmt;
+ else
+ {
+ auto const& ymd = fds.ymd;
+ if (!ymd.ok())
+ os.setstate(std::ios::failbit);
+ auto ld = local_days(ymd);
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#endif
+ {
+ auto st = local_days(Monday[1]/January/ymd.year());
+ if (ld < st)
+ os << CharT{'0'} << CharT{'0'};
+ else
+ {
+ auto wn = duration_cast<weeks>(ld - st).count() + 1;
+ if (wn < 10)
+ os << CharT{'0'};
+ os << wn;
+ }
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ const CharT f[] = {'%', modified, *fmt};
+ tm.tm_year = static_cast<int>(ymd.year()) - 1900;
+ tm.tm_wday = static_cast<int>(extract_weekday(os, fds));
+ if (os.fail())
+ return os;
+ tm.tm_yday = static_cast<int>((ld - local_days(ymd.year()/1/1)).count());
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+ }
+#endif
+ }
+ modified = CharT{};
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'X':
+ if (command)
+ {
+ if (modified == CharT{'O'})
+ os << CharT{'%'} << modified << *fmt;
+ else
+ {
+ if (!fds.has_tod)
+ os.setstate(std::ios::failbit);
+#if !ONLY_C_LOCALE
+ tm = std::tm{};
+ tm.tm_sec = static_cast<int>(fds.tod.seconds().count());
+ tm.tm_min = static_cast<int>(fds.tod.minutes().count());
+ tm.tm_hour = static_cast<int>(fds.tod.hours().count());
+ CharT f[3] = {'%'};
+ auto fe = std::begin(f) + 1;
+ if (modified == CharT{'E'})
+ *fe++ = modified;
+ *fe++ = *fmt;
+ facet.put(os, os, os.fill(), &tm, std::begin(f), fe);
+#else
+ os << fds.tod;
+#endif
+ }
+ command = nullptr;
+ modified = CharT{};
+ }
+ else
+ os << *fmt;
+ break;
+ case 'y':
+ if (command)
+ {
+ if (!fds.ymd.year().ok())
+ os.setstate(std::ios::failbit);
+ auto y = static_cast<int>(fds.ymd.year());
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+ {
+#endif
+ y = std::abs(y) % 100;
+ if (y < 10)
+ os << CharT{'0'};
+ os << y;
+#if !ONLY_C_LOCALE
+ }
+ else
+ {
+ const CharT f[] = {'%', modified, *fmt};
+ tm.tm_year = y - 1900;
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+ }
+#endif
+ modified = CharT{};
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'Y':
+ if (command)
+ {
+ if (modified == CharT{'O'})
+ os << CharT{'%'} << modified << *fmt;
+ else
+ {
+ if (!fds.ymd.year().ok())
+ os.setstate(std::ios::failbit);
+ auto y = fds.ymd.year();
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#endif
+ {
+ save_ostream<CharT, Traits> _(os);
+ os.imbue(std::locale::classic());
+ os << y;
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'E'})
+ {
+ const CharT f[] = {'%', modified, *fmt};
+ tm.tm_year = static_cast<int>(y) - 1900;
+ facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
+ }
+#endif
+ }
+ modified = CharT{};
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'z':
+ if (command)
+ {
+ if (offset_sec == nullptr)
+ {
+ // Can not format %z with unknown offset
+ os.setstate(ios::failbit);
+ return os;
+ }
+ auto m = duration_cast<minutes>(*offset_sec);
+ auto neg = m < minutes{0};
+ m = date::abs(m);
+ auto h = duration_cast<hours>(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 = nullptr;
+ modified = CharT{};
+ }
+ else
+ os << *fmt;
+ break;
+ case 'Z':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ if (abbrev == nullptr)
+ {
+ // Can not format %Z with unknown time_zone
+ os.setstate(ios::failbit);
+ return os;
+ }
+ for (auto c : *abbrev)
+ os << CharT(c);
+ }
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ modified = CharT{};
+ }
+ command = nullptr;
+ }
+ else
+ os << *fmt;
+ break;
+ case 'E':
+ case 'O':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ modified = *fmt;
+ }
+ else
+ {
+ os << CharT{'%'} << modified << *fmt;
+ command = nullptr;
+ modified = CharT{};
+ }
+ }
+ else
+ os << *fmt;
+ break;
+ case '%':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ os << CharT{'%'};
+ command = nullptr;
+ }
+ else
+ {
+ os << CharT{'%'} << modified << CharT{'%'};
+ command = nullptr;
+ modified = CharT{};
+ }
+ }
+ else
+ command = fmt;
+ break;
+ default:
+ if (command)
+ {
+ os << CharT{'%'};
+ command = nullptr;
+ }
+ if (modified != CharT{})
+ {
+ os << modified;
+ modified = CharT{};
+ }
+ os << *fmt;
+ break;
+ }
+ }
+ if (command)
+ os << CharT{'%'};
+ if (modified != CharT{})
+ os << modified;
+ return os;
+}
+
+template <class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const year& y)
+{
+ using CT = std::chrono::seconds;
+ fields<CT> fds{y/0/0};
+ return to_stream(os, fmt, fds);
+}
+
+template <class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const month& m)
+{
+ using CT = std::chrono::seconds;
+ fields<CT> fds{m/0/nanyear};
+ return to_stream(os, fmt, fds);
+}
+
+template <class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const day& d)
+{
+ using CT = std::chrono::seconds;
+ fields<CT> fds{d/0/nanyear};
+ return to_stream(os, fmt, fds);
+}
+
+template <class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const weekday& wd)
+{
+ using CT = std::chrono::seconds;
+ fields<CT> fds{wd};
+ return to_stream(os, fmt, fds);
+}
+
+template <class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const year_month& ym)
+{
+ using CT = std::chrono::seconds;
+ fields<CT> fds{ym/0};
+ return to_stream(os, fmt, fds);
+}
+
+template <class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const month_day& md)
+{
+ using CT = std::chrono::seconds;
+ fields<CT> fds{md/nanyear};
+ return to_stream(os, fmt, fds);
+}
+
+template <class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
+ const year_month_day& ymd)
+{
+ using CT = std::chrono::seconds;
+ fields<CT> fds{ymd};
+ return to_stream(os, fmt, fds);
+}
+
+template <class CharT, class Traits, class Rep, class Period>
+inline
+std::basic_ostream<CharT, Traits>&
+to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
+ const std::chrono::duration<Rep, Period>& d)
+{
+ using Duration = std::chrono::duration<Rep, Period>;
+ using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
+ fields<CT> fds{hh_mm_ss<CT>{d}};
+ return to_stream(os, fmt, fds);
+}
+
+template <class CharT, class Traits, class Duration>
+std::basic_ostream<CharT, Traits>&
+to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
+ const local_time<Duration>& tp, const std::string* abbrev = nullptr,
+ const std::chrono::seconds* offset_sec = nullptr)
+{
+ using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
+ auto ld = std::chrono::time_point_cast<days>(tp);
+ fields<CT> fds;
+ if (ld <= tp)
+ fds = fields<CT>{year_month_day{ld}, hh_mm_ss<CT>{tp-local_seconds{ld}}};
+ else
+ fds = fields<CT>{year_month_day{ld - days{1}},
+ hh_mm_ss<CT>{days{1} - (local_seconds{ld} - tp)}};
+ return to_stream(os, fmt, fds, abbrev, offset_sec);
+}
+
+template <class CharT, class Traits, class Duration>
+std::basic_ostream<CharT, Traits>&
+to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
+ const sys_time<Duration>& tp)
+{
+ using std::chrono::seconds;
+ using CT = typename std::common_type<Duration, seconds>::type;
+ const std::string abbrev("UTC");
+ CONSTDATA seconds offset{0};
+ auto sd = std::chrono::time_point_cast<days>(tp);
+ fields<CT> fds;
+ if (sd <= tp)
+ fds = fields<CT>{year_month_day{sd}, hh_mm_ss<CT>{tp-sys_seconds{sd}}};
+ else
+ fds = fields<CT>{year_month_day{sd - days{1}},
+ hh_mm_ss<CT>{days{1} - (sys_seconds{sd} - tp)}};
+ return to_stream(os, fmt, fds, &abbrev, &offset);
+}
+
+// format
+
+template <class CharT, class Streamable>
+auto
+format(const std::locale& loc, const CharT* fmt, const Streamable& tp)
+ -> decltype(to_stream(std::declval<std::basic_ostream<CharT>&>(), fmt, tp),
+ std::basic_string<CharT>{})
+{
+ std::basic_ostringstream<CharT> os;
+ os.exceptions(std::ios::failbit | std::ios::badbit);
+ os.imbue(loc);
+ to_stream(os, fmt, tp);
+ return os.str();
+}
+
+template <class CharT, class Streamable>
+auto
+format(const CharT* fmt, const Streamable& tp)
+ -> decltype(to_stream(std::declval<std::basic_ostream<CharT>&>(), fmt, tp),
+ std::basic_string<CharT>{})
+{
+ std::basic_ostringstream<CharT> os;
+ os.exceptions(std::ios::failbit | std::ios::badbit);
+ to_stream(os, fmt, tp);
+ return os.str();
+}
+
+template <class CharT, class Traits, class Alloc, class Streamable>
+auto
+format(const std::locale& loc, const std::basic_string<CharT, Traits, Alloc>& fmt,
+ const Streamable& tp)
+ -> decltype(to_stream(std::declval<std::basic_ostream<CharT, Traits>&>(), fmt.c_str(), tp),
+ std::basic_string<CharT, Traits, Alloc>{})
+{
+ std::basic_ostringstream<CharT, Traits, Alloc> os;
+ os.exceptions(std::ios::failbit | std::ios::badbit);
+ os.imbue(loc);
+ to_stream(os, fmt.c_str(), tp);
+ return os.str();
+}
+
+template <class CharT, class Traits, class Alloc, class Streamable>
+auto
+format(const std::basic_string<CharT, Traits, Alloc>& fmt, const Streamable& tp)
+ -> decltype(to_stream(std::declval<std::basic_ostream<CharT, Traits>&>(), fmt.c_str(), tp),
+ std::basic_string<CharT, Traits, Alloc>{})
+{
+ std::basic_ostringstream<CharT, Traits, Alloc> os;
+ os.exceptions(std::ios::failbit | std::ios::badbit);
+ to_stream(os, fmt.c_str(), tp);
+ return os.str();
+}
+
+// parse
+
+namespace detail
+{
+
+template <class CharT, class Traits>
+bool
+read_char(std::basic_istream<CharT, Traits>& 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 <class CharT, class Traits>
+unsigned
+read_unsigned(std::basic_istream<CharT, Traits>& 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<char>(Traits::to_char_type(ic));
+ if (!('0' <= c && c <= '9'))
+ break;
+ (void)is.get();
+ ++count;
+ x = 10*x + static_cast<unsigned>(c - '0');
+ if (count == M)
+ break;
+ }
+ if (count < m)
+ is.setstate(std::ios::failbit);
+ return x;
+}
+
+template <class CharT, class Traits>
+int
+read_signed(std::basic_istream<CharT, Traits>& is, unsigned m = 1, unsigned M = 10)
+{
+ auto ic = is.peek();
+ if (!Traits::eq_int_type(ic, Traits::eof()))
+ {
+ auto c = static_cast<char>(Traits::to_char_type(ic));
+ if (('0' <= c && c <= '9') || c == '-' || c == '+')
+ {
+ if (c == '-' || c == '+')
+ (void)is.get();
+ auto x = static_cast<int>(read_unsigned(is, std::max(m, 1u), M));
+ if (!is.fail())
+ {
+ if (c == '-')
+ x = -x;
+ return x;
+ }
+ }
+ }
+ if (m > 0)
+ is.setstate(std::ios::failbit);
+ return 0;
+}
+
+template <class CharT, class Traits>
+long double
+read_long_double(std::basic_istream<CharT, Traits>& is, unsigned m = 1, unsigned M = 10)
+{
+ unsigned count = 0;
+ unsigned fcount = 0;
+ unsigned long long i = 0;
+ unsigned long long f = 0;
+ bool parsing_fraction = false;
+#if ONLY_C_LOCALE
+ typename Traits::int_type decimal_point = '.';
+#else
+ auto decimal_point = Traits::to_int_type(
+ std::use_facet<std::numpunct<CharT>>(is.getloc()).decimal_point());
+#endif
+ while (true)
+ {
+ auto ic = is.peek();
+ if (Traits::eq_int_type(ic, Traits::eof()))
+ break;
+ if (Traits::eq_int_type(ic, decimal_point))
+ {
+ decimal_point = Traits::eof();
+ parsing_fraction = true;
+ }
+ else
+ {
+ auto c = static_cast<char>(Traits::to_char_type(ic));
+ if (!('0' <= c && c <= '9'))
+ break;
+ if (!parsing_fraction)
+ {
+ i = 10*i + static_cast<unsigned>(c - '0');
+ }
+ else
+ {
+ f = 10*f + static_cast<unsigned>(c - '0');
+ ++fcount;
+ }
+ }
+ (void)is.get();
+ if (++count == M)
+ break;
+ }
+ if (count < m)
+ {
+ is.setstate(std::ios::failbit);
+ return 0;
+ }
+ return static_cast<long double>(i) + static_cast<long double>(f)/std::pow(10.L, fcount);
+}
+
+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 <class CharT, class Traits>
+void
+read(std::basic_istream<CharT, Traits>&)
+{
+}
+
+template <class CharT, class Traits, class ...Args>
+void
+read(std::basic_istream<CharT, Traits>& is, CharT a0, Args&& ...args);
+
+template <class CharT, class Traits, class ...Args>
+void
+read(std::basic_istream<CharT, Traits>& is, rs a0, Args&& ...args);
+
+template <class CharT, class Traits, class ...Args>
+void
+read(std::basic_istream<CharT, Traits>& is, ru a0, Args&& ...args);
+
+template <class CharT, class Traits, class ...Args>
+void
+read(std::basic_istream<CharT, Traits>& is, int a0, Args&& ...args);
+
+template <class CharT, class Traits, class ...Args>
+void
+read(std::basic_istream<CharT, Traits>& is, rld a0, Args&& ...args);
+
+template <class CharT, class Traits, class ...Args>
+void
+read(std::basic_istream<CharT, Traits>& is, CharT a0, Args&& ...args)
+{
+ // No-op if a0 == CharT{}
+ if (a0 != CharT{})
+ {
+ auto ic = is.peek();
+ if (Traits::eq_int_type(ic, Traits::eof()))
+ {
+ is.setstate(std::ios::failbit | std::ios::eofbit);
+ return;
+ }
+ if (!Traits::eq(Traits::to_char_type(ic), a0))
+ {
+ is.setstate(std::ios::failbit);
+ return;
+ }
+ (void)is.get();
+ }
+ read(is, std::forward<Args>(args)...);
+}
+
+template <class CharT, class Traits, class ...Args>
+void
+read(std::basic_istream<CharT, Traits>& 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>(args)...);
+}
+
+template <class CharT, class Traits, class ...Args>
+void
+read(std::basic_istream<CharT, Traits>& is, ru a0, Args&& ...args)
+{
+ auto x = read_unsigned(is, a0.m, a0.M);
+ if (is.fail())
+ return;
+ a0.i = static_cast<int>(x);
+ read(is, std::forward<Args>(args)...);
+}
+
+template <class CharT, class Traits, class ...Args>
+void
+read(std::basic_istream<CharT, Traits>& is, int a0, Args&& ...args)
+{
+ if (a0 != -1)
+ {
+ auto u = static_cast<unsigned>(a0);
+ CharT buf[std::numeric_limits<unsigned>::digits10+2u] = {};
+ auto e = buf;
+ do
+ {
+ *e++ = static_cast<CharT>(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>(args)...);
+}
+
+template <class CharT, class Traits, class ...Args>
+void
+read(std::basic_istream<CharT, Traits>& 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>(args)...);
+}
+
+template <class T, class CharT, class Traits>
+inline
+void
+checked_set(T& value, T from, T not_a_value, std::basic_ios<CharT, Traits>& is)
+{
+ if (!is.fail())
+ {
+ if (value == not_a_value)
+ value = std::move(from);
+ else if (value != from)
+ is.setstate(std::ios::failbit);
+ }
+}
+
+} // namespace detail;
+
+template <class CharT, class Traits, class Duration, class Alloc = std::allocator<CharT>>
+std::basic_istream<CharT, Traits>&
+from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
+ fields<Duration>& fds, std::basic_string<CharT, Traits, Alloc>* abbrev,
+ std::chrono::minutes* offset)
+{
+ using std::numeric_limits;
+ using std::ios;
+ using std::chrono::duration;
+ using std::chrono::duration_cast;
+ using std::chrono::seconds;
+ using std::chrono::minutes;
+ using std::chrono::hours;
+ using detail::round_i;
+ typename std::basic_istream<CharT, Traits>::sentry ok{is, true};
+ if (ok)
+ {
+ date::detail::save_istream<CharT, Traits> ss(is);
+ is.fill(' ');
+ is.flags(std::ios::skipws | std::ios::dec);
+ is.width(0);
+#if !ONLY_C_LOCALE
+ auto& f = std::use_facet<std::time_get<CharT>>(is.getloc());
+ std::tm tm{};
+#endif
+ const CharT* command = nullptr;
+ auto modified = CharT{};
+ auto width = -1;
+
+ CONSTDATA int not_a_year = numeric_limits<short>::min();
+ CONSTDATA int not_a_2digit_year = 100;
+ CONSTDATA int not_a_century = not_a_year / 100;
+ CONSTDATA int not_a_month = 0;
+ CONSTDATA int not_a_day = 0;
+ CONSTDATA int not_a_hour = numeric_limits<int>::min();
+ CONSTDATA int not_a_hour_12_value = 0;
+ CONSTDATA int not_a_minute = not_a_hour;
+ CONSTDATA Duration not_a_second = Duration::min();
+ CONSTDATA int not_a_doy = -1;
+ CONSTDATA int not_a_weekday = 8;
+ CONSTDATA int not_a_week_num = 100;
+ CONSTDATA int not_a_ampm = -1;
+ CONSTDATA minutes not_a_offset = minutes::min();
+
+ int Y = not_a_year; // c, F, Y *
+ int y = not_a_2digit_year; // D, x, y *
+ int g = not_a_2digit_year; // g *
+ int G = not_a_year; // G *
+ int C = not_a_century; // C *
+ int m = not_a_month; // b, B, h, m, c, D, F, x *
+ int d = not_a_day; // c, d, D, e, F, x *
+ int j = not_a_doy; // j *
+ int wd = not_a_weekday; // a, A, u, w *
+ int H = not_a_hour; // c, H, R, T, X *
+ int I = not_a_hour_12_value; // I, r *
+ int p = not_a_ampm; // p, r *
+ int M = not_a_minute; // c, M, r, R, T, X *
+ Duration s = not_a_second; // c, r, S, T, X *
+ int U = not_a_week_num; // U *
+ int V = not_a_week_num; // V *
+ int W = not_a_week_num; // W *
+ std::basic_string<CharT, Traits, Alloc> temp_abbrev; // Z *
+ minutes temp_offset = not_a_offset; // z *
+
+ using detail::read;
+ using detail::rs;
+ using detail::ru;
+ using detail::rld;
+ using detail::checked_set;
+ for (; *fmt != CharT{} && !is.fail(); ++fmt)
+ {
+ switch (*fmt)
+ {
+ case 'a':
+ case 'A':
+ case 'u':
+ case 'w': // wd: a, A, u, w
+ if (command)
+ {
+ int trial_wd = not_a_weekday;
+ if (*fmt == 'a' || *fmt == 'A')
+ {
+ if (modified == CharT{})
+ {
+#if !ONLY_C_LOCALE
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ is.setstate(err);
+ if (!is.fail())
+ trial_wd = tm.tm_wday;
+#else
+ auto nm = detail::weekday_names();
+ auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first;
+ if (!is.fail())
+ trial_wd = i % 7;
+#endif
+ }
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ }
+ else // *fmt == 'u' || *fmt == 'w'
+ {
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#else
+ if (modified != CharT{'E'})
+#endif
+ {
+ read(is, ru{trial_wd, 1, width == -1 ?
+ 1u : static_cast<unsigned>(width)});
+ if (!is.fail())
+ {
+ if (*fmt == 'u')
+ {
+ if (!(1 <= trial_wd && trial_wd <= 7))
+ {
+ trial_wd = not_a_weekday;
+ is.setstate(ios::failbit);
+ }
+ else if (trial_wd == 7)
+ trial_wd = 0;
+ }
+ else // *fmt == 'w'
+ {
+ if (!(0 <= trial_wd && trial_wd <= 6))
+ {
+ trial_wd = not_a_weekday;
+ is.setstate(ios::failbit);
+ }
+ }
+ }
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ is.setstate(err);
+ if (!is.fail())
+ trial_wd = tm.tm_wday;
+ }
+#endif
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ }
+ if (trial_wd != not_a_weekday)
+ checked_set(wd, trial_wd, not_a_weekday, is);
+ }
+ else // !command
+ read(is, *fmt);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ break;
+ case 'b':
+ case 'B':
+ case 'h':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ int ttm = not_a_month;
+#if !ONLY_C_LOCALE
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ if ((err & ios::failbit) == 0)
+ ttm = tm.tm_mon + 1;
+ is.setstate(err);
+#else
+ auto nm = detail::month_names();
+ auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first;
+ if (!is.fail())
+ ttm = i % 12 + 1;
+#endif
+ checked_set(m, ttm, not_a_month, is);
+ }
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'c':
+ if (command)
+ {
+ if (modified != CharT{'O'})
+ {
+#if !ONLY_C_LOCALE
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ if ((err & ios::failbit) == 0)
+ {
+ checked_set(Y, tm.tm_year + 1900, not_a_year, is);
+ checked_set(m, tm.tm_mon + 1, not_a_month, is);
+ checked_set(d, tm.tm_mday, not_a_day, is);
+ checked_set(H, tm.tm_hour, not_a_hour, is);
+ checked_set(M, tm.tm_min, not_a_minute, is);
+ checked_set(s, duration_cast<Duration>(seconds{tm.tm_sec}),
+ not_a_second, is);
+ }
+ is.setstate(err);
+#else
+ // "%a %b %e %T %Y"
+ auto nm = detail::weekday_names();
+ auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first;
+ checked_set(wd, static_cast<int>(i % 7), not_a_weekday, is);
+ ws(is);
+ nm = detail::month_names();
+ i = detail::scan_keyword(is, nm.first, nm.second) - nm.first;
+ checked_set(m, static_cast<int>(i % 12 + 1), not_a_month, is);
+ ws(is);
+ int td = not_a_day;
+ read(is, rs{td, 1, 2});
+ checked_set(d, td, not_a_day, is);
+ ws(is);
+ using dfs = detail::decimal_format_seconds<Duration>;
+ CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
+ int tH;
+ int tM;
+ long double S{};
+ read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2},
+ CharT{':'}, rld{S, 1, w});
+ checked_set(H, tH, not_a_hour, is);
+ checked_set(M, tM, not_a_minute, is);
+ checked_set(s, round_i<Duration>(duration<long double>{S}),
+ not_a_second, is);
+ ws(is);
+ int tY = not_a_year;
+ read(is, rs{tY, 1, 4u});
+ checked_set(Y, tY, not_a_year, is);
+#endif
+ }
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'x':
+ if (command)
+ {
+ if (modified != CharT{'O'})
+ {
+#if !ONLY_C_LOCALE
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ if ((err & ios::failbit) == 0)
+ {
+ checked_set(Y, tm.tm_year + 1900, not_a_year, is);
+ checked_set(m, tm.tm_mon + 1, not_a_month, is);
+ checked_set(d, tm.tm_mday, not_a_day, is);
+ }
+ is.setstate(err);
+#else
+ // "%m/%d/%y"
+ int ty = not_a_2digit_year;
+ int tm = not_a_month;
+ int td = not_a_day;
+ read(is, ru{tm, 1, 2}, CharT{'/'}, ru{td, 1, 2}, CharT{'/'},
+ rs{ty, 1, 2});
+ checked_set(y, ty, not_a_2digit_year, is);
+ checked_set(m, tm, not_a_month, is);
+ checked_set(d, td, not_a_day, is);
+#endif
+ }
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'X':
+ if (command)
+ {
+ if (modified != CharT{'O'})
+ {
+#if !ONLY_C_LOCALE
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ if ((err & ios::failbit) == 0)
+ {
+ checked_set(H, tm.tm_hour, not_a_hour, is);
+ checked_set(M, tm.tm_min, not_a_minute, is);
+ checked_set(s, duration_cast<Duration>(seconds{tm.tm_sec}),
+ not_a_second, is);
+ }
+ is.setstate(err);
+#else
+ // "%T"
+ using dfs = detail::decimal_format_seconds<Duration>;
+ CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
+ int tH = not_a_hour;
+ int tM = not_a_minute;
+ long double S{};
+ read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2},
+ CharT{':'}, rld{S, 1, w});
+ checked_set(H, tH, not_a_hour, is);
+ checked_set(M, tM, not_a_minute, is);
+ checked_set(s, round_i<Duration>(duration<long double>{S}),
+ not_a_second, is);
+#endif
+ }
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'C':
+ if (command)
+ {
+ int tC = not_a_century;
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+ {
+#endif
+ read(is, rs{tC, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
+#if !ONLY_C_LOCALE
+ }
+ else
+ {
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ if ((err & ios::failbit) == 0)
+ {
+ auto tY = tm.tm_year + 1900;
+ tC = (tY >= 0 ? tY : tY-99) / 100;
+ }
+ is.setstate(err);
+ }
+#endif
+ checked_set(C, tC, not_a_century, is);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'D':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ int tn = not_a_month;
+ int td = not_a_day;
+ int ty = not_a_2digit_year;
+ read(is, ru{tn, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'},
+ ru{td, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'},
+ rs{ty, 1, 2});
+ checked_set(y, ty, not_a_2digit_year, is);
+ checked_set(m, tn, not_a_month, is);
+ checked_set(d, td, not_a_day, is);
+ }
+ 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{})
+ {
+ int tY = not_a_year;
+ int tn = not_a_month;
+ int td = not_a_day;
+ read(is, rs{tY, 1, width == -1 ? 4u : static_cast<unsigned>(width)},
+ CharT{'-'}, ru{tn, 1, 2}, CharT{'-'}, ru{td, 1, 2});
+ checked_set(Y, tY, not_a_year, is);
+ checked_set(m, tn, not_a_month, is);
+ checked_set(d, td, not_a_day, is);
+ }
+ 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 !ONLY_C_LOCALE
+ if (modified == CharT{})
+#else
+ if (modified != CharT{'E'})
+#endif
+ {
+ int td = not_a_day;
+ read(is, rs{td, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
+ checked_set(d, td, not_a_day, is);
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ if ((err & ios::failbit) == 0)
+ checked_set(d, tm.tm_mday, not_a_day, is);
+ is.setstate(err);
+ }
+#endif
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'H':
+ if (command)
+ {
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#else
+ if (modified != CharT{'E'})
+#endif
+ {
+ int tH = not_a_hour;
+ read(is, ru{tH, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
+ checked_set(H, tH, not_a_hour, is);
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ if ((err & ios::failbit) == 0)
+ checked_set(H, tm.tm_hour, not_a_hour, is);
+ is.setstate(err);
+ }
+#endif
+ 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{})
+ {
+ int tI = not_a_hour_12_value;
+ // reads in an hour into I, but most be in [1, 12]
+ read(is, rs{tI, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
+ if (!(1 <= tI && tI <= 12))
+ is.setstate(ios::failbit);
+ checked_set(I, tI, not_a_hour_12_value, is);
+ }
+ 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{})
+ {
+ int tj = not_a_doy;
+ read(is, ru{tj, 1, width == -1 ? 3u : static_cast<unsigned>(width)});
+ checked_set(j, tj, not_a_doy, is);
+ }
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'M':
+ if (command)
+ {
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#else
+ if (modified != CharT{'E'})
+#endif
+ {
+ int tM = not_a_minute;
+ read(is, ru{tM, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
+ checked_set(M, tM, not_a_minute, is);
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ if ((err & ios::failbit) == 0)
+ checked_set(M, tm.tm_min, not_a_minute, is);
+ is.setstate(err);
+ }
+#endif
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'm':
+ if (command)
+ {
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#else
+ if (modified != CharT{'E'})
+#endif
+ {
+ int tn = not_a_month;
+ read(is, rs{tn, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
+ checked_set(m, tn, not_a_month, is);
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ if ((err & ios::failbit) == 0)
+ checked_set(m, tm.tm_mon + 1, not_a_month, is);
+ is.setstate(err);
+ }
+#endif
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'n':
+ case 't':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ // %n matches a single white space character
+ // %t matches 0 or 1 white space characters
+ auto ic = is.peek();
+ if (Traits::eq_int_type(ic, Traits::eof()))
+ {
+ ios::iostate err = ios::eofbit;
+ if (*fmt == 'n')
+ err |= ios::failbit;
+ is.setstate(err);
+ break;
+ }
+ if (isspace(ic))
+ {
+ (void)is.get();
+ }
+ else if (*fmt == 'n')
+ is.setstate(ios::failbit);
+ }
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'p':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ int tp = not_a_ampm;
+#if !ONLY_C_LOCALE
+ tm = std::tm{};
+ tm.tm_hour = 1;
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ is.setstate(err);
+ if (tm.tm_hour == 1)
+ tp = 0;
+ else if (tm.tm_hour == 13)
+ tp = 1;
+ else
+ is.setstate(err);
+#else
+ auto nm = detail::ampm_names();
+ auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first;
+ tp = static_cast<decltype(tp)>(i);
+#endif
+ checked_set(p, tp, not_a_ampm, is);
+ }
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+
+ break;
+ case 'r':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+#if !ONLY_C_LOCALE
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ if ((err & ios::failbit) == 0)
+ {
+ checked_set(H, tm.tm_hour, not_a_hour, is);
+ checked_set(M, tm.tm_min, not_a_hour, is);
+ checked_set(s, duration_cast<Duration>(seconds{tm.tm_sec}),
+ not_a_second, is);
+ }
+ is.setstate(err);
+#else
+ // "%I:%M:%S %p"
+ using dfs = detail::decimal_format_seconds<Duration>;
+ CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
+ long double S{};
+ int tI = not_a_hour_12_value;
+ int tM = not_a_minute;
+ read(is, ru{tI, 1, 2}, CharT{':'}, ru{tM, 1, 2},
+ CharT{':'}, rld{S, 1, w});
+ checked_set(I, tI, not_a_hour_12_value, is);
+ checked_set(M, tM, not_a_minute, is);
+ checked_set(s, round_i<Duration>(duration<long double>{S}),
+ not_a_second, is);
+ ws(is);
+ auto nm = detail::ampm_names();
+ auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first;
+ checked_set(p, static_cast<int>(i), not_a_ampm, is);
+#endif
+ }
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'R':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ int tH = not_a_hour;
+ int tM = not_a_minute;
+ read(is, ru{tH, 1, 2}, CharT{'\0'}, CharT{':'}, CharT{'\0'},
+ ru{tM, 1, 2}, CharT{'\0'});
+ checked_set(H, tH, not_a_hour, is);
+ checked_set(M, tM, not_a_minute, is);
+ }
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'S':
+ if (command)
+ {
+ #if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#else
+ if (modified != CharT{'E'})
+#endif
+ {
+ using dfs = detail::decimal_format_seconds<Duration>;
+ CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
+ long double S{};
+ read(is, rld{S, 1, width == -1 ? w : static_cast<unsigned>(width)});
+ checked_set(s, round_i<Duration>(duration<long double>{S}),
+ not_a_second, is);
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'O'})
+ {
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ if ((err & ios::failbit) == 0)
+ checked_set(s, duration_cast<Duration>(seconds{tm.tm_sec}),
+ not_a_second, is);
+ is.setstate(err);
+ }
+#endif
+ 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<Duration>;
+ CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
+ int tH = not_a_hour;
+ int tM = not_a_minute;
+ long double S{};
+ read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2},
+ CharT{':'}, rld{S, 1, w});
+ checked_set(H, tH, not_a_hour, is);
+ checked_set(M, tM, not_a_minute, is);
+ checked_set(s, round_i<Duration>(duration<long double>{S}),
+ not_a_second, is);
+ }
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'Y':
+ if (command)
+ {
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#else
+ if (modified != CharT{'O'})
+#endif
+ {
+ int tY = not_a_year;
+ read(is, rs{tY, 1, width == -1 ? 4u : static_cast<unsigned>(width)});
+ checked_set(Y, tY, not_a_year, is);
+ }
+#if !ONLY_C_LOCALE
+ else if (modified == CharT{'E'})
+ {
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ if ((err & ios::failbit) == 0)
+ checked_set(Y, tm.tm_year + 1900, not_a_year, is);
+ is.setstate(err);
+ }
+#endif
+ else
+ read(is, CharT{'%'}, width, modified, *fmt);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'y':
+ if (command)
+ {
+#if !ONLY_C_LOCALE
+ if (modified == CharT{})
+#endif
+ {
+ int ty = not_a_2digit_year;
+ read(is, ru{ty, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
+ checked_set(y, ty, not_a_2digit_year, is);
+ }
+#if !ONLY_C_LOCALE
+ else
+ {
+ ios::iostate err = ios::goodbit;
+ f.get(is, nullptr, is, err, &tm, command, fmt+1);
+ if ((err & ios::failbit) == 0)
+ checked_set(Y, tm.tm_year + 1900, not_a_year, is);
+ is.setstate(err);
+ }
+#endif
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'g':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ int tg = not_a_2digit_year;
+ read(is, ru{tg, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
+ checked_set(g, tg, not_a_2digit_year, is);
+ }
+ 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{})
+ {
+ int tG = not_a_year;
+ read(is, rs{tG, 1, width == -1 ? 4u : static_cast<unsigned>(width)});
+ checked_set(G, tG, not_a_year, is);
+ }
+ 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{})
+ {
+ int tU = not_a_week_num;
+ read(is, ru{tU, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
+ checked_set(U, tU, not_a_week_num, is);
+ }
+ 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{})
+ {
+ int tV = not_a_week_num;
+ read(is, ru{tV, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
+ checked_set(V, tV, not_a_week_num, is);
+ }
+ 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{})
+ {
+ int tW = not_a_week_num;
+ read(is, ru{tW, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
+ checked_set(W, tW, not_a_week_num, is);
+ }
+ 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 tH, tM;
+ minutes toff = not_a_offset;
+ bool neg = false;
+ auto ic = is.peek();
+ if (!Traits::eq_int_type(ic, Traits::eof()))
+ {
+ auto c = static_cast<char>(Traits::to_char_type(ic));
+ if (c == '-')
+ neg = true;
+ }
+ if (modified == CharT{})
+ {
+ read(is, rs{tH, 2, 2});
+ if (!is.fail())
+ toff = hours{std::abs(tH)};
+ if (is.good())
+ {
+ ic = is.peek();
+ if (!Traits::eq_int_type(ic, Traits::eof()))
+ {
+ auto c = static_cast<char>(Traits::to_char_type(ic));
+ if ('0' <= c && c <= '9')
+ {
+ read(is, ru{tM, 2, 2});
+ if (!is.fail())
+ toff += minutes{tM};
+ }
+ }
+ }
+ }
+ else
+ {
+ read(is, rs{tH, 1, 2});
+ if (!is.fail())
+ toff = hours{std::abs(tH)};
+ if (is.good())
+ {
+ ic = is.peek();
+ if (!Traits::eq_int_type(ic, Traits::eof()))
+ {
+ auto c = static_cast<char>(Traits::to_char_type(ic));
+ if (c == ':')
+ {
+ (void)is.get();
+ read(is, ru{tM, 2, 2});
+ if (!is.fail())
+ toff += minutes{tM};
+ }
+ }
+ }
+ }
+ if (neg)
+ toff = -toff;
+ checked_set(temp_offset, toff, not_a_offset, is);
+ command = nullptr;
+ width = -1;
+ modified = CharT{};
+ }
+ else
+ read(is, *fmt);
+ break;
+ case 'Z':
+ if (command)
+ {
+ if (modified == CharT{})
+ {
+ std::basic_string<CharT, Traits, Alloc> buf;
+ while (is.rdstate() == std::ios::goodbit)
+ {
+ auto i = is.rdbuf()->sgetc();
+ if (Traits::eq_int_type(i, Traits::eof()))
+ {
+ is.setstate(ios::eofbit);
+ break;
+ }
+ auto wc = Traits::to_char_type(i);
+ auto c = static_cast<char>(wc);
+ // is c a valid time zone name or abbreviation character?
+ if (!(CharT{1} < wc && wc < CharT{127}) || !(isalnum(c) ||
+ c == '_' || c == '/' || c == '-' || c == '+'))
+ break;
+ buf.push_back(c);
+ is.rdbuf()->sbumpc();
+ }
+ if (buf.empty())
+ is.setstate(ios::failbit);
+ checked_set(temp_abbrev, buf, {}, is);
+ }
+ 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<char>(*fmt) - '0';
+ while ('0' <= fmt[1] && fmt[1] <= '9')
+ width = 10*width + static_cast<char>(*++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 // !command
+ {
+ if (isspace(static_cast<unsigned char>(*fmt)))
+ {
+ // space matches 0 or more white space characters
+ if (is.good())
+ ws(is);
+ }
+ else
+ read(is, *fmt);
+ }
+ break;
+ }
+ }
+ // is.fail() || *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)
+ {
+ // Convert y and an optional C to Y
+ 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)
+ {
+ // Convert g and an optional C to G
+ 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 (Y < static_cast<int>(year::min()) || Y > static_cast<int>(year::max()))
+ Y = not_a_year;
+ bool computed = false;
+ if (G != not_a_year && V != not_a_week_num && wd != not_a_weekday)
+ {
+ year_month_day ymd_trial = sys_days(year{G-1}/December/Thursday[last]) +
+ (Monday-Thursday) + weeks{V-1} +
+ (weekday{static_cast<unsigned>(wd)}-Monday);
+ if (Y == not_a_year)
+ Y = static_cast<int>(ymd_trial.year());
+ else if (year{Y} != ymd_trial.year())
+ goto broken;
+ if (m == not_a_month)
+ m = static_cast<int>(static_cast<unsigned>(ymd_trial.month()));
+ else if (month(static_cast<unsigned>(m)) != ymd_trial.month())
+ goto broken;
+ if (d == not_a_day)
+ d = static_cast<int>(static_cast<unsigned>(ymd_trial.day()));
+ else if (day(static_cast<unsigned>(d)) != ymd_trial.day())
+ goto broken;
+ computed = true;
+ }
+ if (Y != not_a_year && U != not_a_week_num && wd != not_a_weekday)
+ {
+ year_month_day ymd_trial = sys_days(year{Y}/January/Sunday[1]) +
+ weeks{U-1} +
+ (weekday{static_cast<unsigned>(wd)} - Sunday);
+ if (Y == not_a_year)
+ Y = static_cast<int>(ymd_trial.year());
+ else if (year{Y} != ymd_trial.year())
+ goto broken;
+ if (m == not_a_month)
+ m = static_cast<int>(static_cast<unsigned>(ymd_trial.month()));
+ else if (month(static_cast<unsigned>(m)) != ymd_trial.month())
+ goto broken;
+ if (d == not_a_day)
+ d = static_cast<int>(static_cast<unsigned>(ymd_trial.day()));
+ else if (day(static_cast<unsigned>(d)) != ymd_trial.day())
+ goto broken;
+ computed = true;
+ }
+ if (Y != not_a_year && W != not_a_week_num && wd != not_a_weekday)
+ {
+ year_month_day ymd_trial = sys_days(year{Y}/January/Monday[1]) +
+ weeks{W-1} +
+ (weekday{static_cast<unsigned>(wd)} - Monday);
+ if (Y == not_a_year)
+ Y = static_cast<int>(ymd_trial.year());
+ else if (year{Y} != ymd_trial.year())
+ goto broken;
+ if (m == not_a_month)
+ m = static_cast<int>(static_cast<unsigned>(ymd_trial.month()));
+ else if (month(static_cast<unsigned>(m)) != ymd_trial.month())
+ goto broken;
+ if (d == not_a_day)
+ d = static_cast<int>(static_cast<unsigned>(ymd_trial.day()));
+ else if (day(static_cast<unsigned>(d)) != ymd_trial.day())
+ goto broken;
+ computed = true;
+ }
+ if (j != not_a_doy && Y != not_a_year)
+ {
+ auto ymd_trial = year_month_day{local_days(year{Y}/1/1) + days{j-1}};
+ if (m == not_a_month)
+ m = static_cast<int>(static_cast<unsigned>(ymd_trial.month()));
+ else if (month(static_cast<unsigned>(m)) != ymd_trial.month())
+ goto broken;
+ if (d == not_a_day)
+ d = static_cast<int>(static_cast<unsigned>(ymd_trial.day()));
+ else if (day(static_cast<unsigned>(d)) != ymd_trial.day())
+ goto broken;
+ j = not_a_doy;
+ }
+ auto ymd = year{Y}/m/d;
+ if (ymd.ok())
+ {
+ if (wd == not_a_weekday)
+ wd = static_cast<int>((weekday(sys_days(ymd)) - Sunday).count());
+ else if (wd != static_cast<int>((weekday(sys_days(ymd)) - Sunday).count()))
+ goto broken;
+ if (!computed)
+ {
+ if (G != not_a_year || V != not_a_week_num)
+ {
+ sys_days sd = ymd;
+ auto G_trial = year_month_day{sd + days{3}}.year();
+ auto start = sys_days((G_trial - years{1})/December/Thursday[last]) +
+ (Monday - Thursday);
+ if (sd < start)
+ {
+ --G_trial;
+ if (V != not_a_week_num)
+ start = sys_days((G_trial - years{1})/December/Thursday[last])
+ + (Monday - Thursday);
+ }
+ if (G != not_a_year && G != static_cast<int>(G_trial))
+ goto broken;
+ if (V != not_a_week_num)
+ {
+ auto V_trial = duration_cast<weeks>(sd - start).count() + 1;
+ if (V != V_trial)
+ goto broken;
+ }
+ }
+ if (U != not_a_week_num)
+ {
+ auto start = sys_days(Sunday[1]/January/ymd.year());
+ auto U_trial = floor<weeks>(sys_days(ymd) - start).count() + 1;
+ if (U != U_trial)
+ goto broken;
+ }
+ if (W != not_a_week_num)
+ {
+ auto start = sys_days(Monday[1]/January/ymd.year());
+ auto W_trial = floor<weeks>(sys_days(ymd) - start).count() + 1;
+ if (W != W_trial)
+ goto broken;
+ }
+ }
+ }
+ fds.ymd = ymd;
+ if (I != not_a_hour_12_value)
+ {
+ if (!(1 <= I && I <= 12))
+ goto broken;
+ if (p != not_a_ampm)
+ {
+ // p is in [0, 1] == [AM, PM]
+ // Store trial H in I
+ if (I == 12)
+ --p;
+ I += p*12;
+ // Either set H from I or make sure H and I are consistent
+ if (H == not_a_hour)
+ H = I;
+ else if (I != H)
+ goto broken;
+ }
+ else // p == not_a_ampm
+ {
+ // if H, make sure H and I could be consistent
+ if (H != not_a_hour)
+ {
+ if (I == 12)
+ {
+ if (H != 0 && H != 12)
+ goto broken;
+ }
+ else if (!(I == H || I == H+12))
+ {
+ goto broken;
+ }
+ }
+ else // I is ambiguous, AM or PM?
+ goto broken;
+ }
+ }
+ if (H != not_a_hour)
+ {
+ fds.has_tod = true;
+ fds.tod = hh_mm_ss<Duration>{hours{H}};
+ }
+ if (M != not_a_minute)
+ {
+ fds.has_tod = true;
+ fds.tod.m_ = minutes{M};
+ }
+ if (s != not_a_second)
+ {
+ fds.has_tod = true;
+ fds.tod.s_ = detail::decimal_format_seconds<Duration>{s};
+ }
+ if (j != not_a_doy)
+ {
+ fds.has_tod = true;
+ fds.tod.h_ += hours{days{j}};
+ }
+ if (wd != not_a_weekday)
+ fds.wd = weekday{static_cast<unsigned>(wd)};
+ if (abbrev != nullptr)
+ *abbrev = std::move(temp_abbrev);
+ if (offset != nullptr && temp_offset != not_a_offset)
+ *offset = temp_offset;
+ }
+ return is;
+ }
+broken:
+ is.setstate(ios::failbit);
+ return is;
+}
+
+template <class CharT, class Traits, class Alloc = std::allocator<CharT>>
+std::basic_istream<CharT, Traits>&
+from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, year& y,
+ std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+ std::chrono::minutes* offset = nullptr)
+{
+ using CT = std::chrono::seconds;
+ fields<CT> fds{};
+ date::from_stream(is, fmt, fds, abbrev, offset);
+ if (!fds.ymd.year().ok())
+ is.setstate(std::ios::failbit);
+ if (!is.fail())
+ y = fds.ymd.year();
+ return is;
+}
+
+template <class CharT, class Traits, class Alloc = std::allocator<CharT>>
+std::basic_istream<CharT, Traits>&
+from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, month& m,
+ std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+ std::chrono::minutes* offset = nullptr)
+{
+ using CT = std::chrono::seconds;
+ fields<CT> fds{};
+ date::from_stream(is, fmt, fds, abbrev, offset);
+ if (!fds.ymd.month().ok())
+ is.setstate(std::ios::failbit);
+ if (!is.fail())
+ m = fds.ymd.month();
+ return is;
+}
+
+template <class CharT, class Traits, class Alloc = std::allocator<CharT>>
+std::basic_istream<CharT, Traits>&
+from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, day& d,
+ std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+ std::chrono::minutes* offset = nullptr)
+{
+ using CT = std::chrono::seconds;
+ fields<CT> fds{};
+ date::from_stream(is, fmt, fds, abbrev, offset);
+ if (!fds.ymd.day().ok())
+ is.setstate(std::ios::failbit);
+ if (!is.fail())
+ d = fds.ymd.day();
+ return is;
+}
+
+template <class CharT, class Traits, class Alloc = std::allocator<CharT>>
+std::basic_istream<CharT, Traits>&
+from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, weekday& wd,
+ std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+ std::chrono::minutes* offset = nullptr)
+{
+ using CT = std::chrono::seconds;
+ fields<CT> fds{};
+ date::from_stream(is, fmt, fds, abbrev, offset);
+ if (!fds.wd.ok())
+ is.setstate(std::ios::failbit);
+ if (!is.fail())
+ wd = fds.wd;
+ return is;
+}
+
+template <class CharT, class Traits, class Alloc = std::allocator<CharT>>
+std::basic_istream<CharT, Traits>&
+from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, year_month& ym,
+ std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+ std::chrono::minutes* offset = nullptr)
+{
+ using CT = std::chrono::seconds;
+ fields<CT> fds{};
+ date::from_stream(is, fmt, fds, abbrev, offset);
+ if (!fds.ymd.month().ok())
+ is.setstate(std::ios::failbit);
+ if (!is.fail())
+ ym = fds.ymd.year()/fds.ymd.month();
+ return is;
+}
+
+template <class CharT, class Traits, class Alloc = std::allocator<CharT>>
+std::basic_istream<CharT, Traits>&
+from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, month_day& md,
+ std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+ std::chrono::minutes* offset = nullptr)
+{
+ using CT = std::chrono::seconds;
+ fields<CT> fds{};
+ date::from_stream(is, fmt, fds, abbrev, offset);
+ if (!fds.ymd.month().ok() || !fds.ymd.day().ok())
+ is.setstate(std::ios::failbit);
+ if (!is.fail())
+ md = fds.ymd.month()/fds.ymd.day();
+ return is;
+}
+
+template <class CharT, class Traits, class Alloc = std::allocator<CharT>>
+std::basic_istream<CharT, Traits>&
+from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
+ year_month_day& ymd, std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+ std::chrono::minutes* offset = nullptr)
+{
+ using CT = std::chrono::seconds;
+ fields<CT> fds{};
+ date::from_stream(is, fmt, fds, abbrev, offset);
+ if (!fds.ymd.ok())
+ is.setstate(std::ios::failbit);
+ if (!is.fail())
+ ymd = fds.ymd;
+ return is;
+}
+
+template <class Duration, class CharT, class Traits, class Alloc = std::allocator<CharT>>
+std::basic_istream<CharT, Traits>&
+from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
+ sys_time<Duration>& tp, std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+ std::chrono::minutes* offset = nullptr)
+{
+ using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
+ using detail::round_i;
+ std::chrono::minutes offset_local{};
+ auto offptr = offset ? offset : &offset_local;
+ fields<CT> fds{};
+ fds.has_tod = true;
+ date::from_stream(is, fmt, fds, abbrev, offptr);
+ if (!fds.ymd.ok() || !fds.tod.in_conventional_range())
+ is.setstate(std::ios::failbit);
+ if (!is.fail())
+ tp = round_i<Duration>(sys_days(fds.ymd) - *offptr + fds.tod.to_duration());
+ return is;
+}
+
+template <class Duration, class CharT, class Traits, class Alloc = std::allocator<CharT>>
+std::basic_istream<CharT, Traits>&
+from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
+ local_time<Duration>& tp, std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+ std::chrono::minutes* offset = nullptr)
+{
+ using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
+ using detail::round_i;
+ fields<CT> fds{};
+ fds.has_tod = true;
+ date::from_stream(is, fmt, fds, abbrev, offset);
+ if (!fds.ymd.ok() || !fds.tod.in_conventional_range())
+ is.setstate(std::ios::failbit);
+ if (!is.fail())
+ tp = round_i<Duration>(local_seconds{local_days(fds.ymd)} + fds.tod.to_duration());
+ return is;
+}
+
+template <class Rep, class Period, class CharT, class Traits, class Alloc = std::allocator<CharT>>
+std::basic_istream<CharT, Traits>&
+from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
+ std::chrono::duration<Rep, Period>& d,
+ std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+ std::chrono::minutes* offset = nullptr)
+{
+ using Duration = std::chrono::duration<Rep, Period>;
+ using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
+ using detail::round_i;
+ fields<CT> fds{};
+ date::from_stream(is, fmt, fds, abbrev, offset);
+ if (!fds.has_tod)
+ is.setstate(std::ios::failbit);
+ if (!is.fail())
+ d = round_i<Duration>(fds.tod.to_duration());
+ return is;
+}
+
+template <class Parsable, class CharT, class Traits = std::char_traits<CharT>,
+ class Alloc = std::allocator<CharT>>
+struct parse_manip
+{
+ const std::basic_string<CharT, Traits, Alloc> format_;
+ Parsable& tp_;
+ std::basic_string<CharT, Traits, Alloc>* abbrev_;
+ std::chrono::minutes* offset_;
+
+public:
+ parse_manip(std::basic_string<CharT, Traits, Alloc> format, Parsable& tp,
+ std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+ std::chrono::minutes* offset = nullptr)
+ : format_(std::move(format))
+ , tp_(tp)
+ , abbrev_(abbrev)
+ , offset_(offset)
+ {}
+
+#if HAS_STRING_VIEW
+ parse_manip(const CharT* format, Parsable& tp,
+ std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+ std::chrono::minutes* offset = nullptr)
+ : format_(format)
+ , tp_(tp)
+ , abbrev_(abbrev)
+ , offset_(offset)
+ {}
+
+ parse_manip(std::basic_string_view<CharT, Traits> format, Parsable& tp,
+ std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+ std::chrono::minutes* offset = nullptr)
+ : format_(format)
+ , tp_(tp)
+ , abbrev_(abbrev)
+ , offset_(offset)
+ {}
+#endif // HAS_STRING_VIEW
+};
+
+template <class Parsable, class CharT, class Traits, class Alloc>
+std::basic_istream<CharT, Traits>&
+operator>>(std::basic_istream<CharT, Traits>& is,
+ const parse_manip<Parsable, CharT, Traits, Alloc>& x)
+{
+ return date::from_stream(is, x.format_.c_str(), x.tp_, x.abbrev_, x.offset_);
+}
+
+template <class Parsable, class CharT, class Traits, class Alloc>
+inline
+auto
+parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp)
+ -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
+ format.c_str(), tp),
+ parse_manip<Parsable, CharT, Traits, Alloc>{format, tp})
+{
+ return {format, tp};
+}
+
+template <class Parsable, class CharT, class Traits, class Alloc>
+inline
+auto
+parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp,
+ std::basic_string<CharT, Traits, Alloc>& abbrev)
+ -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
+ format.c_str(), tp, &abbrev),
+ parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev})
+{
+ return {format, tp, &abbrev};
+}
+
+template <class Parsable, class CharT, class Traits, class Alloc>
+inline
+auto
+parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp,
+ std::chrono::minutes& offset)
+ -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
+ format.c_str(), tp,
+ std::declval<std::basic_string<CharT, Traits, Alloc>*>(),
+ &offset),
+ parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, nullptr, &offset})
+{
+ return {format, tp, nullptr, &offset};
+}
+
+template <class Parsable, class CharT, class Traits, class Alloc>
+inline
+auto
+parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp,
+ std::basic_string<CharT, Traits, Alloc>& abbrev, std::chrono::minutes& offset)
+ -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
+ format.c_str(), tp, &abbrev, &offset),
+ parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev, &offset})
+{
+ return {format, tp, &abbrev, &offset};
+}
+
+// const CharT* formats
+
+template <class Parsable, class CharT>
+inline
+auto
+parse(const CharT* format, Parsable& tp)
+ -> decltype(date::from_stream(std::declval<std::basic_istream<CharT>&>(), format, tp),
+ parse_manip<Parsable, CharT>{format, tp})
+{
+ return {format, tp};
+}
+
+template <class Parsable, class CharT, class Traits, class Alloc>
+inline
+auto
+parse(const CharT* format, Parsable& tp, std::basic_string<CharT, Traits, Alloc>& abbrev)
+ -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), format,
+ tp, &abbrev),
+ parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev})
+{
+ return {format, tp, &abbrev};
+}
+
+template <class Parsable, class CharT>
+inline
+auto
+parse(const CharT* format, Parsable& tp, std::chrono::minutes& offset)
+ -> decltype(date::from_stream(std::declval<std::basic_istream<CharT>&>(), format,
+ tp, std::declval<std::basic_string<CharT>*>(), &offset),
+ parse_manip<Parsable, CharT>{format, tp, nullptr, &offset})
+{
+ return {format, tp, nullptr, &offset};
+}
+
+template <class Parsable, class CharT, class Traits, class Alloc>
+inline
+auto
+parse(const CharT* format, Parsable& tp,
+ std::basic_string<CharT, Traits, Alloc>& abbrev, std::chrono::minutes& offset)
+ -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), format,
+ tp, &abbrev, &offset),
+ parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev, &offset})
+{
+ return {format, tp, &abbrev, &offset};
+}
+
+// duration streaming
+
+template <class CharT, class Traits, class Rep, class Period>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os,
+ const std::chrono::duration<Rep, Period>& d)
+{
+ return os << detail::make_string<CharT, Traits>::from(d.count()) +
+ detail::get_units<CharT>(typename Period::type{});
+}
+
+} // namespace date
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+#ifdef __GNUC__
+# pragma GCC diagnostic pop
+#endif
+
+#endif // DATE_H
diff --git a/include/142bot/db.hpp b/include/142bot/db.hpp
index 69245d3..f4f2769 100644
--- a/include/142bot/db.hpp
+++ b/include/142bot/db.hpp
@@ -22,6 +22,7 @@
#include <string>
#include <variant>
#include <pqxx/pqxx>
+#include <142bot/date.h>
namespace db {
@@ -35,3 +36,72 @@ namespace db {
resultset query(const std::string &format, const paramlist &parameters);
const std::string& error();
}
+
+
+/**
+ * https://gist.github.com/JadeMatrix/ef9c5b0292d3370c674233355850528b
+*/
+namespace asdf {
+ using timestamp = date::sys_time< std::chrono::microseconds >;
+
+ timestamp from_iso8601_str( const std::string& );
+ bool from_iso8601_str( const std::string&, timestamp& );
+ std::string to_iso8601_str( const timestamp& );
+ std::string to_http_ts_str( const timestamp& );
+
+ timestamp from_unix_time( unsigned int );
+ unsigned int to_unix_time( const timestamp& );
+}
+
+// Template specialization of `pqxx::string_traits<>(&)` for
+// `asdf::timestamp`, which allows use of `pqxx::field::to<>(&)` and
+// `pqxx::field::as<>(&)`
+namespace pqxx
+{
+ template<> struct string_traits< asdf::timestamp >
+ {
+ using subject_type = asdf::timestamp;
+
+ static constexpr const char* name() noexcept {
+ return "asdf::timestamp";
+ }
+
+ static constexpr bool has_null() noexcept { return false; }
+
+ static bool is_null( const asdf::timestamp& ) { return false; }
+
+ [[noreturn]] static asdf::timestamp null()
+ {
+ internal::throw_null_conversion( name() );
+ }
+
+ static asdf::timestamp from_string( std::string_view &text )
+ {
+ asdf::timestamp ts;
+ if( !asdf::from_iso8601_str( std::string{ text } + "00", ts ) )
+ throw argument_error{
+ "Failed conversion to "
+ + static_cast< std::string >( name() )
+ + ": '"
+ + static_cast< std::string >( text )
+ + "'"
+ };
+
+ return ts;
+ }
+
+ static std::string to_string( const asdf::timestamp& ts )
+ {
+ return asdf::to_iso8601_str( ts );
+ }
+ };
+
+ template<> struct nullness<asdf::timestamp> {
+ static constexpr bool has_null = false;
+ static constexpr bool always_null = false;
+ static constexpr bool is_null(asdf::timestamp* t) noexcept {
+ return t == nullptr;
+ }
+ static constexpr asdf::timestamp *null() { return nullptr; }
+ };
+} \ No newline at end of file
diff --git a/include/142bot/iso_week.h b/include/142bot/iso_week.h
new file mode 100644
index 0000000..4a0a4a9
--- /dev/null
+++ b/include/142bot/iso_week.h
@@ -0,0 +1,1751 @@
+#ifndef ISO_WEEK_H
+#define ISO_WEEK_H
+
+// The MIT License (MIT)
+//
+// Copyright (c) 2015, 2016, 2017 Howard Hinnant
+//
+// 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.
+
+#include "date.h"
+
+#include <climits>
+
+namespace iso_week
+{
+
+// y/wn/wd
+// wn/wd/y
+// wd/wn/y
+
+using days = date::days;
+using weeks = date::weeks;
+using years = date::years;
+
+// time_point
+
+using sys_days = date::sys_days;
+using local_days = date::local_days;
+
+// types
+
+struct last_week
+{
+ explicit last_week() = default;
+};
+
+class weekday;
+class weeknum;
+class year;
+
+class year_weeknum;
+class year_lastweek;
+class weeknum_weekday;
+class lastweek_weekday;
+
+class year_weeknum_weekday;
+class year_lastweek_weekday;
+
+// date composition operators
+
+CONSTCD11 year_weeknum operator/(const year& y, const weeknum& wn) NOEXCEPT;
+CONSTCD11 year_weeknum operator/(const year& y, int wn) NOEXCEPT;
+
+CONSTCD11 year_lastweek operator/(const year& y, last_week wn) NOEXCEPT;
+
+CONSTCD11 weeknum_weekday operator/(const weeknum& wn, const weekday& wd) NOEXCEPT;
+CONSTCD11 weeknum_weekday operator/(const weeknum& wn, int wd) NOEXCEPT;
+CONSTCD11 weeknum_weekday operator/(const weekday& wd, const weeknum& wn) NOEXCEPT;
+CONSTCD11 weeknum_weekday operator/(const weekday& wd, int wn) NOEXCEPT;
+
+CONSTCD11 lastweek_weekday operator/(const last_week& wn, const weekday& wd) NOEXCEPT;
+CONSTCD11 lastweek_weekday operator/(const last_week& wn, int wd) NOEXCEPT;
+CONSTCD11 lastweek_weekday operator/(const weekday& wd, const last_week& wn) NOEXCEPT;
+
+CONSTCD11 year_weeknum_weekday operator/(const year_weeknum& ywn, const weekday& wd) NOEXCEPT;
+CONSTCD11 year_weeknum_weekday operator/(const year_weeknum& ywn, int wd) NOEXCEPT;
+CONSTCD11 year_weeknum_weekday operator/(const weeknum_weekday& wnwd, const year& y) NOEXCEPT;
+CONSTCD11 year_weeknum_weekday operator/(const weeknum_weekday& wnwd, int y) NOEXCEPT;
+
+CONSTCD11 year_lastweek_weekday operator/(const year_lastweek& ylw, const weekday& wd) NOEXCEPT;
+CONSTCD11 year_lastweek_weekday operator/(const year_lastweek& ylw, int wd) NOEXCEPT;
+
+CONSTCD11 year_lastweek_weekday operator/(const lastweek_weekday& lwwd, const year& y) NOEXCEPT;
+CONSTCD11 year_lastweek_weekday operator/(const lastweek_weekday& lwwd, int y) NOEXCEPT;
+
+// weekday
+
+class weekday
+{
+ unsigned char wd_;
+public:
+ explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT;
+ CONSTCD11 weekday(date::weekday wd) NOEXCEPT;
+ explicit weekday(int) = delete;
+ CONSTCD11 weekday(const sys_days& dp) NOEXCEPT;
+ CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT;
+
+ weekday& operator++() NOEXCEPT;
+ weekday operator++(int) NOEXCEPT;
+ weekday& operator--() NOEXCEPT;
+ weekday operator--(int) NOEXCEPT;
+
+ weekday& operator+=(const days& d) NOEXCEPT;
+ weekday& operator-=(const days& d) NOEXCEPT;
+
+ CONSTCD11 explicit operator unsigned() const NOEXCEPT;
+ CONSTCD11 operator date::weekday() const NOEXCEPT;
+ CONSTCD11 bool ok() const NOEXCEPT;
+
+private:
+ static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT;
+ static CONSTCD11 unsigned char to_iso_encoding(unsigned char) NOEXCEPT;
+ static CONSTCD11 unsigned from_iso_encoding(unsigned) 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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd);
+
+// year
+
+class year
+{
+ short y_;
+
+public:
+ explicit CONSTCD11 year(int y) NOEXCEPT;
+
+ year& operator++() NOEXCEPT;
+ year operator++(int) NOEXCEPT;
+ year& operator--() NOEXCEPT;
+ year operator--(int) NOEXCEPT;
+
+ year& operator+=(const years& y) NOEXCEPT;
+ year& operator-=(const years& y) NOEXCEPT;
+
+ CONSTCD11 explicit operator int() 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<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year& y);
+
+// weeknum
+
+class weeknum
+{
+ unsigned char wn_;
+
+public:
+ explicit CONSTCD11 weeknum(unsigned wn) NOEXCEPT;
+
+ weeknum& operator++() NOEXCEPT;
+ weeknum operator++(int) NOEXCEPT;
+ weeknum& operator--() NOEXCEPT;
+ weeknum operator--(int) NOEXCEPT;
+
+ weeknum& operator+=(const weeks& y) NOEXCEPT;
+ weeknum& operator-=(const weeks& y) NOEXCEPT;
+
+ CONSTCD11 explicit operator unsigned() const NOEXCEPT;
+ CONSTCD11 bool ok() const NOEXCEPT;
+};
+
+CONSTCD11 bool operator==(const weeknum& x, const weeknum& y) NOEXCEPT;
+CONSTCD11 bool operator!=(const weeknum& x, const weeknum& y) NOEXCEPT;
+CONSTCD11 bool operator< (const weeknum& x, const weeknum& y) NOEXCEPT;
+CONSTCD11 bool operator> (const weeknum& x, const weeknum& y) NOEXCEPT;
+CONSTCD11 bool operator<=(const weeknum& x, const weeknum& y) NOEXCEPT;
+CONSTCD11 bool operator>=(const weeknum& x, const weeknum& y) NOEXCEPT;
+
+CONSTCD11 weeknum operator+(const weeknum& x, const weeks& y) NOEXCEPT;
+CONSTCD11 weeknum operator+(const weeks& x, const weeknum& y) NOEXCEPT;
+CONSTCD11 weeknum operator-(const weeknum& x, const weeks& y) NOEXCEPT;
+CONSTCD11 weeks operator-(const weeknum& x, const weeknum& y) NOEXCEPT;
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const weeknum& wn);
+
+// year_weeknum
+
+class year_weeknum
+{
+ iso_week::year y_;
+ iso_week::weeknum wn_;
+
+public:
+ CONSTCD11 year_weeknum(const iso_week::year& y, const iso_week::weeknum& wn) NOEXCEPT;
+
+ CONSTCD11 iso_week::year year() const NOEXCEPT;
+ CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT;
+
+ year_weeknum& operator+=(const years& dy) NOEXCEPT;
+ year_weeknum& operator-=(const years& dy) NOEXCEPT;
+
+ CONSTCD11 bool ok() const NOEXCEPT;
+};
+
+CONSTCD11 bool operator==(const year_weeknum& x, const year_weeknum& y) NOEXCEPT;
+CONSTCD11 bool operator!=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT;
+CONSTCD11 bool operator< (const year_weeknum& x, const year_weeknum& y) NOEXCEPT;
+CONSTCD11 bool operator> (const year_weeknum& x, const year_weeknum& y) NOEXCEPT;
+CONSTCD11 bool operator<=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT;
+CONSTCD11 bool operator>=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT;
+
+CONSTCD11 year_weeknum operator+(const year_weeknum& ym, const years& dy) NOEXCEPT;
+CONSTCD11 year_weeknum operator+(const years& dy, const year_weeknum& ym) NOEXCEPT;
+CONSTCD11 year_weeknum operator-(const year_weeknum& ym, const years& dy) NOEXCEPT;
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year_weeknum& ym);
+
+// year_lastweek
+
+class year_lastweek
+{
+ iso_week::year y_;
+
+public:
+ CONSTCD11 explicit year_lastweek(const iso_week::year& y) NOEXCEPT;
+
+ CONSTCD11 iso_week::year year() const NOEXCEPT;
+ CONSTCD14 iso_week::weeknum weeknum() const NOEXCEPT;
+
+ year_lastweek& operator+=(const years& dy) NOEXCEPT;
+ year_lastweek& operator-=(const years& dy) NOEXCEPT;
+
+ CONSTCD11 bool ok() const NOEXCEPT;
+};
+
+CONSTCD11 bool operator==(const year_lastweek& x, const year_lastweek& y) NOEXCEPT;
+CONSTCD11 bool operator!=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT;
+CONSTCD11 bool operator< (const year_lastweek& x, const year_lastweek& y) NOEXCEPT;
+CONSTCD11 bool operator> (const year_lastweek& x, const year_lastweek& y) NOEXCEPT;
+CONSTCD11 bool operator<=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT;
+CONSTCD11 bool operator>=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT;
+
+CONSTCD11 year_lastweek operator+(const year_lastweek& ym, const years& dy) NOEXCEPT;
+CONSTCD11 year_lastweek operator+(const years& dy, const year_lastweek& ym) NOEXCEPT;
+CONSTCD11 year_lastweek operator-(const year_lastweek& ym, const years& dy) NOEXCEPT;
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year_lastweek& ym);
+
+// weeknum_weekday
+
+class weeknum_weekday
+{
+ iso_week::weeknum wn_;
+ iso_week::weekday wd_;
+
+public:
+ CONSTCD11 weeknum_weekday(const iso_week::weeknum& wn,
+ const iso_week::weekday& wd) NOEXCEPT;
+
+ CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT;
+ CONSTCD11 iso_week::weekday weekday() const NOEXCEPT;
+
+ CONSTCD14 bool ok() const NOEXCEPT;
+};
+
+CONSTCD11 bool operator==(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator!=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator< (const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator> (const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator<=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator>=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT;
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const weeknum_weekday& md);
+
+// lastweek_weekday
+
+class lastweek_weekday
+{
+ iso_week::weekday wd_;
+
+public:
+ CONSTCD11 explicit lastweek_weekday(const iso_week::weekday& wd) NOEXCEPT;
+
+ CONSTCD11 iso_week::weekday weekday() const NOEXCEPT;
+
+ CONSTCD14 bool ok() const NOEXCEPT;
+};
+
+CONSTCD11 bool operator==(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator!=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator< (const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator> (const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator<=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator>=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT;
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const lastweek_weekday& md);
+
+// year_lastweek_weekday
+
+class year_lastweek_weekday
+{
+ iso_week::year y_;
+ iso_week::weekday wd_;
+
+public:
+ CONSTCD11 year_lastweek_weekday(const iso_week::year& y,
+ const iso_week::weekday& wd) NOEXCEPT;
+
+ year_lastweek_weekday& operator+=(const years& y) NOEXCEPT;
+ year_lastweek_weekday& operator-=(const years& y) NOEXCEPT;
+
+ CONSTCD11 iso_week::year year() const NOEXCEPT;
+ CONSTCD14 iso_week::weeknum weeknum() const NOEXCEPT;
+ CONSTCD11 iso_week::weekday weekday() 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_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator!=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator< (const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator> (const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator<=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator>=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT;
+
+CONSTCD11 year_lastweek_weekday operator+(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT;
+CONSTCD11 year_lastweek_weekday operator+(const years& y, const year_lastweek_weekday& ywnwd) NOEXCEPT;
+CONSTCD11 year_lastweek_weekday operator-(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT;
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year_lastweek_weekday& ywnwd);
+
+// class year_weeknum_weekday
+
+class year_weeknum_weekday
+{
+ iso_week::year y_;
+ iso_week::weeknum wn_;
+ iso_week::weekday wd_;
+
+public:
+ CONSTCD11 year_weeknum_weekday(const iso_week::year& y, const iso_week::weeknum& wn,
+ const iso_week::weekday& wd) NOEXCEPT;
+ CONSTCD14 year_weeknum_weekday(const year_lastweek_weekday& ylwwd) NOEXCEPT;
+ CONSTCD14 year_weeknum_weekday(const sys_days& dp) NOEXCEPT;
+ CONSTCD14 explicit year_weeknum_weekday(const local_days& dp) NOEXCEPT;
+
+ year_weeknum_weekday& operator+=(const years& y) NOEXCEPT;
+ year_weeknum_weekday& operator-=(const years& y) NOEXCEPT;
+
+ CONSTCD11 iso_week::year year() const NOEXCEPT;
+ CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT;
+ CONSTCD11 iso_week::weekday weekday() const NOEXCEPT;
+
+ CONSTCD14 operator sys_days() const NOEXCEPT;
+ CONSTCD14 explicit operator local_days() const NOEXCEPT;
+ CONSTCD14 bool ok() const NOEXCEPT;
+
+private:
+ static CONSTCD14 year_weeknum_weekday from_days(days dp) NOEXCEPT;
+};
+
+CONSTCD11 bool operator==(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator!=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator< (const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator> (const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator<=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT;
+CONSTCD11 bool operator>=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT;
+
+CONSTCD11 year_weeknum_weekday operator+(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT;
+CONSTCD11 year_weeknum_weekday operator+(const years& y, const year_weeknum_weekday& ywnwd) NOEXCEPT;
+CONSTCD11 year_weeknum_weekday operator-(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT;
+
+template<class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year_weeknum_weekday& ywnwd);
+
+//----------------+
+// Implementation |
+//----------------+
+
+// weekday
+
+CONSTCD11
+inline
+unsigned char
+weekday::to_iso_encoding(unsigned char z) NOEXCEPT
+{
+ return z != 0 ? z : (unsigned char)7;
+}
+
+CONSTCD11
+inline
+unsigned
+weekday::from_iso_encoding(unsigned z) NOEXCEPT
+{
+ return z != 7 ? z : 0u;
+}
+
+CONSTCD11
+inline
+unsigned char
+weekday::weekday_from_days(int z) NOEXCEPT
+{
+ return to_iso_encoding(static_cast<unsigned char>(static_cast<unsigned>(
+ z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6)));
+}
+
+CONSTCD11
+inline
+weekday::weekday(unsigned wd) NOEXCEPT
+ : wd_(static_cast<decltype(wd_)>(wd))
+ {}
+
+CONSTCD11
+inline
+weekday::weekday(date::weekday wd) NOEXCEPT
+ : wd_(wd.iso_encoding())
+ {}
+
+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()))
+ {}
+
+inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 8) wd_ = 1; return *this;}
+inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;}
+inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 1) wd_ = 7; return *this;}
+inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;}
+
+inline
+weekday&
+weekday::operator+=(const days& d) NOEXCEPT
+{
+ *this = *this + d;
+ return *this;
+}
+
+inline
+weekday&
+weekday::operator-=(const days& d) NOEXCEPT
+{
+ *this = *this - d;
+ return *this;
+}
+
+CONSTCD11
+inline
+weekday::operator unsigned() const NOEXCEPT
+{
+ return wd_;
+}
+
+CONSTCD11
+inline
+weekday::operator date::weekday() const NOEXCEPT
+{
+ return date::weekday{from_iso_encoding(unsigned{wd_})};
+}
+
+CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return 1 <= wd_ && wd_ <= 7;}
+
+CONSTCD11
+inline
+bool
+operator==(const weekday& x, const weekday& y) NOEXCEPT
+{
+ return static_cast<unsigned>(x) == static_cast<unsigned>(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<unsigned>(x) - static_cast<unsigned>(y);
+ return days{diff <= 6 ? diff : diff + 7};
+}
+
+CONSTCD14
+inline
+weekday
+operator+(const weekday& x, const days& y) NOEXCEPT
+{
+ auto const wdu = static_cast<long long>(static_cast<unsigned>(x) - 1u) + y.count();
+ auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7;
+ return weekday{static_cast<unsigned>(wdu - wk * 7) + 1u};
+}
+
+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<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
+{
+ switch (static_cast<unsigned>(wd))
+ {
+ case 7:
+ 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<unsigned>(wd) << " is not a valid weekday";
+ break;
+ }
+ return os;
+}
+
+// year
+
+CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast<decltype(y_)>(y)) {}
+inline year& year::operator++() NOEXCEPT {++y_; return *this;}
+inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;}
+inline year& year::operator--() NOEXCEPT {--y_; return *this;}
+inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;}
+inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;}
+inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;}
+
+CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;}
+CONSTCD11 inline bool year::ok() const NOEXCEPT {return min() <= *this && *this <= max();}
+
+CONSTCD11
+inline
+year
+year::min() NOEXCEPT
+{
+ using std::chrono::seconds;
+ using std::chrono::minutes;
+ using std::chrono::hours;
+ using std::chrono::duration_cast;
+ static_assert(sizeof(seconds)*CHAR_BIT >= 41, "seconds may overflow");
+ static_assert(sizeof(hours)*CHAR_BIT >= 30, "hours may overflow");
+ return sizeof(minutes)*CHAR_BIT < 34 ?
+ year{1970} + duration_cast<years>(minutes::min()) :
+ year{std::numeric_limits<short>::min()};
+}
+
+CONSTCD11
+inline
+year
+year::max() NOEXCEPT
+{
+ using std::chrono::seconds;
+ using std::chrono::minutes;
+ using std::chrono::hours;
+ using std::chrono::duration_cast;
+ static_assert(sizeof(seconds)*CHAR_BIT >= 41, "seconds may overflow");
+ static_assert(sizeof(hours)*CHAR_BIT >= 30, "hours may overflow");
+ return sizeof(minutes)*CHAR_BIT < 34 ?
+ year{1969} + duration_cast<years>(minutes::max()) :
+ year{std::numeric_limits<short>::max()};
+}
+
+CONSTCD11
+inline
+bool
+operator==(const year& x, const year& y) NOEXCEPT
+{
+ return static_cast<int>(x) == static_cast<int>(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<int>(x) < static_cast<int>(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<int>(x) - static_cast<int>(y)};
+}
+
+CONSTCD11
+inline
+year
+operator+(const year& x, const years& y) NOEXCEPT
+{
+ return year{static_cast<int>(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<int>(x) - y.count()};
+}
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year& y)
+{
+ date::detail::save_ostream<CharT, Traits> _(os);
+ os.fill('0');
+ os.flags(std::ios::dec | std::ios::internal);
+ os.width(4 + (y < year{0}));
+ os << static_cast<int>(y);
+ return os;
+}
+
+#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
+inline namespace literals
+{
+
+CONSTCD11
+inline
+iso_week::year
+operator "" _y(unsigned long long y) NOEXCEPT
+{
+ return iso_week::year(static_cast<int>(y));
+}
+
+CONSTCD11
+inline
+iso_week::weeknum
+operator "" _w(unsigned long long wn) NOEXCEPT
+{
+ return iso_week::weeknum(static_cast<unsigned>(wn));
+}
+
+#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900)
+
+CONSTDATA iso_week::last_week last{};
+
+CONSTDATA iso_week::weekday sun{7u};
+CONSTDATA iso_week::weekday mon{1u};
+CONSTDATA iso_week::weekday tue{2u};
+CONSTDATA iso_week::weekday wed{3u};
+CONSTDATA iso_week::weekday thu{4u};
+CONSTDATA iso_week::weekday fri{5u};
+CONSTDATA iso_week::weekday sat{6u};
+
+#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
+} // inline namespace literals
+#endif
+
+// weeknum
+
+CONSTCD11
+inline
+weeknum::weeknum(unsigned wn) NOEXCEPT
+ : wn_(static_cast<decltype(wn_)>(wn))
+ {}
+
+inline weeknum& weeknum::operator++() NOEXCEPT {++wn_; return *this;}
+inline weeknum weeknum::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;}
+inline weeknum& weeknum::operator--() NOEXCEPT {--wn_; return *this;}
+inline weeknum weeknum::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;}
+
+inline
+weeknum&
+weeknum::operator+=(const weeks& y) NOEXCEPT
+{
+ *this = *this + y;
+ return *this;
+}
+
+inline
+weeknum&
+weeknum::operator-=(const weeks& y) NOEXCEPT
+{
+ *this = *this - y;
+ return *this;
+}
+
+CONSTCD11 inline weeknum::operator unsigned() const NOEXCEPT {return wn_;}
+CONSTCD11 inline bool weeknum::ok() const NOEXCEPT {return 1 <= wn_ && wn_ <= 53;}
+
+CONSTCD11
+inline
+bool
+operator==(const weeknum& x, const weeknum& y) NOEXCEPT
+{
+ return static_cast<unsigned>(x) == static_cast<unsigned>(y);
+}
+
+CONSTCD11
+inline
+bool
+operator!=(const weeknum& x, const weeknum& y) NOEXCEPT
+{
+ return !(x == y);
+}
+
+CONSTCD11
+inline
+bool
+operator<(const weeknum& x, const weeknum& y) NOEXCEPT
+{
+ return static_cast<unsigned>(x) < static_cast<unsigned>(y);
+}
+
+CONSTCD11
+inline
+bool
+operator>(const weeknum& x, const weeknum& y) NOEXCEPT
+{
+ return y < x;
+}
+
+CONSTCD11
+inline
+bool
+operator<=(const weeknum& x, const weeknum& y) NOEXCEPT
+{
+ return !(y < x);
+}
+
+CONSTCD11
+inline
+bool
+operator>=(const weeknum& x, const weeknum& y) NOEXCEPT
+{
+ return !(x < y);
+}
+
+CONSTCD11
+inline
+weeks
+operator-(const weeknum& x, const weeknum& y) NOEXCEPT
+{
+ return weeks{static_cast<weeks::rep>(static_cast<unsigned>(x)) -
+ static_cast<weeks::rep>(static_cast<unsigned>(y))};
+}
+
+CONSTCD11
+inline
+weeknum
+operator+(const weeknum& x, const weeks& y) NOEXCEPT
+{
+ return weeknum{static_cast<unsigned>(x) + static_cast<unsigned>(y.count())};
+}
+
+CONSTCD11
+inline
+weeknum
+operator+(const weeks& x, const weeknum& y) NOEXCEPT
+{
+ return y + x;
+}
+
+CONSTCD11
+inline
+weeknum
+operator-(const weeknum& x, const weeks& y) NOEXCEPT
+{
+ return x + -y;
+}
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const weeknum& wn)
+{
+ date::detail::save_ostream<CharT, Traits> _(os);
+ os << 'W';
+ os.fill('0');
+ os.flags(std::ios::dec | std::ios::right);
+ os.width(2);
+ os << static_cast<unsigned>(wn);
+ return os;
+}
+
+// year_weeknum
+
+CONSTCD11
+inline
+year_weeknum::year_weeknum(const iso_week::year& y, const iso_week::weeknum& wn) NOEXCEPT
+ : y_(y)
+ , wn_(wn)
+ {}
+
+CONSTCD11 inline year year_weeknum::year() const NOEXCEPT {return y_;}
+CONSTCD11 inline weeknum year_weeknum::weeknum() const NOEXCEPT {return wn_;}
+CONSTCD11 inline bool year_weeknum::ok() const NOEXCEPT
+{
+ return y_.ok() && 1u <= static_cast<unsigned>(wn_) && wn_ <= (y_/last).weeknum();
+}
+
+inline
+year_weeknum&
+year_weeknum::operator+=(const years& dy) NOEXCEPT
+{
+ *this = *this + dy;
+ return *this;
+}
+
+inline
+year_weeknum&
+year_weeknum::operator-=(const years& dy) NOEXCEPT
+{
+ *this = *this - dy;
+ return *this;
+}
+
+CONSTCD11
+inline
+bool
+operator==(const year_weeknum& x, const year_weeknum& y) NOEXCEPT
+{
+ return x.year() == y.year() && x.weeknum() == y.weeknum();
+}
+
+CONSTCD11
+inline
+bool
+operator!=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT
+{
+ return !(x == y);
+}
+
+CONSTCD11
+inline
+bool
+operator<(const year_weeknum& x, const year_weeknum& y) NOEXCEPT
+{
+ return x.year() < y.year() ? true
+ : (x.year() > y.year() ? false
+ : (x.weeknum() < y.weeknum()));
+}
+
+CONSTCD11
+inline
+bool
+operator>(const year_weeknum& x, const year_weeknum& y) NOEXCEPT
+{
+ return y < x;
+}
+
+CONSTCD11
+inline
+bool
+operator<=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT
+{
+ return !(y < x);
+}
+
+CONSTCD11
+inline
+bool
+operator>=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT
+{
+ return !(x < y);
+}
+
+CONSTCD11
+inline
+year_weeknum
+operator+(const year_weeknum& ym, const years& dy) NOEXCEPT
+{
+ return (ym.year() + dy) / ym.weeknum();
+}
+
+CONSTCD11
+inline
+year_weeknum
+operator+(const years& dy, const year_weeknum& ym) NOEXCEPT
+{
+ return ym + dy;
+}
+
+CONSTCD11
+inline
+year_weeknum
+operator-(const year_weeknum& ym, const years& dy) NOEXCEPT
+{
+ return ym + -dy;
+}
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year_weeknum& ywn)
+{
+ return os << ywn.year() << '-' << ywn.weeknum();
+}
+
+
+// year_lastweek
+
+CONSTCD11
+inline
+year_lastweek::year_lastweek(const iso_week::year& y) NOEXCEPT
+ : y_(y)
+ {}
+
+CONSTCD11 inline year year_lastweek::year() const NOEXCEPT {return y_;}
+
+CONSTCD14
+inline
+weeknum
+year_lastweek::weeknum() const NOEXCEPT
+{
+ const auto y = date::year{static_cast<int>(y_)};
+ const auto s0 = sys_days((y-years{1})/12/date::thu[date::last]);
+ const auto s1 = sys_days(y/12/date::thu[date::last]);
+ return iso_week::weeknum(static_cast<unsigned>(date::trunc<weeks>(s1-s0).count()));
+}
+
+CONSTCD11 inline bool year_lastweek::ok() const NOEXCEPT {return y_.ok();}
+
+inline
+year_lastweek&
+year_lastweek::operator+=(const years& dy) NOEXCEPT
+{
+ *this = *this + dy;
+ return *this;
+}
+
+inline
+year_lastweek&
+year_lastweek::operator-=(const years& dy) NOEXCEPT
+{
+ *this = *this - dy;
+ return *this;
+}
+
+CONSTCD11
+inline
+bool
+operator==(const year_lastweek& x, const year_lastweek& y) NOEXCEPT
+{
+ return x.year() == y.year();
+}
+
+CONSTCD11
+inline
+bool
+operator!=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT
+{
+ return !(x == y);
+}
+
+CONSTCD11
+inline
+bool
+operator<(const year_lastweek& x, const year_lastweek& y) NOEXCEPT
+{
+ return x.year() < y.year();
+}
+
+CONSTCD11
+inline
+bool
+operator>(const year_lastweek& x, const year_lastweek& y) NOEXCEPT
+{
+ return y < x;
+}
+
+CONSTCD11
+inline
+bool
+operator<=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT
+{
+ return !(y < x);
+}
+
+CONSTCD11
+inline
+bool
+operator>=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT
+{
+ return !(x < y);
+}
+
+CONSTCD11
+inline
+year_lastweek
+operator+(const year_lastweek& ym, const years& dy) NOEXCEPT
+{
+ return year_lastweek{ym.year() + dy};
+}
+
+CONSTCD11
+inline
+year_lastweek
+operator+(const years& dy, const year_lastweek& ym) NOEXCEPT
+{
+ return ym + dy;
+}
+
+CONSTCD11
+inline
+year_lastweek
+operator-(const year_lastweek& ym, const years& dy) NOEXCEPT
+{
+ return ym + -dy;
+}
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year_lastweek& ywn)
+{
+ return os << ywn.year() << "-W last";
+}
+
+// weeknum_weekday
+
+CONSTCD11
+inline
+weeknum_weekday::weeknum_weekday(const iso_week::weeknum& wn,
+ const iso_week::weekday& wd) NOEXCEPT
+ : wn_(wn)
+ , wd_(wd)
+ {}
+
+CONSTCD11 inline weeknum weeknum_weekday::weeknum() const NOEXCEPT {return wn_;}
+CONSTCD11 inline weekday weeknum_weekday::weekday() const NOEXCEPT {return wd_;}
+
+CONSTCD14
+inline
+bool
+weeknum_weekday::ok() const NOEXCEPT
+{
+ return wn_.ok() && wd_.ok();
+}
+
+CONSTCD11
+inline
+bool
+operator==(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT
+{
+ return x.weeknum() == y.weeknum() && x.weekday() == y.weekday();
+}
+
+CONSTCD11
+inline
+bool
+operator!=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT
+{
+ return !(x == y);
+}
+
+CONSTCD11
+inline
+bool
+operator<(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT
+{
+ return x.weeknum() < y.weeknum() ? true
+ : (x.weeknum() > y.weeknum() ? false
+ : (static_cast<unsigned>(x.weekday()) < static_cast<unsigned>(y.weekday())));
+}
+
+CONSTCD11
+inline
+bool
+operator>(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT
+{
+ return y < x;
+}
+
+CONSTCD11
+inline
+bool
+operator<=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT
+{
+ return !(y < x);
+}
+
+CONSTCD11
+inline
+bool
+operator>=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT
+{
+ return !(x < y);
+}
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const weeknum_weekday& md)
+{
+ return os << md.weeknum() << '-' << md.weekday();
+}
+
+// lastweek_weekday
+
+CONSTCD11
+inline
+lastweek_weekday::lastweek_weekday(const iso_week::weekday& wd) NOEXCEPT
+ : wd_(wd)
+ {}
+
+CONSTCD11 inline weekday lastweek_weekday::weekday() const NOEXCEPT {return wd_;}
+
+CONSTCD14
+inline
+bool
+lastweek_weekday::ok() const NOEXCEPT
+{
+ return wd_.ok();
+}
+
+CONSTCD11
+inline
+bool
+operator==(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT
+{
+ return x.weekday() == y.weekday();
+}
+
+CONSTCD11
+inline
+bool
+operator!=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT
+{
+ return !(x == y);
+}
+
+CONSTCD11
+inline
+bool
+operator<(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT
+{
+ return static_cast<unsigned>(x.weekday()) < static_cast<unsigned>(y.weekday());
+}
+
+CONSTCD11
+inline
+bool
+operator>(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT
+{
+ return y < x;
+}
+
+CONSTCD11
+inline
+bool
+operator<=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT
+{
+ return !(y < x);
+}
+
+CONSTCD11
+inline
+bool
+operator>=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT
+{
+ return !(x < y);
+}
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const lastweek_weekday& md)
+{
+ return os << "W last-" << md.weekday();
+}
+
+// year_lastweek_weekday
+
+CONSTCD11
+inline
+year_lastweek_weekday::year_lastweek_weekday(const iso_week::year& y,
+ const iso_week::weekday& wd) NOEXCEPT
+ : y_(y)
+ , wd_(wd)
+ {}
+
+inline
+year_lastweek_weekday&
+year_lastweek_weekday::operator+=(const years& y) NOEXCEPT
+{
+ *this = *this + y;
+ return *this;
+}
+
+inline
+year_lastweek_weekday&
+year_lastweek_weekday::operator-=(const years& y) NOEXCEPT
+{
+ *this = *this - y;
+ return *this;
+}
+
+CONSTCD11 inline year year_lastweek_weekday::year() const NOEXCEPT {return y_;}
+
+CONSTCD14
+inline
+weeknum
+year_lastweek_weekday::weeknum() const NOEXCEPT
+{
+ return (y_ / last).weeknum();
+}
+
+CONSTCD11 inline weekday year_lastweek_weekday::weekday() const NOEXCEPT {return wd_;}
+
+CONSTCD14
+inline
+year_lastweek_weekday::operator sys_days() const NOEXCEPT
+{
+ return sys_days(date::year{static_cast<int>(y_)}/date::dec/date::thu[date::last])
+ + (sun - thu) - (sun - wd_);
+}
+
+CONSTCD14
+inline
+year_lastweek_weekday::operator local_days() const NOEXCEPT
+{
+ return local_days(date::year{static_cast<int>(y_)}/date::dec/date::thu[date::last])
+ + (sun - thu) - (sun - wd_);
+}
+
+CONSTCD11
+inline
+bool
+year_lastweek_weekday::ok() const NOEXCEPT
+{
+ return y_.ok() && wd_.ok();
+}
+
+CONSTCD11
+inline
+bool
+operator==(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT
+{
+ return x.year() == y.year() && x.weekday() == y.weekday();
+}
+
+CONSTCD11
+inline
+bool
+operator!=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT
+{
+ return !(x == y);
+}
+
+CONSTCD11
+inline
+bool
+operator<(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT
+{
+ return x.year() < y.year() ? true
+ : (x.year() > y.year() ? false
+ : (static_cast<unsigned>(x.weekday()) < static_cast<unsigned>(y.weekday())));
+}
+
+CONSTCD11
+inline
+bool
+operator>(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT
+{
+ return y < x;
+}
+
+CONSTCD11
+inline
+bool
+operator<=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT
+{
+ return !(y < x);
+}
+
+CONSTCD11
+inline
+bool
+operator>=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT
+{
+ return !(x < y);
+}
+
+CONSTCD11
+inline
+year_lastweek_weekday
+operator+(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT
+{
+ return (ywnwd.year() + y) / last / ywnwd.weekday();
+}
+
+CONSTCD11
+inline
+year_lastweek_weekday
+operator+(const years& y, const year_lastweek_weekday& ywnwd) NOEXCEPT
+{
+ return ywnwd + y;
+}
+
+CONSTCD11
+inline
+year_lastweek_weekday
+operator-(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT
+{
+ return ywnwd + -y;
+}
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year_lastweek_weekday& ywnwd)
+{
+ return os << ywnwd.year() << "-W last-" << ywnwd.weekday();
+}
+
+// year_weeknum_weekday
+
+CONSTCD11
+inline
+year_weeknum_weekday::year_weeknum_weekday(const iso_week::year& y,
+ const iso_week::weeknum& wn,
+ const iso_week::weekday& wd) NOEXCEPT
+ : y_(y)
+ , wn_(wn)
+ , wd_(wd)
+ {}
+
+CONSTCD14
+inline
+year_weeknum_weekday::year_weeknum_weekday(const year_lastweek_weekday& ylwwd) NOEXCEPT
+ : y_(ylwwd.year())
+ , wn_(ylwwd.weeknum())
+ , wd_(ylwwd.weekday())
+ {}
+
+CONSTCD14
+inline
+year_weeknum_weekday::year_weeknum_weekday(const sys_days& dp) NOEXCEPT
+ : year_weeknum_weekday(from_days(dp.time_since_epoch()))
+ {}
+
+CONSTCD14
+inline
+year_weeknum_weekday::year_weeknum_weekday(const local_days& dp) NOEXCEPT
+ : year_weeknum_weekday(from_days(dp.time_since_epoch()))
+ {}
+
+inline
+year_weeknum_weekday&
+year_weeknum_weekday::operator+=(const years& y) NOEXCEPT
+{
+ *this = *this + y;
+ return *this;
+}
+
+inline
+year_weeknum_weekday&
+year_weeknum_weekday::operator-=(const years& y) NOEXCEPT
+{
+ *this = *this - y;
+ return *this;
+}
+
+CONSTCD11 inline year year_weeknum_weekday::year() const NOEXCEPT {return y_;}
+CONSTCD11 inline weeknum year_weeknum_weekday::weeknum() const NOEXCEPT {return wn_;}
+CONSTCD11 inline weekday year_weeknum_weekday::weekday() const NOEXCEPT {return wd_;}
+
+CONSTCD14
+inline
+year_weeknum_weekday::operator sys_days() const NOEXCEPT
+{
+ return sys_days(date::year{static_cast<int>(y_)-1}/date::dec/date::thu[date::last])
+ + (date::mon - date::thu) + weeks{static_cast<unsigned>(wn_)-1} + (wd_ - mon);
+}
+
+CONSTCD14
+inline
+year_weeknum_weekday::operator local_days() const NOEXCEPT
+{
+ return local_days(date::year{static_cast<int>(y_)-1}/date::dec/date::thu[date::last])
+ + (date::mon - date::thu) + weeks{static_cast<unsigned>(wn_)-1} + (wd_ - mon);
+}
+
+CONSTCD14
+inline
+bool
+year_weeknum_weekday::ok() const NOEXCEPT
+{
+ return y_.ok() && wd_.ok() && iso_week::weeknum{1u} <= wn_ && wn_ <= year_lastweek{y_}.weeknum();
+}
+
+CONSTCD14
+inline
+year_weeknum_weekday
+year_weeknum_weekday::from_days(days d) NOEXCEPT
+{
+ const auto dp = sys_days{d};
+ const auto wd = iso_week::weekday{dp};
+ auto y = date::year_month_day{dp + days{3}}.year();
+ auto start = sys_days((y - date::years{1})/date::dec/date::thu[date::last]) + (mon-thu);
+ if (dp < start)
+ {
+ --y;
+ start = sys_days((y - date::years{1})/date::dec/date::thu[date::last]) + (mon-thu);
+ }
+ const auto wn = iso_week::weeknum(
+ static_cast<unsigned>(date::trunc<weeks>(dp - start).count() + 1));
+ return {iso_week::year(static_cast<int>(y)), wn, wd};
+}
+
+CONSTCD11
+inline
+bool
+operator==(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT
+{
+ return x.year() == y.year() && x.weeknum() == y.weeknum() && x.weekday() == y.weekday();
+}
+
+CONSTCD11
+inline
+bool
+operator!=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT
+{
+ return !(x == y);
+}
+
+CONSTCD11
+inline
+bool
+operator<(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT
+{
+ return x.year() < y.year() ? true
+ : (x.year() > y.year() ? false
+ : (x.weeknum() < y.weeknum() ? true
+ : (x.weeknum() > y.weeknum() ? false
+ : (static_cast<unsigned>(x.weekday()) < static_cast<unsigned>(y.weekday())))));
+}
+
+CONSTCD11
+inline
+bool
+operator>(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT
+{
+ return y < x;
+}
+
+CONSTCD11
+inline
+bool
+operator<=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT
+{
+ return !(y < x);
+}
+
+CONSTCD11
+inline
+bool
+operator>=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT
+{
+ return !(x < y);
+}
+
+CONSTCD11
+inline
+year_weeknum_weekday
+operator+(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT
+{
+ return (ywnwd.year() + y) / ywnwd.weeknum() / ywnwd.weekday();
+}
+
+CONSTCD11
+inline
+year_weeknum_weekday
+operator+(const years& y, const year_weeknum_weekday& ywnwd) NOEXCEPT
+{
+ return ywnwd + y;
+}
+
+CONSTCD11
+inline
+year_weeknum_weekday
+operator-(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT
+{
+ return ywnwd + -y;
+}
+
+template<class CharT, class Traits>
+inline
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os, const year_weeknum_weekday& ywnwd)
+{
+ return os << ywnwd.year() << '-' << ywnwd.weeknum() << '-' << ywnwd.weekday();
+}
+
+// date composition operators
+
+CONSTCD11
+inline
+year_weeknum
+operator/(const year& y, const weeknum& wn) NOEXCEPT
+{
+ return {y, wn};
+}
+
+CONSTCD11
+inline
+year_weeknum
+operator/(const year& y, int wn) NOEXCEPT
+{
+ return y/weeknum(static_cast<unsigned>(wn));
+}
+
+CONSTCD11
+inline
+year_lastweek
+operator/(const year& y, last_week) NOEXCEPT
+{
+ return year_lastweek{y};
+}
+
+CONSTCD11
+inline
+weeknum_weekday
+operator/(const weeknum& wn, const weekday& wd) NOEXCEPT
+{
+ return {wn, wd};
+}
+
+CONSTCD11
+inline
+weeknum_weekday
+operator/(const weeknum& wn, int wd) NOEXCEPT
+{
+ return wn/weekday{static_cast<unsigned>(wd)};
+}
+
+CONSTCD11
+inline
+weeknum_weekday
+operator/(const weekday& wd, const weeknum& wn) NOEXCEPT
+{
+ return wn/wd;
+}
+
+CONSTCD11
+inline
+weeknum_weekday
+operator/(const weekday& wd, int wn) NOEXCEPT
+{
+ return weeknum{static_cast<unsigned>(wn)}/wd;
+}
+
+CONSTCD11
+inline
+lastweek_weekday
+operator/(const last_week&, const weekday& wd) NOEXCEPT
+{
+ return lastweek_weekday{wd};
+}
+
+CONSTCD11
+inline
+lastweek_weekday
+operator/(const last_week& wn, int wd) NOEXCEPT
+{
+ return wn / weekday{static_cast<unsigned>(wd)};
+}
+
+CONSTCD11
+inline
+lastweek_weekday
+operator/(const weekday& wd, const last_week& wn) NOEXCEPT
+{
+ return wn / wd;
+}
+
+CONSTCD11
+inline
+year_weeknum_weekday
+operator/(const year_weeknum& ywn, const weekday& wd) NOEXCEPT
+{
+ return {ywn.year(), ywn.weeknum(), wd};
+}
+
+CONSTCD11
+inline
+year_weeknum_weekday
+operator/(const year_weeknum& ywn, int wd) NOEXCEPT
+{
+ return ywn / weekday(static_cast<unsigned>(wd));
+}
+
+CONSTCD11
+inline
+year_weeknum_weekday
+operator/(const weeknum_weekday& wnwd, const year& y) NOEXCEPT
+{
+ return {y, wnwd.weeknum(), wnwd.weekday()};
+}
+
+CONSTCD11
+inline
+year_weeknum_weekday
+operator/(const weeknum_weekday& wnwd, int y) NOEXCEPT
+{
+ return wnwd / year{y};
+}
+
+CONSTCD11
+inline
+year_lastweek_weekday
+operator/(const year_lastweek& ylw, const weekday& wd) NOEXCEPT
+{
+ return {ylw.year(), wd};
+}
+
+CONSTCD11
+inline
+year_lastweek_weekday
+operator/(const year_lastweek& ylw, int wd) NOEXCEPT
+{
+ return ylw / weekday(static_cast<unsigned>(wd));
+}
+
+CONSTCD11
+inline
+year_lastweek_weekday
+operator/(const lastweek_weekday& lwwd, const year& y) NOEXCEPT
+{
+ return {y, lwwd.weekday()};
+}
+
+CONSTCD11
+inline
+year_lastweek_weekday
+operator/(const lastweek_weekday& lwwd, int y) NOEXCEPT
+{
+ return lwwd / year{y};
+}
+
+} // namespace iso_week
+
+#endif // ISO_WEEK_H
diff --git a/include/142bot/modules.hpp b/include/142bot/modules.hpp
index 0d48a56..37f4401 100644
--- a/include/142bot/modules.hpp
+++ b/include/142bot/modules.hpp
@@ -203,6 +203,9 @@ public:
virtual bool OnWebhooksUpdate(const dpp::webhooks_update_t &obj);
virtual bool OnCommand(const dpp::message_create_t &message, const std::string &command, const std::vector<std::string>& params);
void EmbedSimple(const std::string &message, int64_t channelID);
+ void EmbedError(int64_t channelId, std::exception &e);
+ void EmbedError(const std::string &message, int64_t channelId);
+ void EmbedSuccess(const std::string &message, int64_t channelId);
};
/* A macro that lets us simply define the entrypoint of a module by name */
diff --git a/modules/spotify/spotify.cpp b/modules/spotify/spotify.cpp
index 93b66ac..8f44f2e 100644
--- a/modules/spotify/spotify.cpp
+++ b/modules/spotify/spotify.cpp
@@ -16,19 +16,29 @@
* =====================================================================================
*/
#include <pcre.h>
+#include <regex>
#include <stdlib.h>
#include "cpr/cpr.h"
#include <142bot/modules.hpp>
+#include <142bot/util.hpp>
+#include <142bot/db.hpp>
#include <fmt/format.h>
+#include <chrono>
+
+using json = nlohmann::json;
class SpotifyModule: public Module {
std::string spotifyRegex;
+ std::string defaultSpotifyAccount;
+ std::string spotifyBaseUrl;
public:
SpotifyModule(Bot* creator, ModuleLoader* ml) : Module(creator, ml) {
ml->attach({I_OnMessage, I_OnCommand}, this);
this->spotifyRegex = "^https:\/\/open.spotify.com\/track\/([a-zA-Z0-9]+)(.*)$";
+ this->defaultSpotifyAccount = "1";
+ this->spotifyBaseUrl = "https://api.spotify.com/v1";
}
virtual std::string version() {
@@ -39,37 +49,174 @@ public:
return "Manage spotify queues for 142";
}
+ void refreshSpotify(std::string refreshToken, pqxx::work &tx) {
+ bot->core->log(dpp::ll_debug, "Attempting to refresh spotify token...");
+ cpr::Response r = cpr::Post(cpr::Url("https://accounts.spotify.com/api/token"),
+ cpr::Authentication{bot->cfg["spotify"]["id"], bot->cfg["spotify"]["secret"], cpr::AuthMode::BASIC}, cpr::Payload{{"grant_type", "refresh_token"}, {"refresh_token", refreshToken}});
+
+ bot->core->log(dpp::ll_trace, "Made request");
+
+ if (r.status_code != 200) {
+ bot->core->log(dpp::ll_error, r.text);
+ throw std::exception();
+ }
+ bot->core->log(dpp::ll_trace, "Request successful");
+ printf("%s\n", r.text.c_str());
+ auto tmp = json::parse(r.text);
+ bot->core->log(dpp::ll_trace, "Parsed JSON");
+
+ uint64_t expires = tmp["expires_in"].get<uint64_t>();
+
+ asdf::timestamp parsed_expires = asdf::from_unix_time(time(0) + expires);
+ bot->core->log(dpp::ll_trace, "Got expires_in");
+ std::string access = tmp["access_token"].get<std::string>();
+ bot->core->log(dpp::ll_trace, "Got access token");
+ tx.exec_params("UPDATE spotify SET spotify_token=$1, spotify_token_expires=$2 WHERE id=$3", access, parsed_expires, this->defaultSpotifyAccount);
+ bot->core->log(dpp::ll_trace, "Updated DB");
+ tx.commit();
+ bot->core->log(dpp::ll_debug, "Done refreshing spotify token");
+ }
+
+ // Obtains the (refreshed) token for the default spotify account
+ std::string get_spotify_token() {
+ bot->core->log(dpp::ll_debug, "Attempting to retrieve spotify token.");
+ std::string token;
+ pqxx::work tx(bot->conn);
+ try {
+ bot->core->log(dpp::ll_debug, fmt::format("Default spotify account: {}", this->defaultSpotifyAccount));
+ auto res = tx.exec_params1("SELECT spotify_username,spotify_token,spotify_token_expires,spotify_refresh_token FROM spotify WHERE id=$1", atoi(this->defaultSpotifyAccount.c_str()));
+ bot->core->log(dpp::ll_trace, "Retrieved from DB.");
+
+ auto ts = res[2].as<asdf::timestamp>();
+
+ if (ts < std::chrono::system_clock::now()) {
+ refreshSpotify(res[3].as<std::string>(), tx);
+ res = tx.exec_params1("SELECT spotify_username, spotify_token FROM spotify WHERE id=$1", atoi(this->defaultSpotifyAccount.c_str()));
+ bot->core->log(dpp::ll_trace, "Retrieved from database *again*");
+ }
+
+ token = res[1].as<std::string>();
+ tx.commit();
+ } catch (std::exception &e) {
+ std::string error_msg = "Error getting spotify token: " + *e.what();
+ bot->core->log(dpp::ll_error, e.what());
+ sentry_value_t event = sentry_value_new_event();
+ sentry_value_t exc = sentry_value_new_exception("Exception", "Spotify SQL error");
+ sentry_value_set_stacktrace(exc, NULL, 5);
+ sentry_event_add_exception(event, exc);
+ sentry_capture_event(event);
+ tx.abort();
+ return nullptr;
+ }
+ bot->core->log(dpp::ll_debug, "Done retrieving spotify token.");
+
+ return token;
+ }
+
+ /**
+ * Performs a GET request to the Spotify API
+ */
+ json spotify_get(const std::string route) {
+ bot->core->log(dpp::ll_debug, fmt::format("Making GET request to {}", route));
+
+ std::string token = get_spotify_token();
+ bot->core->log(dpp::ll_trace, "Obtained token.");
+
+ cpr::Response r = cpr::Get(cpr::Url{fmt::format("{}/{}", this->spotifyBaseUrl, route).c_str()},
+ cpr::Bearer{token});
+
+ bot->core->log(dpp::ll_trace, fmt::format("Made request. Code: {}", r.status_code));
+
+ if (r.status_code != 200) {
+ bot->core->log(dpp::ll_error, fmt::format("Spotify API Error: {}", r.text));
+ throw std::exception();
+ }
+
+ return json::parse(r.text);
+ }
+
virtual bool OnMessage(const dpp::message_create_t &message, const std::string& clean_message, bool mentioned, const std::vector<std::string> & mentions) {
sentry_set_tag("module", "spotify");
bot->core->log(dpp::ll_debug, "Got message event");
- const char* pcre_error;
- int pcre_error_ofs;
+ std::regex re("^https:\/\/open.spotify.com\/track\/([a-zA-Z0-9]+)(.*)$");
- auto comp = pcre_compile("^https:\/\/open.spotify.com\/track\/([a-zA-Z0-9]+)(.*)$", PCRE_CASELESS, &pcre_error, &pcre_error_ofs, NULL);
- if (!comp) {
- bot->core->log(dpp::ll_error, pcre_error);
- }
+ std::smatch match;
+
+ if (std::regex_search(clean_message, match, re) == true) {
+ try {
+ json res = spotify_get("tracks/" + match.str(1));
- int matcharr[90];
- int matchcount = pcre_exec(comp, NULL, clean_message.c_str(), clean_message.length(), 0, 0, matcharr, 90);
+ dpp::embed embed = dpp::embed()
+ .set_title(res["name"])
+ .set_author(res["artists"][0]["name"], "", "")
+ .set_image(res["album"]["images"][0]["url"]);
- if (matchcount > 0) {
- // We found a spotify URL!
- EmbedSimple("Found a spotify URL", message.msg.channel_id);
- } else {
- if (clean_message.starts_with(bot->prefix)) {
-
+ bot->core->message_create(dpp::message(message.msg.channel_id, embed).set_reference(message.msg.id));
+ return true;
+ } catch (std::exception &e) {
+ EmbedError(message.msg.channel_id, e);
+ return false;
}
- }
+ }
return true;
}
- virtual bool OnCommand(const dpp::message_create_t &message, const std::string &command, std::vector<std::string>& params) {
- bot->core->log(dpp::ll_debug, fmt::format("spotify Got command: {}", command));
- printf("Am i getting called?????\n");
- EmbedSimple("Got your command!", message.msg.channel_id);
+ bool OnCommand(const dpp::message_create_t &message, const std::string &command, const std::vector<std::string>& params) {
+
+ if (command == "spotify") {
+ // We can process subcommands!
+
+ if (params.size() <= 1) {
+ return true; // we don't have enough to process
+ }
+
+ std::string subcommand = lowercase(params[1]);
+
+ if (subcommand == "accounts") {
+ pqxx::work tx(bot->conn);
+
+ auto res = tx.exec("SELECT id, spotify_username FROM spotify");
+ tx.commit();
+ dpp::embed embed = dpp::embed().
+ set_color(dpp::colors::green)
+ .set_title("Spotify Accounts")
+ .set_description("List of Spotify accounts. Username is field ID and ID is field content.\n\nCurrent default account ID: " + this->defaultSpotifyAccount);
+
+ for (int i = 0; i < res.size(); i++) {
+ embed.add_field(res[i][1].c_str(), res[i][0].c_str(), true);
+ }
+
+
+ bot->core->message_create(dpp::message(message.msg.channel_id, embed).set_reference(message.msg.id));
+ return true;
+ } else if (subcommand == "default") {
+ pqxx::work tx(bot->conn);
+
+ try {
+ auto res = tx.exec_params1("SELECT id FROM spotify WHERE id=$1", params[2]);
+
+ this->defaultSpotifyAccount = params[2];
+
+ tx.commit();
+ } catch (std::exception &e) {
+ bot->core->log(dpp::ll_error, e.what());
+ sentry_value_t event = sentry_value_new_event();
+ sentry_value_t exc = sentry_value_new_exception("Exception", "Spotify SQL error");
+ sentry_value_set_stacktrace(exc, NULL, 5);
+ sentry_event_add_exception(event, exc);
+ sentry_capture_event(event);
+ tx.abort();
+ EmbedError("Invalid ID", message.msg.channel_id);
+ return false;
+ }
+
+ EmbedSuccess("Updated default account.", message.msg.channel_id);
+ } else {
+ EmbedError("Unknown Command", message.msg.channel_id);
+ }
+ }
return true;
}
diff --git a/src/bot.cpp b/src/bot.cpp
index d5f21de..2bc6a6b 100644
--- a/src/bot.cpp
+++ b/src/bot.cpp
@@ -150,7 +150,7 @@ void Bot::onMessage(const dpp::message_create_t &message) {
result.push_back(std::string(begin, str));
} while (0 != *str++);
core->log(dpp::ll_debug, "Attempting to call FOREACH_MOD");
- FOREACH_MOD(I_OnCommand,OnCommand(message, result[0], result));
+ FOREACH_MOD(I_OnCommand,OnCommand(message, lowercase(result[0].erase(0,1)), result));
} else {
/* Call modules */
diff --git a/src/db.cpp b/src/db.cpp
index cea15c0..73a7f02 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -81,3 +81,68 @@ namespace db {
}
}
+
+#include <142bot/date.h>
+
+#include <142bot/iso_week.h>
+#include <iomanip>
+#include <sstream>
+#include <stdexcept> // std::invalid_argument
+
+namespace asdf
+{
+ timestamp from_iso8601_str( const std::string& s )
+ {
+ timestamp ts;
+ if( !from_iso8601_str( s, ts ) )
+ throw std::invalid_argument{
+ "failed to parse "
+ + s
+ + " as an ISO 8601 timestamp"
+ };
+ return ts;
+ }
+
+ bool from_iso8601_str( const std::string& s, timestamp& ts )
+ {
+ std::istringstream stream{ s };
+ stream >> date::parse( "%F %T%z", ts );
+ return !stream.fail();
+ }
+
+ std::string to_iso8601_str( const timestamp& ts )
+ {
+ return date::format( "%F %T%z", ts );
+ }
+
+ std::string to_http_ts_str( const timestamp& ts )
+ {
+ std::stringstream weekday_abbreviation;
+ weekday_abbreviation << static_cast< iso_week::year_weeknum_weekday >(
+ std::chrono::time_point_cast< date::days >( ts )
+ ).weekday();
+
+ return (
+ weekday_abbreviation.str()
+ // timestamps serialize to UTC/GMT by default
+ + date::format(
+ " %d-%m-%Y %H:%M:%S GMT",
+ std::chrono::time_point_cast< std::chrono::seconds >( ts )
+ )
+ );
+ }
+
+ timestamp from_unix_time( unsigned int unix_time )
+ {
+ return timestamp{ std::chrono::duration_cast<
+ std::chrono::microseconds
+ >( std::chrono::seconds{ unix_time } ) };
+ }
+
+ unsigned int to_unix_time( const timestamp& ts )
+ {
+ return std::chrono::duration_cast<
+ std::chrono::seconds
+ >( ts.time_since_epoch() ).count();
+ }
+} \ No newline at end of file
diff --git a/src/modules.cpp b/src/modules.cpp
index 4b636f9..e784c0c 100644
--- a/src/modules.cpp
+++ b/src/modules.cpp
@@ -470,4 +470,39 @@ void Module::EmbedSimple(const std::string &message, int64_t channelID)
} else {
bot->core->log(dpp::ll_error, fmt::format("Invalid channel {} passed to EmbedSimple", channelID));
}
+}
+
+void Module::EmbedError(int64_t channelId, std::exception &e) {
+ std::string msg = "Got an error running that command!\n\n```";
+ msg.append(e.what());
+ msg.append("\n```");
+ dpp::embed embed = dpp::embed()
+ .set_color(dpp::colors::red)
+ .set_title("Error")
+ .set_description(msg);
+
+ bot->core->message_create(dpp::message(channelId, embed));
+}
+
+
+void Module::EmbedError(const std::string &message, int64_t channelId) {
+ std::string msg = "Got an error running that command!\n\n```";
+ msg.append(message);
+ msg.append("\n```");
+ dpp::embed embed = dpp::embed()
+ .set_color(dpp::colors::red)
+ .set_title("Error")
+ .set_description(msg);
+
+ bot->core->message_create(dpp::message(channelId, embed));
+}
+
+
+void Module::EmbedSuccess(const std::string &message, int64_t channelId) {
+ dpp::embed embed = dpp::embed()
+ .set_color(dpp::colors::green)
+ .set_title("Success!")
+ .set_description(message);
+
+ bot->core->message_create(dpp::message(channelId, embed));
} \ No newline at end of file