Initial commit

This commit is contained in:
Howard Hinnant
2015-07-17 22:30:53 -04:00
commit 796448e4ad
4 changed files with 5756 additions and 0 deletions
+3405
View File
File diff suppressed because it is too large Load Diff
+1622
View File
File diff suppressed because it is too large Load Diff
+550
View File
@@ -0,0 +1,550 @@
#ifndef TZ_H
#define TZ_H
// Howard Hinnant
// This work is licensed under a Creative Commons Attribution 4.0 International License.
// http://creativecommons.org/licenses/by/4.0/
// Get more recent database at http://www.iana.org/time-zones
// Questions:
// 1. Reload database.
// 4. Is the utc to sys renaming complete? Was it done correctly?
#include "date.h"
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cstdint>
#include <istream>
#include <ostream>
#include <ratio>
#include <sstream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace date
{
using seconds_point = std::chrono::time_point<std::chrono::system_clock,
std::chrono::seconds>;
enum class tz {utc, local, standard};
enum class choose {earliest, latest};
class nonexistent_local_time
: public std::runtime_error
{
public:
template <class Rep, class Period>
nonexistent_local_time(std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp,
seconds_point first, const std::string& first_abrev,
seconds_point last, const std::string& last_abrev,
seconds_point time_sys);
private:
template <class Rep, class Period>
static
std::string
make_msg(std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp,
seconds_point first, const std::string& first_abrev,
seconds_point last, const std::string& last_abrev,
seconds_point time_sys);
};
template <class Rep, class Period>
inline
nonexistent_local_time::nonexistent_local_time(
std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp,
seconds_point first, const std::string& first_abrev,
seconds_point last, const std::string& last_abrev,
seconds_point time_sys)
: std::runtime_error(make_msg(tp, first, first_abrev, last, last_abrev, time_sys))
{}
template <class Rep, class Period>
std::string
nonexistent_local_time::make_msg(std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp,
seconds_point first, const std::string& first_abrev,
seconds_point last, const std::string& last_abrev,
seconds_point time_sys)
{
using namespace date;
std::ostringstream os;
os << tp << " is in a gap between\n"
<< first << ' ' << first_abrev << " and\n"
<< last << ' ' << last_abrev
<< " which are both equivalent to\n"
<< time_sys << " UTC";
return os.str();
}
class ambiguous_local_time
: public std::runtime_error
{
public:
template <class Rep, class Period>
ambiguous_local_time(std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp,
std::chrono::seconds first_offset,
const std::string& first_abrev,
std::chrono::seconds second_offset,
const std::string& second_abrev);
private:
template <class Rep, class Period>
static
std::string
make_msg(std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp,
std::chrono::seconds first_offset, const std::string& first_abrev,
std::chrono::seconds second_offset, const std::string& second_abrev);
};
template <class Rep, class Period>
inline
ambiguous_local_time::ambiguous_local_time(
std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp,
std::chrono::seconds first_offset,
const std::string& first_abrev,
std::chrono::seconds second_offset,
const std::string& second_abrev)
: std::runtime_error(make_msg(tp, first_offset, first_abrev, second_offset,
second_abrev))
{}
template <class Rep, class Period>
std::string
ambiguous_local_time::make_msg(std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp,
std::chrono::seconds first_offset,
const std::string& first_abrev,
std::chrono::seconds second_offset,
const std::string& second_abrev)
{
using namespace date;
std::ostringstream os;
os << tp << " is ambiguous. It could be\n"
<< tp << ' ' << first_abrev << " == " << tp - first_offset << " UTC or\n"
<< tp << ' ' << second_abrev << " == " << tp - second_offset << " UTC";
return os.str();
}
class Rule;
struct Info
{
seconds_point begin;
seconds_point end;
std::chrono::seconds offset;
std::chrono::minutes save;
std::string abbrev;
};
std::ostream&
operator<<(std::ostream& os, const Info& r);
class Zone
{
private:
struct zonelet;
std::string name_;
std::vector<zonelet> zonelets_;
public:
explicit Zone(const std::string& s);
const std::string& name() const {return name_;}
Info get_info(std::chrono::system_clock::time_point tp, tz timezone) const;
template <class Rep, class Period>
Info
get_info(std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp,
tz timezone) const
{
using namespace std::chrono;
return get_info(floor<system_clock::duration>(tp), timezone);
}
template <class Rep, class Period>
std::chrono::time_point<std::chrono::system_clock,
typename std::common_type<std::chrono::duration<Rep, Period>,
std::chrono::seconds>::type>
to_sys(std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp) const;
template <class Rep, class Period>
std::chrono::time_point<std::chrono::system_clock,
typename std::common_type<std::chrono::duration<Rep, Period>,
std::chrono::seconds>::type>
to_sys(std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp,
choose z) const;
template <class Rep, class Period>
std::pair
<
std::chrono::time_point<std::chrono::system_clock,
typename std::common_type<std::chrono::duration<Rep, Period>,
std::chrono::seconds>::type>,
std::string
>
to_local(std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp) const;
friend bool operator==(const Zone& x, const Zone& y);
friend bool operator< (const Zone& x, const Zone& y);
friend std::ostream& operator<<(std::ostream& os, const Zone& z);
void add(const std::string& s);
void adjust_infos(const std::vector<Rule>& rules);
private:
void parse_info(std::istream& in);
template <class Rep, class Period, bool b>
std::chrono::time_point<std::chrono::system_clock,
typename std::common_type<std::chrono::duration<Rep, Period>,
std::chrono::seconds>::type>
to_sys_impl(std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp,
choose z, std::integral_constant<bool, b> do_throw) const;
};
template <class Rep, class Period>
inline
std::chrono::time_point<std::chrono::system_clock,
typename std::common_type<std::chrono::duration<Rep, Period>,
std::chrono::seconds>::type>
Zone::to_sys(std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp) const
{
return to_sys_impl(tp, choose{}, std::true_type{});
}
template <class Rep, class Period>
inline
std::chrono::time_point<std::chrono::system_clock,
typename std::common_type<std::chrono::duration<Rep, Period>,
std::chrono::seconds>::type>
Zone::to_sys(std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp, choose z) const
{
return to_sys_impl(tp, z, std::false_type{});
}
template <class Rep, class Period>
inline
std::pair
<
std::chrono::time_point<std::chrono::system_clock,
typename std::common_type<std::chrono::duration<Rep, Period>,
std::chrono::seconds>::type>,
std::string
>
Zone::to_local(std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp) const
{
auto const i = get_info(tp, tz::utc);
return {tp + i.offset, i.abbrev};
}
inline bool operator==(const Zone& x, const Zone& y) {return x.name_ == y.name_;}
inline bool operator< (const Zone& x, const Zone& y) {return x.name_ < y.name_;}
inline bool operator!=(const Zone& x, const Zone& y) {return !(x == y);}
inline bool operator> (const Zone& x, const Zone& y) {return y < x;}
inline bool operator<=(const Zone& x, const Zone& y) {return !(y < x);}
inline bool operator>=(const Zone& x, const Zone& y) {return !(x < y);}
template <class Rep, class Period, bool b>
std::chrono::time_point<std::chrono::system_clock,
typename std::common_type<std::chrono::duration<Rep, Period>,
std::chrono::seconds>::type>
Zone::to_sys_impl(std::chrono::time_point<std::chrono::system_clock,
std::chrono::duration<Rep, Period>> tp,
choose z, std::integral_constant<bool, b> do_throw) const
{
using namespace date;
using namespace std::chrono;
auto i = get_info(tp, tz::local);
auto tp_sys = tp - i.offset;
if (tp_sys - i.begin <= days{1})
{
if (tp < i.begin + i.offset)
{
if (do_throw)
{
auto prev = get_info(i.begin - seconds{1}, tz::utc);
throw nonexistent_local_time(tp, i.begin + prev.offset, prev.abbrev,
i.begin + i.offset, i.abbrev, i.begin);
}
return i.begin;
}
assert(tp >= i.begin + get_info(i.begin - seconds{1}, tz::utc).offset);
}
if (i.end - tp_sys <= days{1})
{
assert(tp < i.end + i.offset);
auto next = get_info(i.end, tz::utc);
if (tp >= i.end + next.offset)
{
if (do_throw)
throw ambiguous_local_time(tp, i.offset, i.abbrev,
next.offset, next.abbrev);
if (z == choose::earliest)
return tp_sys;
return tp - next.offset;
}
}
return tp_sys;
}
class Link
{
private:
std::string name_;
std::string target_;
public:
explicit Link(const std::string& s);
const std::string& name() const {return name_;}
const std::string& target() const {return target_;}
friend bool operator==(const Link& x, const Link& y) {return x.name_ == y.name_;}
friend bool operator< (const Link& x, const Link& y) {return x.name_ < y.name_;}
friend std::ostream& operator<<(std::ostream& os, const Link& x);
};
inline bool operator!=(const Link& x, const Link& y) {return !(x == y);}
inline bool operator> (const Link& x, const Link& y) {return y < x;}
inline bool operator<=(const Link& x, const Link& y) {return !(y < x);}
inline bool operator>=(const Link& x, const Link& y) {return !(x < y);}
class Leap
{
private:
seconds_point date_;
public:
explicit Leap(const std::string& s);
seconds_point date() const {return date_;}
friend bool operator==(const Leap& x, const Leap& y) {return x.date_ == y.date_;}
friend bool operator< (const Leap& x, const Leap& y) {return x.date_ < y.date_;}
template <class Duration>
friend
bool
operator==(const Leap& x,
const std::chrono::time_point<std::chrono::system_clock, Duration>& y)
{
return x.date_ == y;
}
template <class Duration>
friend
bool
operator< (const Leap& x,
const std::chrono::time_point<std::chrono::system_clock, Duration>& y)
{
return x.date_ < y;
}
template <class Duration>
friend
bool
operator< (const std::chrono::time_point<std::chrono::system_clock, Duration>& x,
const Leap& y)
{
return x < y.date_;
}
friend std::ostream& operator<<(std::ostream& os, const Leap& x);
};
inline bool operator!=(const Leap& x, const Leap& y) {return !(x == y);}
inline bool operator> (const Leap& x, const Leap& y) {return y < x;}
inline bool operator<=(const Leap& x, const Leap& y) {return !(y < x);}
inline bool operator>=(const Leap& x, const Leap& y) {return !(x < y);}
template <class Duration>
inline
bool
operator==(const std::chrono::time_point<std::chrono::system_clock, Duration>& x,
const Leap& y)
{
return y == x;
}
template <class Duration>
inline
bool
operator!=(const Leap& x,
const std::chrono::time_point<std::chrono::system_clock, Duration>& y)
{
return !(x == y);
}
template <class Duration>
inline
bool
operator!=(const std::chrono::time_point<std::chrono::system_clock, Duration>& x,
const Leap& y)
{
return !(x == y);
}
template <class Duration>
inline
bool
operator> (const Leap& x,
const std::chrono::time_point<std::chrono::system_clock, Duration>& y)
{
return y < x;
}
template <class Duration>
inline
bool
operator> (const std::chrono::time_point<std::chrono::system_clock, Duration>& x,
const Leap& y)
{
return y < x;
}
template <class Duration>
inline
bool
operator<=(const Leap& x,
const std::chrono::time_point<std::chrono::system_clock, Duration>& y)
{
return !(y < x);
}
template <class Duration>
inline
bool
operator<=(const std::chrono::time_point<std::chrono::system_clock, Duration>& x,
const Leap& y)
{
return !(y < x);
}
template <class Duration>
inline
bool
operator>=(const Leap& x,
const std::chrono::time_point<std::chrono::system_clock, Duration>& y)
{
return !(x < y);
}
template <class Duration>
inline
bool
operator>=(const std::chrono::time_point<std::chrono::system_clock, Duration>& x,
const Leap& y)
{
return !(x < y);
}
struct TZ_DB
{
std::vector<Zone> zones;
std::vector<Link> links;
std::vector<Leap> leaps;
std::vector<Rule> rules;
TZ_DB() = default;
TZ_DB(TZ_DB&&) = default;
TZ_DB& operator=(TZ_DB&&) = default;
};
std::ostream& operator<<(std::ostream& os, const TZ_DB& db);
const TZ_DB& get_tzdb();
const TZ_DB& reload_tzdb();
const TZ_DB& reload_tzdb(const std::string& new_install);
const Zone* locate_zone(const std::string& tz_name);
const Zone* current_timezone();
class utc_clock
{
public:
using duration = std::chrono::system_clock::duration;
using rep = duration::rep;
using period = duration::period;
using time_point = std::chrono::time_point<utc_clock>;
static CONSTDATA bool is_steady = true;
static time_point now() noexcept;
template <class Duration>
static
std::chrono::time_point<utc_clock,
typename std::common_type<Duration, std::chrono::seconds>::type>
sys_to_utc(std::chrono::time_point<std::chrono::system_clock, Duration> t);
template <class Duration>
static
std::chrono::time_point<std::chrono::system_clock,
typename std::common_type<Duration, std::chrono::seconds>::type>
utc_to_sys(std::chrono::time_point<utc_clock, Duration> t);
};
inline
utc_clock::time_point
utc_clock::now() noexcept
{
using namespace std::chrono;
return sys_to_utc(system_clock::now());
}
template <class Duration>
std::chrono::time_point<utc_clock,
typename std::common_type<Duration, std::chrono::seconds>::type>
utc_clock::sys_to_utc(std::chrono::time_point<std::chrono::system_clock, Duration> t)
{
using namespace std::chrono;
using duration = typename std::common_type<Duration, seconds>::type;
using time_point = std::chrono::time_point<utc_clock, duration>;
auto const& leaps = get_tzdb().leaps;
auto const lt = std::upper_bound(leaps.begin(), leaps.end(), t);
return time_point{t.time_since_epoch() + seconds{lt-leaps.begin()}};
}
template <class Duration>
std::chrono::time_point<std::chrono::system_clock,
typename std::common_type<Duration, std::chrono::seconds>::type>
utc_clock::utc_to_sys(std::chrono::time_point<utc_clock, Duration> t)
{
using namespace std::chrono;
using duration = typename std::common_type<Duration, seconds>::type;
using time_point = std::chrono::time_point<system_clock, duration>;
auto const& leaps = get_tzdb().leaps;
auto tp = time_point{t.time_since_epoch()};
auto const lt = std::upper_bound(leaps.begin(), leaps.end(), tp);
tp -= seconds{lt-leaps.begin()};
if (lt != leaps.begin() && tp + seconds{1} < lt[-1])
tp += seconds{1};
return tp;
}
} // namespace date
#endif // TZ_H
+179
View File
@@ -0,0 +1,179 @@
#ifndef TZ_PRIVATE_H
#define TZ_PRIVATE_H
// Howard Hinnant
// This work is licensed under a Creative Commons Attribution 4.0 International License.
// http://creativecommons.org/licenses/by/4.0/
#include "tz.h"
namespace date
{
class MonthDayTime
{
private:
struct pair
{
date::month_day month_day_;
date::weekday weekday_;
};
enum Type {month_day, month_last_dow, lteq, gteq};
Type type_{month_day};
union U
{
date::month_day month_day_;
date::month_weekday_last month_weekday_last_;
pair month_day_weekday_;
U() : month_day_{date::jan/1} {}
U& operator=(const date::month_day& x);
U& operator=(const date::month_weekday_last& x);
U& operator=(const pair& x);
} u;
std::chrono::hours h_{0};
std::chrono::minutes m_{0};
std::chrono::seconds s_{0};
tz zone_{tz::local};
public:
MonthDayTime() = default;
MonthDayTime(seconds_point tp, tz timezone);
MonthDayTime(const date::month_day& md, tz timezone);
date::day day() const;
date::month month() const;
tz zone() const {return zone_;}
void canonicalize(date::year y);
seconds_point
to_sys(date::year y, std::chrono::seconds offset, std::chrono::seconds save) const;
date::day_point to_day_point(date::year y) const;
seconds_point to_time_point(date::year y) const;
int compare(date::year y, const MonthDayTime& x, date::year yx,
std::chrono::seconds offset, std::chrono::minutes prev_save) const;
friend std::istream& operator>>(std::istream& is, MonthDayTime& x);
friend std::ostream& operator<<(std::ostream& os, const MonthDayTime& x);
};
// A Rule specifies one or more set of datetimes without using an offset.
// Multiple dates are specified with multiple years. The years in effect
// go from starting_year_ to ending_year_, inclusive. starting_year_ <=
// ending_year_. save_ is ineffect for times from the specified time
// onward, including the specified time. When the specified time is
// local, it uses the save_ from the chronologically previous Rule, or if
// there is none, 0.
class Rule
{
private:
std::string name_;
date::year starting_year_{0};
date::year ending_year_{0};
MonthDayTime starting_at_;
std::chrono::minutes save_{0};
std::string abbrev_;
public:
Rule() = default;
explicit Rule(const std::string& s);
Rule(const Rule& r, date::year starting_year, date::year ending_year);
const std::string& name() const {return name_;}
const std::string& abbrev() const {return abbrev_;}
const MonthDayTime& mdt() const {return starting_at_;}
const date::year& starting_year() const {return starting_year_;}
const date::year& ending_year() const {return ending_year_;}
const std::chrono::minutes& save() const {return save_;}
static void split_overlaps(std::vector<Rule>& rules);
friend bool operator==(const Rule& x, const Rule& y);
friend bool operator<(const Rule& x, const Rule& y);
friend bool operator==(const Rule& x, const date::year& y);
friend bool operator<(const Rule& x, const date::year& y);
friend bool operator==(const date::year& x, const Rule& y);
friend bool operator<(const date::year& x, const Rule& y);
friend bool operator==(const Rule& x, const std::string& y);
friend bool operator<(const Rule& x, const std::string& y);
friend bool operator==(const std::string& x, const Rule& y);
friend bool operator<(const std::string& x, const Rule& y);
friend std::ostream& operator<<(std::ostream& os, const Rule& r);
private:
date::day day() const;
date::month month() const;
static void split_overlaps(std::vector<Rule>& rules, std::size_t i, std::size_t& e);
static bool overlaps(const Rule& x, const Rule& y);
static void split(std::vector<Rule>& rules, std::size_t i, std::size_t k,
std::size_t& e);
};
inline bool operator!=(const Rule& x, const Rule& y) {return !(x == y);}
inline bool operator> (const Rule& x, const Rule& y) {return y < x;}
inline bool operator<=(const Rule& x, const Rule& y) {return !(y < x);}
inline bool operator>=(const Rule& x, const Rule& y) {return !(x < y);}
inline bool operator!=(const Rule& x, const date::year& y) {return !(x == y);}
inline bool operator> (const Rule& x, const date::year& y) {return y < x;}
inline bool operator<=(const Rule& x, const date::year& y) {return !(y < x);}
inline bool operator>=(const Rule& x, const date::year& y) {return !(x < y);}
inline bool operator!=(const date::year& x, const Rule& y) {return !(x == y);}
inline bool operator> (const date::year& x, const Rule& y) {return y < x;}
inline bool operator<=(const date::year& x, const Rule& y) {return !(y < x);}
inline bool operator>=(const date::year& x, const Rule& y) {return !(x < y);}
inline bool operator!=(const Rule& x, const std::string& y) {return !(x == y);}
inline bool operator> (const Rule& x, const std::string& y) {return y < x;}
inline bool operator<=(const Rule& x, const std::string& y) {return !(y < x);}
inline bool operator>=(const Rule& x, const std::string& y) {return !(x < y);}
inline bool operator!=(const std::string& x, const Rule& y) {return !(x == y);}
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);}
struct Zone::zonelet
{
enum tag {has_rule, has_save, is_empty};
std::chrono::seconds gmtoff_;
tag tag_ = has_rule;
union U
{
std::string rule_;
std::chrono::minutes save_;
~U() {}
U() {}
U(const U&) {}
U& operator=(const U&) = delete;
} u;
std::string format_;
date::year until_year_{0};
MonthDayTime until_date_;
seconds_point until_utc_;
seconds_point until_std_;
seconds_point until_loc_;
std::chrono::minutes initial_save_{};
std::string initial_abrev_;
std::pair<const Rule*, date::year> first_rule_{nullptr, date::year::min()};
std::pair<const Rule*, date::year> last_rule_{nullptr, date::year::max()};
~zonelet();
zonelet();
zonelet(const zonelet& i);
zonelet& operator=(const zonelet&) = delete;
};
} // namespace date
#endif // TZ_PRIVATE_H