Merge branch 'working'

This commit is contained in:
Howard Hinnant 2016-05-21 10:53:17 -04:00
commit 78a882acdd
20 changed files with 1121 additions and 555 deletions

View File

@ -1,11 +1,11 @@
This is actually three separate C++11/C++14 libraries: This is actually three separate C++11/C++14 libraries:
1. `"date.h"` is a header-only library which builds upon `<chrono>`. It adds some new `duration` types, and new `time_point` types. It also adds "field" types such as `year_month_day` which is a struct `{year, month, day}`. And it provides convenient means to convert between the "field" types and the `time_point` types. See http://howardhinnant.github.io/date_v2.html for more details. 1. `"date.h"` is a header-only library which builds upon `<chrono>`. It adds some new `duration` types, and new `time_point` types. It also adds "field" types such as `year_month_day` which is a struct `{year, month, day}`. And it provides convenient means to convert between the "field" types and the `time_point` types. See http://howardhinnant.github.io/date/date.html for more details.
Here is the Cppcon 2015 presentation on date.h: https://www.youtube.com/watch?v=tzyGjOm8AKo Here is the Cppcon 2015 presentation on date.h: https://www.youtube.com/watch?v=tzyGjOm8AKo
Here are the Cppcon 2015 slides on date.h: http://schd.ws/hosted_files/cppcon2015/43/hinnant_dates.pdf Here are the Cppcon 2015 slides on date.h: http://schd.ws/hosted_files/cppcon2015/43/hinnant_dates.pdf
2. `"tz.h"` / `"tz.cpp"` are a timezone library built on top of the `"date.h"` library. This timezone library is a complete parser of the IANA timezone database. It provides for an easy way to access all of the data in this database, using the types from `"date.h"` and `<chrono>`. The IANA database also includes data on leap seconds, and this library provides utilities to compute with that information as well. See http://howardhinnant.github.io/tz.html for more details. 2. `"tz.h"` / `"tz.cpp"` are a timezone library built on top of the `"date.h"` library. This timezone library is a complete parser of the IANA timezone database. It provides for an easy way to access all of the data in this database, using the types from `"date.h"` and `<chrono>`. The IANA database also includes data on leap seconds, and this library provides utilities to compute with that information as well. See http://howardhinnant.github.io/date/tz.html for more details.
3. `"iso_week.h"` is a header-only library built on top of the `"date.h"` library which implements the ISO week date calendar. See http://howardhinnant.github.io/iso_week.html for more details. 3. `"iso_week.h"` is a header-only library built on top of the `"date.h"` library which implements the ISO week date calendar. See http://howardhinnant.github.io/date/iso_week.html for more details.

192
date.h
View File

@ -22,17 +22,23 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
//
// Our apologies. When the previous paragraph was written, lowercase had not yet
// been invented (that woud involve another several millennia of evolution).
// We did not mean to shout.
#include <chrono> #include <chrono>
#include <climits> #include <climits>
#if !(__cplusplus >= 201402) #if !(__cplusplus >= 201402)
# include <cmath> # include <cmath>
#endif #endif
#include <cstdint>
#include <cstdlib>
#include <limits> #include <limits>
#include <locale> #include <locale>
#include <ostream> #include <ostream>
#include <ratio> #include <ratio>
#include <stdexcept> #include <type_traits>
namespace date namespace date
{ {
@ -81,7 +87,19 @@ using months = std::chrono::duration
// time_point // time_point
using day_point = std::chrono::time_point<std::chrono::system_clock, days>; 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 // types
@ -314,7 +332,8 @@ class weekday
public: public:
explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT;
explicit weekday(int) = delete; explicit weekday(int) = delete;
CONSTCD11 weekday(const day_point& dp) NOEXCEPT; CONSTCD11 weekday(const sys_days& dp) NOEXCEPT;
CONSTCD11 weekday(const local_days& dp) NOEXCEPT;
weekday& operator++() NOEXCEPT; weekday& operator++() NOEXCEPT;
weekday operator++(int) NOEXCEPT; weekday operator++(int) NOEXCEPT;
@ -525,7 +544,9 @@ public:
CONSTCD11 year_month_day(const date::year& y, const date::month& m, CONSTCD11 year_month_day(const date::year& y, const date::month& m,
const date::day& d) NOEXCEPT; const date::day& d) NOEXCEPT;
CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT;
CONSTCD14 year_month_day(const day_point& dp) NOEXCEPT;
CONSTCD14 year_month_day(sys_days dp) NOEXCEPT;
CONSTCD14 year_month_day(local_days dp) NOEXCEPT;
year_month_day& operator+=(const months& m) NOEXCEPT; year_month_day& operator+=(const months& m) NOEXCEPT;
year_month_day& operator-=(const months& m) NOEXCEPT; year_month_day& operator-=(const months& m) NOEXCEPT;
@ -536,11 +557,13 @@ public:
CONSTCD11 date::month month() const NOEXCEPT; CONSTCD11 date::month month() const NOEXCEPT;
CONSTCD11 date::day day() const NOEXCEPT; CONSTCD11 date::day day() const NOEXCEPT;
CONSTCD14 operator day_point() const NOEXCEPT; CONSTCD14 operator sys_days() const NOEXCEPT;
CONSTCD14 explicit operator local_days() const NOEXCEPT;
CONSTCD14 bool ok() const NOEXCEPT; CONSTCD14 bool ok() const NOEXCEPT;
private: private:
static CONSTCD14 year_month_day from_day_point(const day_point& dp) NOEXCEPT; 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;
@ -580,7 +603,8 @@ public:
CONSTCD11 date::month_day_last month_day_last() const NOEXCEPT; CONSTCD11 date::month_day_last month_day_last() const NOEXCEPT;
CONSTCD14 date::day day() const NOEXCEPT; CONSTCD14 date::day day() const NOEXCEPT;
CONSTCD14 operator day_point() const NOEXCEPT; CONSTCD14 operator sys_days() const NOEXCEPT;
CONSTCD14 explicit operator local_days() const NOEXCEPT;
CONSTCD11 bool ok() const NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT;
}; };
@ -634,7 +658,8 @@ class year_month_weekday
public: public:
CONSTCD11 year_month_weekday(const date::year& y, const date::month& m, CONSTCD11 year_month_weekday(const date::year& y, const date::month& m,
const date::weekday_indexed& wdi) NOEXCEPT; const date::weekday_indexed& wdi) NOEXCEPT;
CONSTCD14 year_month_weekday(const day_point& dp) NOEXCEPT; CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT;
CONSTCD14 year_month_weekday(const local_days& dp) NOEXCEPT;
year_month_weekday& operator+=(const months& m) NOEXCEPT; year_month_weekday& operator+=(const months& m) NOEXCEPT;
year_month_weekday& operator-=(const months& m) NOEXCEPT; year_month_weekday& operator-=(const months& m) NOEXCEPT;
@ -647,11 +672,13 @@ public:
CONSTCD11 unsigned index() const NOEXCEPT; CONSTCD11 unsigned index() const NOEXCEPT;
CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT;
CONSTCD14 operator day_point() const NOEXCEPT; CONSTCD14 operator sys_days() const NOEXCEPT;
CONSTCD14 explicit operator local_days() const NOEXCEPT;
CONSTCD14 bool ok() const NOEXCEPT; CONSTCD14 bool ok() const NOEXCEPT;
private: private:
static CONSTCD14 year_month_weekday from_day_point(const day_point& dp) NOEXCEPT; static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT;
CONSTCD14 days to_days() const NOEXCEPT;
}; };
CONSTCD11 CONSTCD11
@ -707,8 +734,12 @@ public:
CONSTCD11 date::weekday weekday() const NOEXCEPT; CONSTCD11 date::weekday weekday() const NOEXCEPT;
CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT;
CONSTCD14 operator day_point() const NOEXCEPT; CONSTCD14 operator sys_days() const NOEXCEPT;
CONSTCD14 explicit operator local_days() const NOEXCEPT;
CONSTCD11 bool ok() const NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT;
private:
CONSTCD14 days to_days() const NOEXCEPT;
}; };
CONSTCD11 CONSTCD11
@ -1292,19 +1323,14 @@ year::is_leap() const NOEXCEPT
} }
CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;}
CONSTCD11 inline bool year::ok() const NOEXCEPT {return min() <= *this && *this <= max();} CONSTCD11 inline bool year::ok() const NOEXCEPT {return true;}
CONSTCD11 CONSTCD11
inline inline
year year
year::min() NOEXCEPT year::min() NOEXCEPT
{ {
using namespace std::chrono; return year{std::numeric_limits<short>::min()};
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 CONSTCD11
@ -1312,12 +1338,7 @@ inline
year year
year::max() NOEXCEPT year::max() NOEXCEPT
{ {
using namespace std::chrono; return year{std::numeric_limits<short>::max()};
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 CONSTCD11
@ -1431,7 +1452,13 @@ weekday::weekday(unsigned wd) NOEXCEPT
CONSTCD11 CONSTCD11
inline inline
weekday::weekday(const day_point& dp) NOEXCEPT 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())) : wd_(weekday_from_days(dp.time_since_epoch().count()))
{} {}
@ -2171,6 +2198,20 @@ year_month_day_last::day() const NOEXCEPT
d[static_cast<unsigned>(month()) - 1] : date::day{29}; 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 CONSTCD11
inline inline
bool bool
@ -2305,8 +2346,14 @@ year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT
CONSTCD14 CONSTCD14
inline inline
year_month_day::year_month_day(const day_point& dp) NOEXCEPT year_month_day::year_month_day(sys_days dp) NOEXCEPT
: year_month_day(from_day_point(dp)) : 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 year year_month_day::year() const NOEXCEPT {return y_;}
@ -2347,7 +2394,8 @@ year_month_day::operator-=(const years& y) NOEXCEPT
CONSTCD14 CONSTCD14
inline inline
year_month_day::operator day_point() const NOEXCEPT days
year_month_day::to_days() const NOEXCEPT
{ {
static_assert(std::numeric_limits<unsigned>::digits >= 18, static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer"); "This algorithm has not been ported to a 16 bit unsigned integer");
@ -2360,14 +2408,21 @@ year_month_day::operator day_point() const NOEXCEPT
auto const yoe = static_cast<unsigned>(y - era * 400); // [0, 399] 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 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] auto const doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
return day_point{days{era * 146097 + static_cast<int>(doe) - 719468}}; return days{era * 146097 + static_cast<int>(doe) - 719468};
} }
CONSTCD14 CONSTCD14
inline inline
year_month_day_last::operator day_point() const NOEXCEPT year_month_day::operator sys_days() const NOEXCEPT
{ {
return day_point(year()/month()/day()); return sys_days{to_days()};
}
CONSTCD14
inline
year_month_day::operator local_days() const NOEXCEPT
{
return local_days{to_days()};
} }
CONSTCD14 CONSTCD14
@ -2449,17 +2504,17 @@ operator<<(std::ostream& os, const year_month_day& ymd)
CONSTCD14 CONSTCD14
inline inline
year_month_day year_month_day
year_month_day::from_day_point(const day_point& dp) NOEXCEPT year_month_day::from_days(days dp) NOEXCEPT
{ {
static_assert(std::numeric_limits<unsigned>::digits >= 18, static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer"); "This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<int>::digits >= 20, static_assert(std::numeric_limits<int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer"); "This algorithm has not been ported to a 16 bit signed integer");
auto const z = dp.time_since_epoch().count() + 719468; auto const z = dp.count() + 719468;
auto const era = (z >= 0 ? z : z - 146096) / 146097; auto const era = (z >= 0 ? z : z - 146096) / 146097;
auto const doe = static_cast<unsigned>(z - era * 146097); // [0, 146096] 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 yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399]
auto const y = static_cast<day_point::rep>(yoe) + era * 400; auto const y = static_cast<days::rep>(yoe) + era * 400;
auto const doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365] auto const doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365]
auto const mp = (5*doy + 2)/153; // [0, 11] auto const mp = (5*doy + 2)/153; // [0, 11]
auto const d = doy - (153*mp+2)/5 + 1; // [1, 31] auto const d = doy - (153*mp+2)/5 + 1; // [1, 31]
@ -2529,8 +2584,14 @@ year_month_weekday::year_month_weekday(const date::year& y, const date::month& m
CONSTCD14 CONSTCD14
inline inline
year_month_weekday::year_month_weekday(const day_point& dp) NOEXCEPT year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT
: year_month_weekday(from_day_point(dp)) : 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()))
{} {}
inline inline
@ -2594,10 +2655,16 @@ year_month_weekday::weekday_indexed() const NOEXCEPT
CONSTCD14 CONSTCD14
inline inline
year_month_weekday::operator day_point() const NOEXCEPT year_month_weekday::operator sys_days() const NOEXCEPT
{ {
auto d = day_point(y_/m_/1); return sys_days{to_days()};
return d + (wdi_.weekday() - date::weekday(d) + days{(wdi_.index()-1)*7}); }
CONSTCD14
inline
year_month_weekday::operator local_days() const NOEXCEPT
{
return local_days{to_days()};
} }
CONSTCD14 CONSTCD14
@ -2616,13 +2683,24 @@ year_month_weekday::ok() const NOEXCEPT
CONSTCD14 CONSTCD14
inline inline
year_month_weekday year_month_weekday
year_month_weekday::from_day_point(const day_point& dp) NOEXCEPT year_month_weekday::from_days(days d) NOEXCEPT
{ {
sys_days dp{d};
auto const wd = date::weekday(dp); auto const wd = date::weekday(dp);
auto const ymd = year_month_day(dp); auto const ymd = year_month_day(dp);
return {ymd.year(), ymd.month(), wd[(static_cast<unsigned>(ymd.day())-1)/7+1]}; 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 CONSTCD11
inline inline
bool bool
@ -2761,10 +2839,16 @@ year_month_weekday_last::weekday_last() const NOEXCEPT
CONSTCD14 CONSTCD14
inline inline
year_month_weekday_last::operator day_point() const NOEXCEPT year_month_weekday_last::operator sys_days() const NOEXCEPT
{ {
auto const d = day_point(y_/m_/last); return sys_days{to_days()};
return d - (date::weekday{d} - wdl_.weekday()); }
CONSTCD14
inline
year_month_weekday_last::operator local_days() const NOEXCEPT
{
return local_days{to_days()};
} }
CONSTCD11 CONSTCD11
@ -2775,6 +2859,15 @@ year_month_weekday_last::ok() const NOEXCEPT
return y_.ok() && m_.ok() && wdl_.ok(); 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 CONSTCD11
inline inline
bool bool
@ -3723,8 +3816,7 @@ typename std::enable_if
std::ratio_less<typename Duration::period, days::period>::value std::ratio_less<typename Duration::period, days::period>::value
, std::ostream& , std::ostream&
>::type >::type
operator<<(std::ostream& os, operator<<(std::ostream& os, const sys_time<Duration>& tp)
const std::chrono::time_point<std::chrono::system_clock, Duration>& tp)
{ {
auto const dp = floor<days>(tp); auto const dp = floor<days>(tp);
return os << year_month_day(dp) << ' ' << make_time(tp-dp); return os << year_month_day(dp) << ' ' << make_time(tp-dp);
@ -3732,11 +3824,19 @@ operator<<(std::ostream& os,
inline inline
std::ostream& std::ostream&
operator<<(std::ostream& os, const day_point& dp) operator<<(std::ostream& os, const sys_days& dp)
{ {
return os << year_month_day(dp); return os << year_month_day(dp);
} }
template <class Duration>
inline
std::ostream&
operator<<(std::ostream& os, const local_time<Duration>& ut)
{
return os << sys_time<Duration>{ut.time_since_epoch()};
}
} // namespace date } // namespace date
#endif // DATE_H #endif // DATE_H

View File

@ -38,7 +38,8 @@ using years = date::years;
// time_point // time_point
using day_point = date::day_point; using sys_days = date::sys_days;
using local_days = date::local_days;
// types // types
@ -95,7 +96,8 @@ public:
explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT;
CONSTCD11 weekday(date::weekday wd) NOEXCEPT; CONSTCD11 weekday(date::weekday wd) NOEXCEPT;
explicit weekday(int) = delete; explicit weekday(int) = delete;
CONSTCD11 weekday(const day_point& dp) NOEXCEPT; CONSTCD11 weekday(const sys_days& dp) NOEXCEPT;
CONSTCD11 weekday(const local_days& dp) NOEXCEPT;
weekday& operator++() NOEXCEPT; weekday& operator++() NOEXCEPT;
weekday operator++(int) NOEXCEPT; weekday operator++(int) NOEXCEPT;
@ -328,7 +330,8 @@ public:
CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT; CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT;
CONSTCD11 iso_week::weekday weekday() const NOEXCEPT; CONSTCD11 iso_week::weekday weekday() const NOEXCEPT;
CONSTCD14 operator day_point() const NOEXCEPT; CONSTCD14 operator sys_days() const NOEXCEPT;
CONSTCD14 explicit operator local_days() const NOEXCEPT;
CONSTCD11 bool ok() const NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT;
}; };
@ -357,7 +360,8 @@ public:
CONSTCD11 year_weeknum_weekday(const iso_week::year& y, const iso_week::weeknum& wn, CONSTCD11 year_weeknum_weekday(const iso_week::year& y, const iso_week::weeknum& wn,
const iso_week::weekday& wd) NOEXCEPT; const iso_week::weekday& wd) NOEXCEPT;
CONSTCD14 year_weeknum_weekday(const year_lastweek_weekday& ylwwd) NOEXCEPT; CONSTCD14 year_weeknum_weekday(const year_lastweek_weekday& ylwwd) NOEXCEPT;
CONSTCD14 year_weeknum_weekday(const day_point& dp) NOEXCEPT; CONSTCD14 year_weeknum_weekday(const sys_days& dp) NOEXCEPT;
CONSTCD14 year_weeknum_weekday(const local_days& dp) NOEXCEPT;
year_weeknum_weekday& operator+=(const years& y) NOEXCEPT; year_weeknum_weekday& operator+=(const years& y) NOEXCEPT;
year_weeknum_weekday& operator-=(const years& y) NOEXCEPT; year_weeknum_weekday& operator-=(const years& y) NOEXCEPT;
@ -366,11 +370,12 @@ public:
CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT; CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT;
CONSTCD11 iso_week::weekday weekday() const NOEXCEPT; CONSTCD11 iso_week::weekday weekday() const NOEXCEPT;
CONSTCD14 operator day_point() const NOEXCEPT; CONSTCD14 operator sys_days() const NOEXCEPT;
CONSTCD14 explicit operator local_days() const NOEXCEPT;
CONSTCD14 bool ok() const NOEXCEPT; CONSTCD14 bool ok() const NOEXCEPT;
private: private:
static CONSTCD14 year_weeknum_weekday from_day_point(const day_point& dp) NOEXCEPT; 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;
@ -431,7 +436,13 @@ weekday::weekday(date::weekday wd) NOEXCEPT
CONSTCD11 CONSTCD11
inline inline
weekday::weekday(const day_point& dp) NOEXCEPT 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())) : wd_(weekday_from_days(dp.time_since_epoch().count()))
{} {}
@ -980,8 +991,8 @@ weeknum
year_lastweek::weeknum() const NOEXCEPT year_lastweek::weeknum() const NOEXCEPT
{ {
const auto y = date::year{int{y_}}; const auto y = date::year{int{y_}};
const auto s0 = day_point{(y-years{1})/12/date::thu[date::last]}; const auto s0 = sys_days{(y-years{1})/12/date::thu[date::last]};
const auto s1 = day_point{y/12/date::thu[date::last]}; const auto s1 = sys_days{y/12/date::thu[date::last]};
return iso_week::weeknum(date::trunc<weeks>(s1-s0).count()); return iso_week::weeknum(date::trunc<weeks>(s1-s0).count());
} }
@ -1273,9 +1284,17 @@ CONSTCD11 inline weekday year_lastweek_weekday::weekday() const NOEXCEPT {return
CONSTCD14 CONSTCD14
inline inline
year_lastweek_weekday::operator day_point() const NOEXCEPT year_lastweek_weekday::operator sys_days() const NOEXCEPT
{ {
return day_point{date::year{int{y_}}/date::dec/date::thu[date::last]} + (mon - thu) return sys_days{date::year{int{y_}}/date::dec/date::thu[date::last]} + (mon - thu)
- (mon - wd_);
}
CONSTCD14
inline
year_lastweek_weekday::operator local_days() const NOEXCEPT
{
return local_days{date::year{int{y_}}/date::dec/date::thu[date::last]} + (mon - thu)
- (mon - wd_); - (mon - wd_);
} }
@ -1390,8 +1409,14 @@ year_weeknum_weekday::year_weeknum_weekday(const year_lastweek_weekday& ylwwd) N
CONSTCD14 CONSTCD14
inline inline
year_weeknum_weekday::year_weeknum_weekday(const day_point& dp) NOEXCEPT year_weeknum_weekday::year_weeknum_weekday(const sys_days& dp) NOEXCEPT
: year_weeknum_weekday(from_day_point(dp)) : 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 inline
@ -1416,9 +1441,17 @@ CONSTCD11 inline weekday year_weeknum_weekday::weekday() const NOEXCEPT {return
CONSTCD14 CONSTCD14
inline inline
year_weeknum_weekday::operator day_point() const NOEXCEPT year_weeknum_weekday::operator sys_days() const NOEXCEPT
{ {
return day_point{date::year{int{y_}-1}/date::dec/date::thu[date::last]} return sys_days{date::year{int{y_}-1}/date::dec/date::thu[date::last]}
+ (date::mon - date::thu) + weeks{unsigned{wn_}-1} + (wd_ - mon);
}
CONSTCD14
inline
year_weeknum_weekday::operator local_days() const NOEXCEPT
{
return local_days{date::year{int{y_}-1}/date::dec/date::thu[date::last]}
+ (date::mon - date::thu) + weeks{unsigned{wn_}-1} + (wd_ - mon); + (date::mon - date::thu) + weeks{unsigned{wn_}-1} + (wd_ - mon);
} }
@ -1433,15 +1466,16 @@ year_weeknum_weekday::ok() const NOEXCEPT
CONSTCD14 CONSTCD14
inline inline
year_weeknum_weekday year_weeknum_weekday
year_weeknum_weekday::from_day_point(const day_point& dp) NOEXCEPT year_weeknum_weekday::from_days(days d) NOEXCEPT
{ {
const auto dp = sys_days{d};
const auto wd = iso_week::weekday{dp}; const auto wd = iso_week::weekday{dp};
auto y = date::year_month_day{dp + days{3}}.year(); auto y = date::year_month_day{dp + days{3}}.year();
auto start = day_point{(y - date::years{1})/date::dec/date::thu[date::last]} + (mon-thu); auto start = sys_days{(y - date::years{1})/date::dec/date::thu[date::last]} + (mon-thu);
if (dp < start) if (dp < start)
{ {
--y; --y;
start = day_point{(y - date::years{1})/date::dec/date::thu[date::last]} + (mon-thu); start = sys_days{(y - date::years{1})/date::dec/date::thu[date::last]} + (mon-thu);
} }
const auto wn = iso_week::weeknum(date::trunc<weeks>(dp - start).count() + 1); const auto wn = iso_week::weeknum(date::trunc<weeks>(dp - start).count() + 1);
return {iso_week::year(int{y}), wn, wd}; return {iso_week::year(int{y}), wn, wd};

View File

@ -36,7 +36,7 @@
// //
// time_point // time_point
// //
// using day_point = std::chrono::time_point<std::chrono::system_clock, days>; // using sys_days = std::chrono::time_point<std::chrono::system_clock, days>;
#include "date.h" #include "date.h"
@ -54,7 +54,7 @@ static_assert(date::years{400} == date::days{146097}, "");
static_assert(date::days{365} < date::years{1} && date::years{1} < date::days{366}, ""); static_assert(date::days{365} < date::years{1} && date::years{1} < date::days{366}, "");
static_assert(date::weeks{52} < date::years{1} && date::years{1} < date::weeks{53}, ""); static_assert(date::weeks{52} < date::years{1} && date::years{1} < date::weeks{53}, "");
static_assert(std::is_same<date::day_point::duration, date::days>{}, ""); static_assert(std::is_same<date::sys_days::duration, date::days>{}, "");
int int
main() main()

View File

@ -34,7 +34,7 @@ main()
static_assert(sizeof(months) == 4, ""); static_assert(sizeof(months) == 4, "");
static_assert(sizeof(years) == 4, ""); static_assert(sizeof(years) == 4, "");
static_assert(sizeof(day_point) == 4, ""); static_assert(sizeof(sys_days) == 4, "");
static_assert(sizeof(last_spec) == 1, ""); static_assert(sizeof(last_spec) == 1, "");

View File

@ -25,7 +25,7 @@
// unsigned char wd_; // unsigned char wd_;
// public: // public:
// explicit constexpr weekday(unsigned wd) noexcept; // explicit constexpr weekday(unsigned wd) noexcept;
// constexpr weekday(const day_point& dp) noexcept; // constexpr weekday(const sys_days& dp) noexcept;
// //
// weekday& operator++() noexcept; // weekday& operator++() noexcept;
// weekday operator++(int) noexcept; // weekday operator++(int) noexcept;
@ -76,10 +76,10 @@ static_assert( std::is_trivially_move_constructible<date::weekday>{}, "");
static_assert( std::is_trivially_move_assignable<date::weekday>{}, ""); static_assert( std::is_trivially_move_assignable<date::weekday>{}, "");
static_assert( std::is_nothrow_constructible<date::weekday, unsigned>{}, ""); static_assert( std::is_nothrow_constructible<date::weekday, unsigned>{}, "");
static_assert( std::is_nothrow_constructible<date::weekday, date::day_point>{}, ""); static_assert( std::is_nothrow_constructible<date::weekday, date::sys_days>{}, "");
static_assert( std::is_nothrow_constructible<unsigned, date::weekday>{}, ""); static_assert( std::is_nothrow_constructible<unsigned, date::weekday>{}, "");
static_assert(!std::is_convertible<unsigned, date::weekday>{}, ""); static_assert(!std::is_convertible<unsigned, date::weekday>{}, "");
static_assert( std::is_convertible<date::day_point, date::weekday>{}, ""); static_assert( std::is_convertible<date::sys_days, date::weekday>{}, "");
static_assert(!std::is_convertible<date::weekday, unsigned>{}, ""); static_assert(!std::is_convertible<date::weekday, unsigned>{}, "");
static_assert(static_cast<unsigned>(date::weekday{1u}) == 1, ""); static_assert(static_cast<unsigned>(date::weekday{1u}) == 1, "");

View File

@ -108,29 +108,29 @@ main()
#if __cplusplus >= 201402 #if __cplusplus >= 201402
using int64_t = std::int64_t; using int64_t = std::int64_t;
static_assert(day_point(year::min()/jan/1) - day_point(1970_y/jan/1) static_assert(sys_days(year::min()/jan/1) - sys_days(1970_y/jan/1)
>= as<int64_t>(days::min()), ""); >= as<int64_t>(days::min()), "");
static_assert(day_point(year::min()/jan/1) - day_point(1970_y/jan/1) static_assert(sys_days(year::min()/jan/1) - sys_days(1970_y/jan/1)
>= as<int64_t>(hours::min()), ""); >= as<int64_t>(hours::min()), "");
static_assert(day_point(year::min()/jan/1) - day_point(1970_y/jan/1) static_assert(sys_days(year::min()/jan/1) - sys_days(1970_y/jan/1)
>= as<int64_t>(minutes::min()), ""); >= as<int64_t>(minutes::min()), "");
static_assert(day_point(year::min()/jan/1) - day_point(1970_y/jan/1) static_assert(sys_days(year::min()/jan/1) - sys_days(1970_y/jan/1)
>= as<int64_t>(seconds::min()), ""); >= as<int64_t>(seconds::min()), "");
static_assert(day_point(year::min()/jan/1) - day_point(1970_y/jan/1) static_assert(sys_days(year::min()/jan/1) - sys_days(1970_y/jan/1)
>= as<int64_t>(milliseconds::min()), ""); >= as<int64_t>(milliseconds::min()), "");
static_assert(day_point(year::min()/jan/1) - day_point(1970_y/jan/1) static_assert(sys_days(year::min()/jan/1) - sys_days(1970_y/jan/1)
>= as<int64_t>(microseconds::min()), ""); >= as<int64_t>(microseconds::min()), "");
static_assert(day_point(year::max()/dec/31) - day_point(1970_y/jan/1) static_assert(sys_days(year::max()/dec/31) - sys_days(1970_y/jan/1)
<= as<int64_t>(microseconds::max()), ""); <= as<int64_t>(microseconds::max()), "");
static_assert(day_point(year::max()/dec/31) - day_point(1970_y/jan/1) static_assert(sys_days(year::max()/dec/31) - sys_days(1970_y/jan/1)
<= as<int64_t>(milliseconds::max()), ""); <= as<int64_t>(milliseconds::max()), "");
static_assert(day_point(year::max()/dec/31) - day_point(1970_y/jan/1) static_assert(sys_days(year::max()/dec/31) - sys_days(1970_y/jan/1)
<= as<int64_t>(seconds::max()), ""); <= as<int64_t>(seconds::max()), "");
static_assert(day_point(year::max()/dec/31) - day_point(1970_y/jan/1) static_assert(sys_days(year::max()/dec/31) - sys_days(1970_y/jan/1)
<= as<int64_t>(minutes::max()), ""); <= as<int64_t>(minutes::max()), "");
static_assert(day_point(year::max()/dec/31) - day_point(1970_y/jan/1) static_assert(sys_days(year::max()/dec/31) - sys_days(1970_y/jan/1)
<= as<int64_t>(hours::max()), ""); <= as<int64_t>(hours::max()), "");
static_assert(day_point(year::max()/dec/31) - day_point(1970_y/jan/1) static_assert(sys_days(year::max()/dec/31) - sys_days(1970_y/jan/1)
<= as<int64_t>(days::max()), ""); <= as<int64_t>(days::max()), "");
#endif #endif

View File

@ -26,7 +26,7 @@
// constexpr year_month_day(const date::year& y, const date::month& m, // constexpr year_month_day(const date::year& y, const date::month& m,
// const date::day& d) noexcept; // const date::day& d) noexcept;
// constexpr year_month_day(const year_month_day_last& ymdl) noexcept; // constexpr year_month_day(const year_month_day_last& ymdl) noexcept;
// constexpr year_month_day(const day_point& dp) noexcept; // constexpr year_month_day(const sys_days& dp) noexcept;
// //
// year_month_day& operator+=(const months& m) noexcept; // year_month_day& operator+=(const months& m) noexcept;
// year_month_day& operator-=(const months& m) noexcept; // year_month_day& operator-=(const months& m) noexcept;
@ -37,7 +37,7 @@
// constexpr date::month month() const noexcept; // constexpr date::month month() const noexcept;
// constexpr date::day day() const noexcept; // constexpr date::day day() const noexcept;
// //
// constexpr operator day_point() const noexcept; // constexpr operator sys_days() const noexcept;
// constexpr bool ok() const noexcept; // constexpr bool ok() const noexcept;
// }; // };
@ -76,10 +76,10 @@ static_assert(std::is_nothrow_constructible<date::year_month_day, date::year,
static_assert(std::is_nothrow_constructible<date::year_month_day, static_assert(std::is_nothrow_constructible<date::year_month_day,
date::year_month_day_last>{}, ""); date::year_month_day_last>{}, "");
static_assert(std::is_convertible<date::year_month_day_last, date::year_month_day>{}, ""); static_assert(std::is_convertible<date::year_month_day_last, date::year_month_day>{}, "");
static_assert(std::is_nothrow_constructible<date::year_month_day, date::day_point>{}, ""); static_assert(std::is_nothrow_constructible<date::year_month_day, date::sys_days>{}, "");
static_assert(std::is_convertible<date::day_point, date::year_month_day>{}, ""); static_assert(std::is_convertible<date::sys_days, date::year_month_day>{}, "");
static_assert(std::is_nothrow_constructible<date::day_point, date::year_month_day>{}, ""); static_assert(std::is_nothrow_constructible<date::sys_days, date::year_month_day>{}, "");
static_assert(std::is_convertible<date::year_month_day, date::day_point>{}, ""); static_assert(std::is_convertible<date::year_month_day, date::sys_days>{}, "");
void void
test_arithmetic() test_arithmetic()
@ -131,7 +131,7 @@ test_day_point_conversion()
using namespace date; using namespace date;
year y = year{-1000}; year y = year{-1000};
year end = 3000_y; year end = 3000_y;
day_point prev_dp = day_point(year_month_day{y, jan, 1_d}) - days{1}; sys_days prev_dp = sys_days(year_month_day{y, jan, 1_d}) - days{1};
weekday prev_wd = weekday{prev_dp}; weekday prev_wd = weekday{prev_dp};
for (; y <= end; ++y) for (; y <= end; ++y)
{ {
@ -143,7 +143,7 @@ test_day_point_conversion()
{ {
year_month_day ymd = {y, m, d}; year_month_day ymd = {y, m, d};
assert(ymd.ok()); assert(ymd.ok());
day_point dp = ymd; sys_days dp = ymd;
assert(dp == prev_dp + days{1}); assert(dp == prev_dp + days{1});
year_month_day ymd2 = dp; year_month_day ymd2 = dp;
assert(ymd2 == ymd); assert(ymd2 == ymd);
@ -171,7 +171,7 @@ main()
static_assert(ymd1.day() == 9_d, ""); static_assert(ymd1.day() == 9_d, "");
#if __cplusplus >= 201402 #if __cplusplus >= 201402
constexpr day_point dp = ymd1; constexpr sys_days dp = ymd1;
static_assert(dp.time_since_epoch() == days{16656}, ""); static_assert(dp.time_since_epoch() == days{16656}, "");
constexpr year_month_day ymd2 = dp; constexpr year_month_day ymd2 = dp;
static_assert(ymd1 == ymd2, ""); static_assert(ymd1 == ymd2, "");
@ -186,7 +186,7 @@ main()
static_assert(ymd3.day() == 31_d, ""); static_assert(ymd3.day() == 31_d, "");
#if __cplusplus >= 201402 #if __cplusplus >= 201402
constexpr day_point dp3 = ymd3; constexpr sys_days dp3 = ymd3;
static_assert(dp3.time_since_epoch() == days{-1}, ""); static_assert(dp3.time_since_epoch() == days{-1}, "");
constexpr year_month_day ymd4 = dp3; constexpr year_month_day ymd4 = dp3;
static_assert(ymd3 == ymd4, ""); static_assert(ymd3 == ymd4, "");
@ -211,9 +211,9 @@ main()
static_assert( (2100_y/feb/28).ok(), ""); static_assert( (2100_y/feb/28).ok(), "");
static_assert(!(2100_y/feb/29).ok(), ""); static_assert(!(2100_y/feb/29).ok(), "");
static_assert(day_point(2100_y/feb/28) + days{1} == day_point(2100_y/mar/1), ""); static_assert(sys_days(2100_y/feb/28) + days{1} == sys_days(2100_y/mar/1), "");
static_assert(day_point(2000_y/mar/1) - day_point(2000_y/feb/28) == days{2}, ""); static_assert(sys_days(2000_y/mar/1) - sys_days(2000_y/feb/28) == days{2}, "");
static_assert(day_point(2100_y/mar/1) - day_point(2100_y/feb/28) == days{1}, ""); static_assert(sys_days(2100_y/mar/1) - sys_days(2100_y/feb/28) == days{1}, "");
static_assert(jan/31/2015 == jan/last/2015, ""); static_assert(jan/31/2015 == jan/last/2015, "");
static_assert(feb/28/2015 == feb/last/2015, ""); static_assert(feb/28/2015 == feb/last/2015, "");

View File

@ -36,7 +36,7 @@
// constexpr date::month_day_last month_day_last() const noexcept; // constexpr date::month_day_last month_day_last() const noexcept;
// constexpr date::day day() const noexcept; // constexpr date::day day() const noexcept;
// //
// constexpr operator day_point() const noexcept; // constexpr operator sys_days() const noexcept;
// constexpr bool ok() const noexcept; // constexpr bool ok() const noexcept;
// }; // };
@ -94,9 +94,9 @@ static_assert( std::is_trivially_move_assignable<date::year_month_day_last>{}, "
static_assert(std::is_nothrow_constructible<date::year_month_day_last, static_assert(std::is_nothrow_constructible<date::year_month_day_last,
date::year, date::month_day_last>{}, ""); date::year, date::month_day_last>{}, "");
static_assert(std::is_nothrow_constructible<date::day_point, static_assert(std::is_nothrow_constructible<date::sys_days,
date::year_month_day_last>{}, ""); date::year_month_day_last>{}, "");
static_assert(std::is_convertible<date::year_month_day_last, date::day_point>{}, ""); static_assert(std::is_convertible<date::year_month_day_last, date::sys_days>{}, "");
void void
test_arithmetic() test_arithmetic()
@ -154,7 +154,7 @@ main()
static_assert(ymdl1.month_day_last() == month_day_last{aug}, ""); static_assert(ymdl1.month_day_last() == month_day_last{aug}, "");
#if __cplusplus >= 201402 #if __cplusplus >= 201402
static_assert(ymdl1.day() == 31_d, ""); static_assert(ymdl1.day() == 31_d, "");
constexpr day_point dp = ymdl1; constexpr sys_days dp = ymdl1;
constexpr year_month_day ymd = dp; constexpr year_month_day ymd = dp;
static_assert(ymd == 2015_y/aug/31, ""); static_assert(ymd == 2015_y/aug/31, "");
#endif #endif

View File

@ -25,7 +25,7 @@
// public: // public:
// constexpr year_month_weekday(const date::year& y, const date::month& m, // constexpr year_month_weekday(const date::year& y, const date::month& m,
// const date::weekday_indexed& wdi) noexcept; // const date::weekday_indexed& wdi) noexcept;
// constexpr year_month_weekday(const day_point& dp) noexcept; // constexpr year_month_weekday(const sys_days& dp) noexcept;
// //
// year_month_weekday& operator+=(const months& m) noexcept; // year_month_weekday& operator+=(const months& m) noexcept;
// year_month_weekday& operator-=(const months& m) noexcept; // year_month_weekday& operator-=(const months& m) noexcept;
@ -38,11 +38,11 @@
// constexpr unsigned index() const noexcept; // constexpr unsigned index() const noexcept;
// constexpr date::weekday_indexed weekday_indexed() const noexcept; // constexpr date::weekday_indexed weekday_indexed() const noexcept;
// //
// constexpr operator day_point() const noexcept; // constexpr operator sys_days() const noexcept;
// constexpr bool ok() const noexcept; // constexpr bool ok() const noexcept;
// //
// private: // private:
// static constexpr year_month_weekday from_day_point(const day_point& dp) noexcept; // static constexpr year_month_weekday from_day_point(const sys_days& dp) noexcept;
// }; // };
// constexpr // constexpr
@ -93,11 +93,11 @@ static_assert(std::is_nothrow_constructible<date::year_month_weekday,
date::year, date::month, date::year, date::month,
date::weekday_indexed>{}, ""); date::weekday_indexed>{}, "");
static_assert(std::is_nothrow_constructible<date::year_month_weekday, static_assert(std::is_nothrow_constructible<date::year_month_weekday,
date::day_point>{}, ""); date::sys_days>{}, "");
static_assert(std::is_convertible<date::day_point, date::year_month_weekday>{}, ""); static_assert(std::is_convertible<date::sys_days, date::year_month_weekday>{}, "");
static_assert(std::is_nothrow_constructible<date::day_point, static_assert(std::is_nothrow_constructible<date::sys_days,
date::year_month_weekday>{}, ""); date::year_month_weekday>{}, "");
static_assert(std::is_convertible<date::year_month_weekday, date::day_point>{}, ""); static_assert(std::is_convertible<date::year_month_weekday, date::sys_days>{}, "");
void void
test_arithmetic() test_arithmetic()
@ -158,7 +158,7 @@ main()
static_assert(ymdl1.index() == 2u, ""); static_assert(ymdl1.index() == 2u, "");
static_assert(ymdl1.weekday_indexed() == fri[2], ""); static_assert(ymdl1.weekday_indexed() == fri[2], "");
#if __cplusplus >= 201402 #if __cplusplus >= 201402
constexpr day_point dp = ymdl1; constexpr sys_days dp = ymdl1;
constexpr year_month_day ymd = dp; constexpr year_month_day ymd = dp;
static_assert(ymd == 2015_y/aug/14, ""); static_assert(ymd == 2015_y/aug/14, "");
#endif #endif

View File

@ -36,7 +36,7 @@
// constexpr date::weekday weekday() const noexcept; // constexpr date::weekday weekday() const noexcept;
// constexpr date::weekday_last weekday_last() const noexcept; // constexpr date::weekday_last weekday_last() const noexcept;
// //
// constexpr operator day_point() const noexcept; // constexpr operator sys_days() const noexcept;
// constexpr bool ok() const noexcept; // constexpr bool ok() const noexcept;
// }; // };
@ -90,9 +90,9 @@ static_assert( std::is_trivially_move_assignable<date::year_month_weekday_last>{
static_assert(std::is_nothrow_constructible<date::year_month_weekday_last, static_assert(std::is_nothrow_constructible<date::year_month_weekday_last,
date::year, date::month, date::year, date::month,
date::weekday_last>{}, ""); date::weekday_last>{}, "");
static_assert(std::is_nothrow_constructible<date::day_point, static_assert(std::is_nothrow_constructible<date::sys_days,
date::year_month_weekday_last>{}, ""); date::year_month_weekday_last>{}, "");
static_assert(std::is_convertible<date::year_month_weekday_last, date::day_point>{}, ""); static_assert(std::is_convertible<date::year_month_weekday_last, date::sys_days>{}, "");
void void
test_arithmetic() test_arithmetic()
@ -150,7 +150,7 @@ main()
static_assert(ymdl1.weekday() == fri, ""); static_assert(ymdl1.weekday() == fri, "");
static_assert(ymdl1.weekday_last() == fri[last], ""); static_assert(ymdl1.weekday_last() == fri[last], "");
#if __cplusplus >= 201402 #if __cplusplus >= 201402
constexpr day_point dp = ymdl1; constexpr sys_days dp = ymdl1;
constexpr year_month_day ymd = dp; constexpr year_month_day ymd = dp;
static_assert(ymd == 2015_y/aug/28, ""); static_assert(ymd == 2015_y/aug/28, "");
#endif #endif

View File

@ -26,7 +26,7 @@
// public: // public:
// explicit constexpr weekday(unsigned wd) noexcept; // explicit constexpr weekday(unsigned wd) noexcept;
// constexpr weekday(date::weekday wd) noexcept; // constexpr weekday(date::weekday wd) noexcept;
// constexpr weekday(const day_point& dp) noexcept; // constexpr weekday(const sys_days& dp) noexcept;
// //
// weekday& operator++() noexcept; // weekday& operator++() noexcept;
// weekday operator++(int) noexcept; // weekday operator++(int) noexcept;
@ -85,12 +85,12 @@ static_assert(std::is_standard_layout<iso_week::weekday>{}, "");
static_assert(std::is_literal_type<iso_week::weekday>{}, ""); static_assert(std::is_literal_type<iso_week::weekday>{}, "");
static_assert( std::is_nothrow_constructible<iso_week::weekday, unsigned>{}, ""); static_assert( std::is_nothrow_constructible<iso_week::weekday, unsigned>{}, "");
static_assert( std::is_nothrow_constructible<iso_week::weekday, iso_week::day_point>{}, ""); static_assert( std::is_nothrow_constructible<iso_week::weekday, iso_week::sys_days>{}, "");
static_assert( std::is_nothrow_constructible<iso_week::weekday, date::weekday>{}, ""); static_assert( std::is_nothrow_constructible<iso_week::weekday, date::weekday>{}, "");
static_assert( std::is_nothrow_constructible<date::weekday, iso_week::weekday>{}, ""); static_assert( std::is_nothrow_constructible<date::weekday, iso_week::weekday>{}, "");
static_assert( std::is_nothrow_constructible<unsigned, iso_week::weekday>{}, ""); static_assert( std::is_nothrow_constructible<unsigned, iso_week::weekday>{}, "");
static_assert(!std::is_convertible<unsigned, iso_week::weekday>{}, ""); static_assert(!std::is_convertible<unsigned, iso_week::weekday>{}, "");
static_assert( std::is_convertible<iso_week::day_point, iso_week::weekday>{}, ""); static_assert( std::is_convertible<iso_week::sys_days, iso_week::weekday>{}, "");
static_assert( std::is_convertible<iso_week::weekday, date::weekday>{}, ""); static_assert( std::is_convertible<iso_week::weekday, date::weekday>{}, "");
static_assert( std::is_convertible<date::weekday, iso_week::weekday>{}, ""); static_assert( std::is_convertible<date::weekday, iso_week::weekday>{}, "");
static_assert(!std::is_convertible<iso_week::weekday, unsigned>{}, ""); static_assert(!std::is_convertible<iso_week::weekday, unsigned>{}, "");

View File

@ -33,7 +33,7 @@
// constexpr iso_week::weeknum weeknum() const noexcept; // constexpr iso_week::weeknum weeknum() const noexcept;
// constexpr iso_week::weekday weekday() const noexcept; // constexpr iso_week::weekday weekday() const noexcept;
// //
// constexpr operator day_point() const noexcept; // constexpr operator sys_days() const noexcept;
// constexpr bool ok() const noexcept; // constexpr bool ok() const noexcept;
// }; // };
// //
@ -69,10 +69,10 @@ static_assert(std::is_literal_type<iso_week::year_lastweek_weekday>{}, "");
static_assert( std::is_nothrow_constructible<iso_week::year_lastweek_weekday, static_assert( std::is_nothrow_constructible<iso_week::year_lastweek_weekday,
iso_week::year, iso_week::weekday>{}, ""); iso_week::year, iso_week::weekday>{}, "");
static_assert( std::is_nothrow_constructible<iso_week::day_point, static_assert( std::is_nothrow_constructible<iso_week::sys_days,
iso_week::year_lastweek_weekday>{}, ""); iso_week::year_lastweek_weekday>{}, "");
static_assert( std::is_convertible<iso_week::year_lastweek_weekday, static_assert( std::is_convertible<iso_week::year_lastweek_weekday,
iso_week::day_point>{}, ""); iso_week::sys_days>{}, "");
int int
main() main()
@ -100,8 +100,8 @@ main()
assert(x3.weeknum() == 53_w); assert(x3.weeknum() == 53_w);
assert(x3.weekday() == tue); assert(x3.weekday() == tue);
constexpr day_point dp = 2015_y/last/wed; constexpr sys_days dp = 2015_y/last/wed;
static_assert(dp == day_point{days{16799}}, ""); static_assert(dp == sys_days{days{16799}}, "");
static_assert(x0.ok(), ""); static_assert(x0.ok(), "");
assert(x3.ok()); assert(x3.ok());

View File

@ -28,7 +28,7 @@
// constexpr year_weeknum_weekday(const iso_week::year& y, const iso_week::weeknum& wn, // constexpr year_weeknum_weekday(const iso_week::year& y, const iso_week::weeknum& wn,
// const iso_week::weekday& wd) noexcept; // const iso_week::weekday& wd) noexcept;
// constexpr year_weeknum_weekday(const year_lastweek_weekday& ylwwd) noexcept; // constexpr year_weeknum_weekday(const year_lastweek_weekday& ylwwd) noexcept;
// constexpr year_weeknum_weekday(const day_point& dp) noexcept; // constexpr year_weeknum_weekday(const sys_days& dp) noexcept;
// //
// year_weeknum_weekday& operator+=(const years& y) noexcept; // year_weeknum_weekday& operator+=(const years& y) noexcept;
// year_weeknum_weekday& operator-=(const years& y) noexcept; // year_weeknum_weekday& operator-=(const years& y) noexcept;
@ -37,7 +37,7 @@
// constexpr iso_week::weeknum weeknum() const noexcept; // constexpr iso_week::weeknum weeknum() const noexcept;
// constexpr iso_week::weekday weekday() const noexcept; // constexpr iso_week::weekday weekday() const noexcept;
// //
// constexpr operator day_point() const noexcept; // constexpr operator sys_days() const noexcept;
// constexpr bool ok() const noexcept; // constexpr bool ok() const noexcept;
// }; // };
// //
@ -79,13 +79,13 @@ static_assert( std::is_nothrow_constructible<iso_week::year_weeknum_weekday,
static_assert( std::is_convertible<iso_week::year_lastweek_weekday, static_assert( std::is_convertible<iso_week::year_lastweek_weekday,
iso_week::year_weeknum_weekday>{}, ""); iso_week::year_weeknum_weekday>{}, "");
static_assert( std::is_nothrow_constructible<iso_week::year_weeknum_weekday, static_assert( std::is_nothrow_constructible<iso_week::year_weeknum_weekday,
iso_week::day_point>{}, ""); iso_week::sys_days>{}, "");
static_assert( std::is_convertible<iso_week::day_point, static_assert( std::is_convertible<iso_week::sys_days,
iso_week::year_weeknum_weekday>{}, ""); iso_week::year_weeknum_weekday>{}, "");
static_assert( std::is_nothrow_constructible<iso_week::day_point, static_assert( std::is_nothrow_constructible<iso_week::sys_days,
iso_week::year_weeknum_weekday>{}, ""); iso_week::year_weeknum_weekday>{}, "");
static_assert( std::is_convertible<iso_week::year_weeknum_weekday, static_assert( std::is_convertible<iso_week::year_weeknum_weekday,
iso_week::day_point>{}, ""); iso_week::sys_days>{}, "");
int int
main() main()
@ -102,7 +102,7 @@ main()
static_assert(x1.weeknum() == 53_w, ""); static_assert(x1.weeknum() == 53_w, "");
static_assert(x1.weekday() == tue, ""); static_assert(x1.weekday() == tue, "");
constexpr year_weeknum_weekday x2 = day_point{days{16792}}; constexpr year_weeknum_weekday x2 = sys_days{days{16792}};
static_assert(x2.year() == 2015_y, ""); static_assert(x2.year() == 2015_y, "");
static_assert(x2.weeknum() == 52_w, ""); static_assert(x2.weeknum() == 52_w, "");
static_assert(x2.weekday() == wed, ""); static_assert(x2.weekday() == wed, "");
@ -118,8 +118,8 @@ main()
assert(x3.weeknum() == 52_w); assert(x3.weeknum() == 52_w);
assert(x3.weekday() == wed); assert(x3.weekday() == wed);
constexpr day_point dp = 2015_y/52_w/wed; constexpr sys_days dp = 2015_y/52_w/wed;
static_assert(dp == day_point{days{16792}}, ""); static_assert(dp == sys_days{days{16792}}, "");
static_assert(x0.ok(), ""); static_assert(x0.ok(), "");
static_assert(x1.ok(), ""); static_assert(x1.ok(), "");

Binary file not shown.

View File

@ -2,38 +2,42 @@
#include <iostream> #include <iostream>
void void
test_info(const date::Zone* zone, const date::Info& info) test_info(const date::time_zone* zone, const date::Info& info)
{ {
using namespace date; using namespace date;
using namespace std::chrono; using namespace std::chrono;
auto begin = info.begin; auto begin = info.begin;
auto end = info.end - microseconds{1}; auto end = info.end - microseconds{1};
auto mid = begin + (end - begin) /2 ; auto mid = begin + (end - begin) /2;
using sys_microseconds = sys_time<microseconds>;
using zoned_microseconds = zoned_time<microseconds>;
using local_microseconds = local_time<microseconds>;
zoned_microseconds local{zone};
if (begin > day_point{jan/1/1700}) if (begin > sys_days{jan/1/1700})
{ {
auto local = zone->to_local(begin).first; auto prev_local = local;
auto prev_local = zone->to_local(begin - seconds{1}).first; local = begin;
if (prev_local < local - seconds{1}) prev_local = begin - seconds{1};
auto slocal = local.get_local_time();
auto plocal = prev_local.get_local_time();
if (plocal < slocal - seconds{1})
{ {
assert(zone->to_sys(local) == begin); assert(sys_microseconds{local} == begin);
auto imaginary = prev_local + (local - seconds{1} - prev_local) / 2;
try try
{ {
zone->to_sys(imaginary); local = plocal + (slocal - seconds{1} - plocal) / 2;
assert(false); assert(false);
} }
catch (const nonexistent_local_time&) catch (const nonexistent_local_time&)
{ {
} }
} }
else if (prev_local > local - seconds{1}) else if (plocal > slocal - seconds{1})
{ {
auto ambiguous = local - seconds{1} +
(prev_local - (local - seconds{1})) / 2;
try try
{ {
zone->to_sys(ambiguous); local = slocal - seconds{1} + (plocal - (slocal - seconds{1})) / 2;
assert(false); assert(false);
} }
catch (const ambiguous_local_time&) catch (const ambiguous_local_time&)
@ -42,33 +46,34 @@ test_info(const date::Zone* zone, const date::Info& info)
} }
} }
auto local = zone->to_local(mid).first; local = mid;
assert(zone->to_sys(local) == mid); assert(sys_microseconds{local} == mid);
if (end < day_point{jan/1/3000}) if (end < sys_days{jan/1/3000})
{ {
auto local = zone->to_local(end).first; local = end;
auto next_local = zone->to_local(info.end).first; auto next_local = local;
if (next_local < local + microseconds{1}) next_local = info.end;
auto slocal = local.get_local_time();
auto nlocal = next_local.get_local_time();
if (nlocal < slocal + microseconds{1})
{ {
auto ambiguous = next_local + (local + microseconds{1} - next_local) / 2;
try try
{ {
zone->to_sys(ambiguous); local = nlocal + (slocal + microseconds{1} - nlocal) / 2;
assert(false); assert(false);
} }
catch (const ambiguous_local_time&) catch (const ambiguous_local_time&)
{ {
} }
} }
else if (next_local > local + microseconds{1}) else if (nlocal > slocal + microseconds{1})
{ {
assert(zone->to_sys(local) == end); assert(sys_microseconds{local} == end);
auto imaginary = local + microseconds{1} +
(next_local - (local + microseconds{1})) / 2;
try try
{ {
zone->to_sys(imaginary); local = slocal + microseconds{1} +
(nlocal - (slocal + microseconds{1})) / 2;
assert(false); assert(false);
} }
catch (const nonexistent_local_time&) catch (const nonexistent_local_time&)
@ -96,9 +101,9 @@ main()
{ {
std::cout << name << '\n'; std::cout << name << '\n';
auto z = locate_zone(name); auto z = locate_zone(name);
auto begin = day_point(jan/1/year::min()) + 0s; auto begin = sys_days(jan/1/year::min()) + 0s;
auto end = day_point(jan/1/2035) + 0s; auto end = sys_days(jan/1/2035) + 0s;
auto info = z->get_info(begin, tz::utc); auto info = z->get_info(begin);
std::cout << "Initially: "; std::cout << "Initially: ";
if (info.offset >= 0s) if (info.offset >= 0s)
std::cout << '+'; std::cout << '+';
@ -114,7 +119,7 @@ main()
auto prev_save = info.save; auto prev_save = info.save;
for (begin = info.end; begin < end; begin = info.end) for (begin = info.end; begin < end; begin = info.end)
{ {
info = z->get_info(begin, tz::utc); info = z->get_info(begin);
test_info(z, info); test_info(z, info);
if (info.offset == prev_offset && info.abbrev == prev_abbrev && if (info.offset == prev_offset && info.abbrev == prev_abbrev &&
info.save == prev_save) info.save == prev_save)
@ -122,7 +127,7 @@ main()
auto dp = floor<days>(begin); auto dp = floor<days>(begin);
auto ymd = year_month_day(dp); auto ymd = year_month_day(dp);
auto time = make_time(begin - dp); auto time = make_time(begin - dp);
std::cout << ymd << 'T' << time << "Z "; std::cout << ymd << ' ' << time << "Z ";
if (info.offset >= 0s) if (info.offset >= 0s)
std::cout << '+'; std::cout << '+';
std::cout << make_time(info.offset); std::cout << make_time(info.offset);

View File

@ -6,10 +6,10 @@ main()
{ {
using namespace std; using namespace std;
using namespace date; using namespace date;
static_assert( is_nothrow_destructible<Zone>{}, ""); static_assert( is_nothrow_destructible<time_zone>{}, "");
static_assert(!is_default_constructible<Zone>{}, ""); static_assert(!is_default_constructible<time_zone>{}, "");
static_assert(!is_copy_constructible<Zone>{}, ""); static_assert(!is_copy_constructible<time_zone>{}, "");
static_assert(!is_copy_assignable<Zone>{}, ""); static_assert(!is_copy_assignable<time_zone>{}, "");
static_assert( is_nothrow_move_constructible<Zone>{}, ""); static_assert( is_nothrow_move_constructible<time_zone>{}, "");
static_assert( is_nothrow_move_assignable<Zone>{}, ""); static_assert( is_nothrow_move_assignable<time_zone>{}, "");
} }

206
tz.cpp
View File

@ -20,6 +20,10 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
//
// Our apologies. When the previous paragraph was written, lowercase had not yet
// been invented (that woud involve another several millennia of evolution).
// We did not mean to shout.
#include "tz_private.h" #include "tz_private.h"
@ -115,24 +119,16 @@ static const std::vector<std::string> files =
CONSTDATA auto min_year = date::year::min(); CONSTDATA auto min_year = date::year::min();
CONSTDATA auto max_year = date::year::max(); CONSTDATA auto max_year = date::year::max();
// Arbitrary day of the year that will be away from any limits. CONSTDATA auto min_day = date::jan/1;
// Used with year::min() and year::max(). CONSTDATA auto max_day = date::dec/31;
CONSTDATA auto boring_day = date::aug/18;
// +-------------------+ // +-------------------+
// | End Configuration | // | End Configuration |
// +-------------------+ // +-------------------+
#if _MSC_VER && ! defined(__clang__) && ! defined( __GNUG__) #ifndef _MSC_VER
// We can't use static_assert here for MSVC (yet) because
// the expression isn't constexpr in MSVC yet.
// FIXME! Remove this when MSVC's constexpr support improves.
#else
static_assert(min_year <= max_year, "Configuration error"); static_assert(min_year <= max_year, "Configuration error");
#endif #endif
#if __cplusplus >= 201402
static_assert(boring_day.ok(), "Configuration error");
#endif
// Until filesystem arrives. // Until filesystem arrives.
static CONSTDATA char folder_delimiter = static CONSTDATA char folder_delimiter =
@ -284,7 +280,7 @@ static inline size_t countof(T(&arr)[N])
// The routine tries to load as many time zone entries as possible despite errors. // The routine tries to load as many time zone entries as possible despite errors.
// We don't want to fail to load the whole database just because one record can't be read. // We don't want to fail to load the whole database just because one record can't be read.
static void get_windows_timezone_info(std::vector<timezone_info>& tz_list) static void get_windows_timezone_info(std::vector<detail::timezone_info>& tz_list)
{ {
tz_list.clear(); tz_list.clear();
LONG result; LONG result;
@ -309,7 +305,7 @@ static void get_windows_timezone_info(std::vector<timezone_info>& tz_list)
std::wstring full_zone_key_name; std::wstring full_zone_key_name;
for (DWORD zone_index = 0; ; ++zone_index) for (DWORD zone_index = 0; ; ++zone_index)
{ {
timezone_info tz; detail::timezone_info tz;
size = (DWORD) sizeof(zone_key_name)/sizeof(zone_key_name[0]); size = (DWORD) sizeof(zone_key_name)/sizeof(zone_key_name[0]);
auto status = RegEnumKeyExW(zones_key.handle(), zone_index, zone_key_name, &size, auto status = RegEnumKeyExW(zones_key.handle(), zone_index, zone_key_name, &size,
@ -360,7 +356,7 @@ static void get_windows_timezone_info(std::vector<timezone_info>& tz_list)
// under the windows registry key Time Zones. // under the windows registry key Time Zones.
// To be clear, standard_name does NOT represent a windows timezone id // To be clear, standard_name does NOT represent a windows timezone id
// or an IANA tzid // or an IANA tzid
static const timezone_info* find_native_timezone_by_standard_name( static const detail::timezone_info* find_native_timezone_by_standard_name(
const std::string& standard_name) const std::string& standard_name)
{ {
// TODO! we can improve on linear search. // TODO! we can improve on linear search.
@ -377,11 +373,11 @@ static const timezone_info* find_native_timezone_by_standard_name(
// Read CSV file of "other","territory","type". // Read CSV file of "other","territory","type".
// See timezone_mapping structure for more info. // See timezone_mapping structure for more info.
// This function should be kept in sync with the code that writes this file. // This function should be kept in sync with the code that writes this file.
static std::vector<timezone_mapping> static std::vector<detail::timezone_mapping>
load_timezone_mappings_from_csv_file(const std::string& input_path) load_timezone_mappings_from_csv_file(const std::string& input_path)
{ {
size_t line = 1; size_t line = 1;
std::vector<timezone_mapping> mappings; std::vector<detail::timezone_mapping> mappings;
std::ifstream is(input_path, std::ios_base::in | std::ios_base::binary); std::ifstream is(input_path, std::ios_base::in | std::ios_base::binary);
if (!is.is_open()) if (!is.is_open())
{ {
@ -412,7 +408,7 @@ load_timezone_mappings_from_csv_file(const std::string& input_path)
for (;;) for (;;)
{ {
timezone_mapping zm{}; detail::timezone_mapping zm{};
char ch; char ch;
is.read(&ch, 1); is.read(&ch, 1);
@ -549,7 +545,7 @@ parse_signed_time(std::istream& in)
// MonthDayTime // MonthDayTime
MonthDayTime::MonthDayTime(second_point tp, tz timezone) MonthDayTime::MonthDayTime(local_seconds tp, tz timezone)
: zone_(timezone) : zone_(timezone)
{ {
using namespace date; using namespace date;
@ -606,8 +602,8 @@ MonthDayTime::compare(date::year y, const MonthDayTime& x, date::year yx,
{ {
if (zone_ != x.zone_) if (zone_ != x.zone_)
{ {
auto dp0 = to_day_point(y); auto dp0 = to_sys_days(y);
auto dp1 = x.to_day_point(yx); auto dp1 = x.to_sys_days(yx);
if (std::abs((dp0-dp1).count()) > 1) if (std::abs((dp0-dp1).count()) > 1)
return dp0 < dp1 ? -1 : 1; return dp0 < dp1 ? -1 : 1;
if (zone_ == tz::local) if (zone_ == tz::local)
@ -642,7 +638,7 @@ MonthDayTime::compare(date::year y, const MonthDayTime& x, date::year yx,
return t0 < t1 ? -1 : t0 == t1 ? 0 : 1; return t0 < t1 ? -1 : t0 == t1 ? 0 : 1;
} }
second_point sys_seconds
MonthDayTime::to_sys(date::year y, std::chrono::seconds offset, MonthDayTime::to_sys(date::year y, std::chrono::seconds offset,
std::chrono::seconds save) const std::chrono::seconds save) const
{ {
@ -677,23 +673,23 @@ MonthDayTime::U::operator=(const pair& x)
return *this; return *this;
} }
date::day_point date::sys_days
MonthDayTime::to_day_point(date::year y) const MonthDayTime::to_sys_days(date::year y) const
{ {
using namespace std::chrono; using namespace std::chrono;
using namespace date; using namespace date;
switch (type_) switch (type_)
{ {
case month_day: case month_day:
return day_point(y/u.month_day_); return sys_days(y/u.month_day_);
case month_last_dow: case month_last_dow:
return day_point(y/u.month_weekday_last_); return sys_days(y/u.month_weekday_last_);
case lteq: case lteq:
{ {
auto const x = y/u.month_day_weekday_.month_day_; auto const x = y/u.month_day_weekday_.month_day_;
auto const wd1 = weekday(x); auto const wd1 = weekday(x);
auto const wd0 = u.month_day_weekday_.weekday_; auto const wd0 = u.month_day_weekday_.weekday_;
return day_point(x) - (wd1-wd0); return sys_days(x) - (wd1-wd0);
} }
case gteq: case gteq:
break; break;
@ -701,13 +697,13 @@ MonthDayTime::to_day_point(date::year y) const
auto const x = y/u.month_day_weekday_.month_day_; auto const x = y/u.month_day_weekday_.month_day_;
auto const wd1 = u.month_day_weekday_.weekday_; auto const wd1 = u.month_day_weekday_.weekday_;
auto const wd0 = weekday(x); auto const wd0 = weekday(x);
return day_point(x) + (wd1-wd0); return sys_days(x) + (wd1-wd0);
} }
second_point sys_seconds
MonthDayTime::to_time_point(date::year y) const MonthDayTime::to_time_point(date::year y) const
{ {
return to_day_point(y) + h_ + m_ + s_; return to_sys_days(y) + h_ + m_ + s_;
} }
void void
@ -721,7 +717,7 @@ MonthDayTime::canonicalize(date::year y)
return; return;
case month_last_dow: case month_last_dow:
{ {
auto const ymd = year_month_day(y/u.month_weekday_last_); auto const ymd = year_month_day(sys_days{y/u.month_weekday_last_});
u.month_day_ = ymd.month()/ymd.day(); u.month_day_ = ymd.month()/ymd.day();
type_ = month_day; type_ = month_day;
return; return;
@ -731,7 +727,7 @@ MonthDayTime::canonicalize(date::year y)
auto const x = y/u.month_day_weekday_.month_day_; auto const x = y/u.month_day_weekday_.month_day_;
auto const wd1 = weekday(x); auto const wd1 = weekday(x);
auto const wd0 = u.month_day_weekday_.weekday_; auto const wd0 = u.month_day_weekday_.weekday_;
auto const ymd = year_month_day(day_point(x) - (wd1-wd0)); auto const ymd = year_month_day(sys_days(x) - (wd1-wd0));
u.month_day_ = ymd.month()/ymd.day(); u.month_day_ = ymd.month()/ymd.day();
type_ = month_day; type_ = month_day;
return; return;
@ -741,7 +737,7 @@ MonthDayTime::canonicalize(date::year y)
auto const x = y/u.month_day_weekday_.month_day_; auto const x = y/u.month_day_weekday_.month_day_;
auto const wd1 = u.month_day_weekday_.weekday_; auto const wd1 = u.month_day_weekday_.weekday_;
auto const wd0 = weekday(x); auto const wd0 = weekday(x);
auto const ymd = year_month_day(day_point(x) + (wd1-wd0)); auto const ymd = year_month_day(sys_days(x) + (wd1-wd0));
u.month_day_ = ymd.month()/ymd.day(); u.month_day_ = ymd.month()/ymd.day();
type_ = month_day; type_ = month_day;
return; return;
@ -1218,9 +1214,9 @@ Rule::split_overlaps(std::vector<Rule>& rules)
rules.shrink_to_fit(); rules.shrink_to_fit();
} }
// Zone // time_zone
Zone::zonelet::~zonelet() time_zone::zonelet::~zonelet()
{ {
#if !defined(_MSC_VER) || (_MSC_VER >= 1900) #if !defined(_MSC_VER) || (_MSC_VER >= 1900)
using minutes = std::chrono::minutes; using minutes = std::chrono::minutes;
@ -1232,14 +1228,14 @@ Zone::zonelet::~zonelet()
#endif #endif
} }
Zone::zonelet::zonelet() time_zone::zonelet::zonelet()
{ {
#if !defined(_MSC_VER) || (_MSC_VER >= 1900) #if !defined(_MSC_VER) || (_MSC_VER >= 1900)
::new(&u.rule_) std::string(); ::new(&u.rule_) std::string();
#endif #endif
} }
Zone::zonelet::zonelet(const zonelet& i) time_zone::zonelet::zonelet(const zonelet& i)
: gmtoff_(i.gmtoff_) : gmtoff_(i.gmtoff_)
, tag_(i.tag_) , tag_(i.tag_)
, format_(i.format_) , format_(i.format_)
@ -1266,7 +1262,7 @@ Zone::zonelet::zonelet(const zonelet& i)
#endif #endif
} }
Zone::Zone(const std::string& s) time_zone::time_zone(const std::string& s)
#if LAZY_INIT #if LAZY_INIT
: adjusted_(new std::once_flag{}) : adjusted_(new std::once_flag{})
#endif #endif
@ -1290,7 +1286,7 @@ Zone::Zone(const std::string& s)
} }
void void
Zone::add(const std::string& s) time_zone::add(const std::string& s)
{ {
try try
{ {
@ -1310,7 +1306,7 @@ Zone::add(const std::string& s)
} }
void void
Zone::parse_info(std::istream& in) time_zone::parse_info(std::istream& in)
{ {
using namespace date; using namespace date;
using namespace std::chrono; using namespace std::chrono;
@ -1326,7 +1322,7 @@ Zone::parse_info(std::istream& in)
if (in.eof() || in.peek() == '#') if (in.eof() || in.peek() == '#')
{ {
zonelet.until_year_ = year::max(); zonelet.until_year_ = year::max();
zonelet.until_date_ = MonthDayTime(boring_day, tz::utc); zonelet.until_date_ = MonthDayTime(max_day, tz::utc);
} }
else else
{ {
@ -1489,8 +1485,9 @@ find_rule_for_zone(const std::pair<const Rule*, const Rule*>& eqr,
static static
std::pair<const Rule*, date::year> std::pair<const Rule*, date::year>
find_rule_for_zone(const std::pair<const Rule*, const Rule*>& eqr, find_rule_for_zone(const std::pair<const Rule*, const Rule*>& eqr,
const second_point& tp_utc, const second_point& tp_std, const sys_seconds& tp_utc,
const second_point& tp_loc) const local_seconds& tp_std,
const local_seconds& tp_loc)
{ {
using namespace std::chrono; using namespace std::chrono;
using namespace date; using namespace date;
@ -1508,10 +1505,10 @@ find_rule_for_zone(const std::pair<const Rule*, const Rule*>& eqr,
found = tp_utc < r->mdt().to_time_point(ry); found = tp_utc < r->mdt().to_time_point(ry);
break; break;
case tz::standard: case tz::standard:
found = tp_std < r->mdt().to_time_point(ry); found = sys_seconds{tp_std.time_since_epoch()} < r->mdt().to_time_point(ry);
break; break;
case tz::local: case tz::local:
found = tp_loc < r->mdt().to_time_point(ry); found = sys_seconds{tp_loc.time_since_epoch()} < r->mdt().to_time_point(ry);
break; break;
} }
if (found) if (found)
@ -1525,7 +1522,7 @@ find_rule_for_zone(const std::pair<const Rule*, const Rule*>& eqr,
} }
static static
Info sys_info
find_rule(const std::pair<const Rule*, date::year>& first_rule, find_rule(const std::pair<const Rule*, date::year>& first_rule,
const std::pair<const Rule*, date::year>& last_rule, const std::pair<const Rule*, date::year>& last_rule,
const date::year& y, const std::chrono::seconds& offset, const date::year& y, const std::chrono::seconds& offset,
@ -1536,8 +1533,8 @@ find_rule(const std::pair<const Rule*, date::year>& first_rule,
using namespace date; using namespace date;
auto r = first_rule.first; auto r = first_rule.first;
auto ry = first_rule.second; auto ry = first_rule.second;
Info x{day_point(year::min()/boring_day), day_point(year::max()/boring_day), sys_info x{sys_days(year::min()/min_day), sys_days(year::max()/max_day),
seconds{0}, initial_save, initial_abbrev}; seconds{0}, initial_save, initial_abbrev};
while (r != nullptr) while (r != nullptr)
{ {
auto tr = r->mdt().to_sys(ry, offset, x.save); auto tr = r->mdt().to_sys(ry, offset, x.save);
@ -1569,7 +1566,7 @@ find_rule(const std::pair<const Rule*, date::year>& first_rule,
x.end = r->mdt().to_sys(ry, offset, x.save); x.end = r->mdt().to_sys(ry, offset, x.save);
} }
else else
x.end = day_point(year::max()/boring_day); x.end = sys_days(year::max()/max_day);
break; break;
} }
x.save = r->save(); x.save = r->save();
@ -1580,7 +1577,7 @@ find_rule(const std::pair<const Rule*, date::year>& first_rule,
} }
void void
Zone::adjust_infos(const std::vector<Rule>& rules) time_zone::adjust_infos(const std::vector<Rule>& rules)
{ {
using namespace std::chrono; using namespace std::chrono;
using namespace date; using namespace date;
@ -1640,7 +1637,7 @@ Zone::adjust_infos(const std::vector<Rule>& rules)
final_save = z.last_rule_.first->save(); final_save = z.last_rule_.first->save();
} }
z.until_utc_ = z.until_date_.to_sys(z.until_year_, z.gmtoff_, final_save); z.until_utc_ = z.until_date_.to_sys(z.until_year_, z.gmtoff_, final_save);
z.until_std_ = z.until_utc_ + z.gmtoff_; z.until_std_ = local_seconds{z.until_utc_.time_since_epoch()} + z.gmtoff_;
z.until_loc_ = z.until_std_ + final_save; z.until_loc_ = z.until_std_ + final_save;
if (z.tag_ == zonelet::has_rule) if (z.tag_ == zonelet::has_rule)
@ -1750,31 +1747,64 @@ format_abbrev(std::string format, const std::string& variable, std::chrono::seco
return format; return format;
} }
Info sys_info
Zone::get_info(std::chrono::system_clock::time_point tp, tz timezone) const time_zone::get_info_impl(sys_seconds tp) const
{
return get_info_impl(tp, static_cast<int>(tz::utc));
}
local_info
time_zone::get_info_impl(local_seconds tp) const
{
using namespace std::chrono;
local_info i{};
i.first = get_info_impl(sys_seconds{tp.time_since_epoch()}, static_cast<int>(tz::local));
auto tps = sys_seconds{(tp - i.first.offset).time_since_epoch()};
if (tps < i.first.begin)
{
i.second = std::move(i.first);
i.first = get_info_impl(i.second.begin - seconds{1}, static_cast<int>(tz::utc));
i.result = local_info::nonexistent;
}
else if (i.first.end - tps <= days{1})
{
i.second = get_info_impl(i.first.end, static_cast<int>(tz::utc));
tps = sys_seconds{(tp - i.second.offset).time_since_epoch()};
if (tps >= i.second.begin)
i.result = local_info::ambiguous;
else
i.second = {};
}
return i;
}
sys_info
time_zone::get_info_impl(sys_seconds tp, int tz_int) const
{ {
using namespace std::chrono; using namespace std::chrono;
using namespace date; using namespace date;
tz timezone = static_cast<tz>(tz_int);
assert(timezone != tz::standard); assert(timezone != tz::standard);
auto y = year_month_day(floor<days>(tp)).year(); auto y = year_month_day(floor<days>(tp)).year();
if (y < min_year || y > max_year) if (y < min_year || y > max_year)
throw std::runtime_error("The year " + std::to_string(static_cast<int>(y)) + throw std::runtime_error("The year " + std::to_string(static_cast<int>(y)) +
" is out of range:[" + std::to_string(static_cast<int>(min_year)) + ", " " is out of range:[" + std::to_string(static_cast<int>(min_year)) + ", "
+ std::to_string(static_cast<int>(max_year)) + "]"); + std::to_string(static_cast<int>(max_year)) + "]");
auto tps = floor<seconds>(tp);
#if LAZY_INIT #if LAZY_INIT
std::call_once(*adjusted_, [this]() std::call_once(*adjusted_,
{ [this]()
const_cast<Zone*>(this)->adjust_infos(get_tzdb().rules); {
}); const_cast<time_zone*>(this)->adjust_infos(get_tzdb().rules);
});
#endif #endif
auto i = std::upper_bound(zonelets_.begin(), zonelets_.end(), tps, auto i = std::upper_bound(zonelets_.begin(), zonelets_.end(), tp,
[timezone](second_point t, const zonelet& zl) [timezone](sys_seconds t, const zonelet& zl)
{ {
return timezone == tz::utc ? t < zl.until_utc_ : t < zl.until_loc_; return timezone == tz::utc ? t < zl.until_utc_ :
t < sys_seconds{zl.until_loc_.time_since_epoch()};
}); });
Info r{}; sys_info r{};
if (i != zonelets_.end()) if (i != zonelets_.end())
{ {
if (i->tag_ == zonelet::has_save) if (i->tag_ == zonelet::has_save)
@ -1782,7 +1812,7 @@ Zone::get_info(std::chrono::system_clock::time_point tp, tz timezone) const
if (i != zonelets_.begin()) if (i != zonelets_.begin())
r.begin = i[-1].until_utc_; r.begin = i[-1].until_utc_;
else else
r.begin = day_point(year::min()/boring_day); r.begin = sys_days(year::min()/min_day);
r.end = i->until_utc_; r.end = i->until_utc_;
r.offset = i->gmtoff_ + i->u.save_; r.offset = i->gmtoff_ + i->u.save_;
r.save = i->u.save_; r.save = i->u.save_;
@ -1792,15 +1822,15 @@ Zone::get_info(std::chrono::system_clock::time_point tp, tz timezone) const
if (i != zonelets_.begin()) if (i != zonelets_.begin())
r.begin = i[-1].until_utc_; r.begin = i[-1].until_utc_;
else else
r.begin = day_point(year::min()/boring_day); r.begin = sys_days(year::min()/min_day);
r.end = i->until_utc_; r.end = i->until_utc_;
r.offset = i->gmtoff_; r.offset = i->gmtoff_;
} }
else else
{ {
r = find_rule(i->first_rule_, i->last_rule_, y, i->gmtoff_, r = find_rule(i->first_rule_, i->last_rule_, y, i->gmtoff_,
MonthDayTime(tps, timezone), i->initial_save_, MonthDayTime(local_seconds{tp.time_since_epoch()}, timezone),
i->initial_abbrev_); i->initial_save_, i->initial_abbrev_);
r.offset = i->gmtoff_ + r.save; r.offset = i->gmtoff_ + r.save;
if (i != zonelets_.begin() && r.begin < i[-1].until_utc_) if (i != zonelets_.begin() && r.begin < i[-1].until_utc_)
r.begin = i[-1].until_utc_; r.begin = i[-1].until_utc_;
@ -1814,7 +1844,7 @@ Zone::get_info(std::chrono::system_clock::time_point tp, tz timezone) const
} }
std::ostream& std::ostream&
operator<<(std::ostream& os, const Zone& z) operator<<(std::ostream& os, const time_zone& z)
{ {
using namespace date; using namespace date;
using namespace std::chrono; using namespace std::chrono;
@ -1822,10 +1852,11 @@ operator<<(std::ostream& os, const Zone& z)
os.fill(' '); os.fill(' ');
os.flags(std::ios::dec | std::ios::left); os.flags(std::ios::dec | std::ios::left);
#if LAZY_INIT #if LAZY_INIT
std::call_once(*z.adjusted_, [&z]() std::call_once(*z.adjusted_,
{ [&z]()
const_cast<Zone&>(z).adjust_infos(get_tzdb().rules); {
}); const_cast<time_zone&>(z).adjust_infos(get_tzdb().rules);
});
#endif #endif
os.width(35); os.width(35);
os << z.name_; os << z.name_;
@ -1837,7 +1868,7 @@ operator<<(std::ostream& os, const Zone& z)
os << ' '; os << ' ';
os << make_time(s.gmtoff_) << " "; os << make_time(s.gmtoff_) << " ";
os.width(15); os.width(15);
if (s.tag_ != Zone::zonelet::has_save) if (s.tag_ != time_zone::zonelet::has_save)
os << s.u.rule_; os << s.u.rule_;
else else
{ {
@ -2120,7 +2151,7 @@ init_tzdb()
} }
else if (word == "Zone") else if (word == "Zone")
{ {
db.zones.push_back(Zone(line)); db.zones.push_back(time_zone(line));
continue_zone = true; continue_zone = true;
} }
else if (line[0] == '\t' && continue_zone) else if (line[0] == '\t' && continue_zone)
@ -2182,12 +2213,12 @@ get_tzdb()
return ref; return ref;
} }
const Zone* const time_zone*
locate_zone(const std::string& tz_name) locate_zone(const std::string& tz_name)
{ {
const auto& db = get_tzdb(); const auto& db = get_tzdb();
auto zi = std::lower_bound(db.zones.begin(), db.zones.end(), tz_name, auto zi = std::lower_bound(db.zones.begin(), db.zones.end(), tz_name,
[](const Zone& z, const std::string& nm) [](const time_zone& z, const std::string& nm)
{ {
return z.name() < nm; return z.name() < nm;
}); });
@ -2201,7 +2232,7 @@ locate_zone(const std::string& tz_name)
if (li != db.links.end() && li->name() == tz_name) if (li != db.links.end() && li->name() == tz_name)
{ {
zi = std::lower_bound(db.zones.begin(), db.zones.end(), li->target(), zi = std::lower_bound(db.zones.begin(), db.zones.end(), li->target(),
[](const Zone& z, const std::string& nm) [](const time_zone& z, const std::string& nm)
{ {
return z.name() < nm; return z.name() < nm;
}); });
@ -2215,7 +2246,7 @@ locate_zone(const std::string& tz_name)
#ifdef TZ_TEST #ifdef TZ_TEST
#ifdef _WIN32 #ifdef _WIN32
const Zone* const time_zone*
locate_native_zone(const std::string& native_tz_name) locate_native_zone(const std::string& native_tz_name)
{ {
std::string standard_tz_name; std::string standard_tz_name;
@ -2292,9 +2323,8 @@ operator<<(std::ostream& os, const TZ_DB& db)
// ----------------------- // -----------------------
std::ostream& std::ostream&
operator<<(std::ostream& os, const Info& r) operator<<(std::ostream& os, const sys_info& r)
{ {
using namespace date;
os << r.begin << '\n'; os << r.begin << '\n';
os << r.end << '\n'; os << r.end << '\n';
os << make_time(r.offset) << "\n"; os << make_time(r.offset) << "\n";
@ -2303,9 +2333,25 @@ operator<<(std::ostream& os, const Info& r)
return os; return os;
} }
std::ostream&
operator<<(std::ostream& os, const local_info& r)
{
if (r.result == local_info::nonexistent)
os << "nonexistent between\n";
else if (r.result == local_info::ambiguous)
os << "ambiguous between\n";
os << r.first;
if (r.result != local_info::unique)
{
os << "and\n";
os << r.second;
}
return os;
}
#ifdef _WIN32 #ifdef _WIN32
const Zone* const time_zone*
current_zone() current_zone()
{ {
#if TIMEZONE_MAPPING #if TIMEZONE_MAPPING
@ -2346,7 +2392,7 @@ current_zone()
#else // ! WIN32 #else // ! WIN32
const Zone* const time_zone*
current_zone() current_zone()
{ {
// On some versions of some linux distro's (e.g. Ubuntu), // On some versions of some linux distro's (e.g. Ubuntu),

961
tz.h

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
// The MIT License (MIT) // The MIT License (MIT)
// //
// Copyright (c) 2015 Howard Hinnant // Copyright (c) 2015, 2016 Howard Hinnant
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@ -22,12 +22,18 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
//
// Our apologies. When the previous paragraph was written, lowercase had not yet
// been invented (that woud involve another several millennia of evolution).
// We did not mean to shout.
#include "tz.h" #include "tz.h"
namespace date namespace date
{ {
enum class tz {utc, local, standard};
class MonthDayTime class MonthDayTime
{ {
private: private:
@ -80,7 +86,7 @@ private:
public: public:
MonthDayTime() = default; MonthDayTime() = default;
MonthDayTime(second_point tp, tz timezone); MonthDayTime(local_seconds tp, tz timezone);
MonthDayTime(const date::month_day& md, tz timezone); MonthDayTime(const date::month_day& md, tz timezone);
date::day day() const; date::day day() const;
@ -89,11 +95,11 @@ public:
void canonicalize(date::year y); void canonicalize(date::year y);
second_point sys_seconds
to_sys(date::year y, std::chrono::seconds offset, std::chrono::seconds save) const; to_sys(date::year y, std::chrono::seconds offset, std::chrono::seconds save) const;
date::day_point to_day_point(date::year y) const; sys_days to_sys_days(date::year y) const;
second_point to_time_point(date::year y) const; sys_seconds to_time_point(date::year y) const;
int compare(date::year y, const MonthDayTime& x, date::year yx, int compare(date::year y, const MonthDayTime& x, date::year yx,
std::chrono::seconds offset, std::chrono::minutes prev_save) const; std::chrono::seconds offset, std::chrono::minutes prev_save) const;
@ -181,7 +187,7 @@ inline bool operator> (const std::string& x, const Rule& y) {return y < x;}
inline bool operator<=(const std::string& x, const Rule& y) {return !(y < x);} inline bool operator<=(const std::string& x, const Rule& y) {return !(y < x);}
inline bool operator>=(const std::string& x, const Rule& y) {return !(x < y);} inline bool operator>=(const std::string& x, const Rule& y) {return !(x < y);}
struct Zone::zonelet struct time_zone::zonelet
{ {
enum tag {has_rule, has_save, is_empty}; enum tag {has_rule, has_save, is_empty};
@ -203,14 +209,14 @@ struct Zone::zonelet
U& operator=(const U&) = delete; U& operator=(const U&) = delete;
} u; } u;
std::string format_; std::string format_;
date::year until_year_{0}; date::year until_year_{0};
MonthDayTime until_date_; MonthDayTime until_date_;
second_point until_utc_; sys_seconds until_utc_;
second_point until_std_; local_seconds until_std_;
second_point until_loc_; local_seconds until_loc_;
std::chrono::minutes initial_save_{}; std::chrono::minutes initial_save_{};
std::string initial_abbrev_; std::string initial_abbrev_;
std::pair<const Rule*, date::year> first_rule_{nullptr, date::year::min()}; std::pair<const Rule*, date::year> first_rule_{nullptr, date::year::min()};
std::pair<const Rule*, date::year> last_rule_{nullptr, date::year::max()}; std::pair<const Rule*, date::year> last_rule_{nullptr, date::year::max()};