Add support for the zic-compiled OS-supplied time zone DB:

*  Avoids the need to download the IANA database.

*  Heavily based on contributions by Aaron Bishop.

*  Turn on with -DUSE_OS_TZDB, off by default.

*  Not supported on Windows.

*  Disables HAS_REMOTE_API.

*  get_tzdb().version only supported on Apple.  This string has
   the value "unknown" elsewhere.

*  Leap second support is missing on Apple, and may not be on your
   platform either (please report).  Leap second support is enabled,
   disabled with -DMISSING_LEAP_SECONDS.

   Without leap second support, utc_time, tai_time, and gps_time (and
   those clocks) are not available.

*  On Apple, time zone transitions are only supported in the range:

   1901-12-13 20:45:52 to 2038-01-19 03:14:07

*  On Linux, time zone transitions are only as far in the future as
   the OS-provided transitions go.  There is no support for POSIX-
   style transitions.
This commit is contained in:
Howard Hinnant 2017-06-04 14:58:57 -04:00
parent 5132385454
commit a610f087c1
4 changed files with 954 additions and 203 deletions

View File

@ -89,12 +89,18 @@ tzmain()
using namespace std::chrono;
auto& db = get_tzdb();
std::vector<std::string> names;
#if USE_OS_TZDB
names.reserve(db.zones.size());
for (auto& zone : db.zones)
names.push_back(zone.name());
#else // !USE_OS_TZDB
names.reserve(db.zones.size() + db.links.size());
for (auto& zone : db.zones)
names.push_back(zone.name());
for (auto& link : db.links)
names.push_back(link.name());
std::sort(names.begin(), names.end());
#endif // !USE_OS_TZDB
std::cout << db.version << "\n\n";
for (auto const& name : names)
{

978
tz.cpp

File diff suppressed because it is too large Load Diff

121
tz.h
View File

@ -5,6 +5,7 @@
//
// Copyright (c) 2015, 2016, 2017 Howard Hinnant
// Copyright (c) 2017 Jiangang Zhuang
// Copyright (c) 2017 Aaron Bishop
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@ -41,14 +42,25 @@
// required. On Windows, the names are never "Standard" so mapping is always required.
// Technically any OS may use the mapping process but currently only Windows does use it.
#ifndef USE_OS_TZDB
# define USE_OS_TZDB 0
#endif
#ifndef HAS_REMOTE_API
# ifdef _WIN32
# if USE_OS_TZDB == 0
# ifdef _WIN32
# define HAS_REMOTE_API 0
# else
# define HAS_REMOTE_API 1
# endif
# else // HAS_REMOTE_API makes no since when using the OS timezone database
# define HAS_REMOTE_API 0
# else
# define HAS_REMOTE_API 1
# endif
#endif
static_assert(!(USE_OS_TZDB && HAS_REMOTE_API),
"USE_OS_TZDB and HAS_REMOTE_API can not be used together");
#ifndef AUTO_DOWNLOAD
# define AUTO_DOWNLOAD HAS_REMOTE_API
#endif
@ -60,6 +72,19 @@ static_assert(HAS_REMOTE_API == 0 ? AUTO_DOWNLOAD == 0 : true,
# define USE_SHELL_API 1
#endif
#if USE_OS_TZDB
# ifdef _WIN32
# error "USE_OS_TZDB can not be used on Windows"
# endif
# ifndef MISSING_LEAP_SECONDS
# ifdef __APPLE__
# define MISSING_LEAP_SECONDS 1
# else
# define MISSING_LEAP_SECONDS 0
# endif
# endif
#endif
#include "date.h"
#if defined(_MSC_VER) && (_MSC_VER < 1900)
@ -198,8 +223,6 @@ ambiguous_local_time::make_msg(local_time<Duration> tp,
return os.str();
}
namespace detail { class Rule; }
struct sys_info
{
sys_seconds begin;
@ -331,16 +354,31 @@ operator!=(const zoned_time<Duration1>& x, const zoned_time<Duration2>& y)
}
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
namespace detail { struct zonelet; }
#endif
namespace detail
{
# if USE_OS_TZDB
struct transition;
struct expanded_ttinfo;
# else // !USE_OS_TZDB
struct zonelet;
class Rule;
# endif // !USE_OS_TZDB
}
#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900)
class time_zone
{
private:
std::string name_;
std::vector<detail::zonelet> zonelets_;
std::unique_ptr<std::once_flag> adjusted_;
std::string name_;
#if USE_OS_TZDB
std::vector<detail::transition> transitions_;
std::vector<detail::expanded_ttinfo> ttinfos_;
#else // !USE_OS_TZDB
std::vector<detail::zonelet> zonelets_;
#endif // !USE_OS_TZDB
std::unique_ptr<std::once_flag> adjusted_;
public:
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
@ -374,15 +412,13 @@ public:
friend bool operator< (const time_zone& x, const time_zone& y) NOEXCEPT;
friend DATE_API std::ostream& operator<<(std::ostream& os, const time_zone& z);
#if !USE_OS_TZDB
DATE_API void add(const std::string& s);
DATE_API void adjust_infos(const std::vector<detail::Rule>& rules);
#endif // !USE_OS_TZDB
private:
DATE_API sys_info get_info_impl(sys_seconds tp) const;
DATE_API local_info get_info_impl(local_seconds tp) const;
DATE_API sys_info get_info_impl(sys_seconds tp, int timezone) const;
void parse_info(std::istream& in);
template <class Duration>
sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
@ -390,6 +426,22 @@ private:
template <class Duration>
sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
to_sys_impl(local_time<Duration> tp, choose, std::true_type) const;
#if USE_OS_TZDB
DATE_API void init() const;
DATE_API void init_impl();
DATE_API sys_info
load_sys_info(std::vector<detail::transition>::const_iterator i) const;
template <class TimeType>
DATE_API void
load_data(std::istream& inf, std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt,
std::int32_t tzh_typecnt, std::int32_t tzh_charcnt);
#else // !USE_OS_TZDB
DATE_API sys_info get_info_impl(sys_seconds tp, int timezone) const;
DATE_API void adjust_infos(const std::vector<detail::Rule>& rules);
DATE_API void parse_info(std::istream& in);
#endif // !USE_OS_TZDB
};
#if defined(_MSC_VER) && (_MSC_VER < 1900)
@ -515,6 +567,8 @@ time_zone::to_sys_impl(local_time<Duration> tp, choose, std::true_type) const
return sys_time<Duration>{tp.time_since_epoch()} - i.first.offset;
}
#if !USE_OS_TZDB
class link
{
private:
@ -537,13 +591,21 @@ 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);}
#endif // !USE_OS_TZDB
#if !MISSING_LEAP_SECONDS
class leap
{
private:
sys_seconds date_;
public:
#if USE_OS_TZDB
DATE_API explicit leap(const sys_seconds& s, detail::undocumented);
#else
DATE_API explicit leap(const std::string& s, detail::undocumented);
#endif
sys_seconds date() const {return date_;}
@ -654,6 +716,8 @@ operator>=(const sys_time<Duration>& x, const leap& y)
return !(x < y);
}
#endif // !MISSING_LEAP_SECONDS
#ifdef _WIN32
namespace detail
@ -694,11 +758,17 @@ struct timezone_mapping
struct TZ_DB
{
std::string version;
std::string version = "unknown";
std::vector<time_zone> zones;
#if !USE_OS_TZDB
std::vector<link> links;
#endif
#if !MISSING_LEAP_SECONDS
std::vector<leap> leaps;
#endif
#if !USE_OS_TZDB
std::vector<detail::Rule> rules;
#endif
#ifdef _WIN32
std::vector<detail::timezone_mapping> mappings;
#endif
@ -707,16 +777,14 @@ struct TZ_DB
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
TZ_DB(TZ_DB&&) = default;
TZ_DB& operator=(TZ_DB&&) = default;
#else // defined(_MSC_VER) || (_MSC_VER >= 1900)
#else // defined(_MSC_VER) && (_MSC_VER < 1900)
TZ_DB(TZ_DB&& src)
: version(std::move(src.version))
, zones(std::move(src.zones))
, links(std::move(src.links))
, leaps(std::move(src.leaps))
, rules(std::move(src.rules))
#ifdef _WIN32
, mappings(std::move(src.mappings))
#endif
{}
TZ_DB& operator=(TZ_DB&& src)
@ -726,25 +794,30 @@ struct TZ_DB
links = std::move(src.links);
leaps = std::move(src.leaps);
rules = std::move(src.rules);
#ifdef _WIN32
mappings = std::move(src.mappings);
#endif
return *this;
}
#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900)
#endif // defined(_MSC_VER) && (_MSC_VER < 1900)
};
DATE_API std::ostream&
operator<<(std::ostream& os, const TZ_DB& db);
DATE_API const TZ_DB& get_tzdb();
#if !USE_OS_TZDB
DATE_API const TZ_DB& reload_tzdb();
DATE_API void set_install(const std::string& install);
#endif // !USE_OS_TZDB
#if HAS_REMOTE_API
DATE_API std::string remote_version();
DATE_API bool remote_download(const std::string& version);
DATE_API bool remote_install(const std::string& version);
#endif
DATE_API const time_zone* locate_zone(const std::string& tz_name);
@ -1050,6 +1123,8 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const zoned_time<Duration>& t)
return os;
}
#if !MISSING_LEAP_SECONDS
class utc_clock
{
public:
@ -1441,6 +1516,8 @@ to_gps_time(const tai_time<Duration>& t) NOEXCEPT
(sys_days(year{1980}/jan/sun[1]) - sys_days(year{1958}/jan/1) + seconds{19});
}
#endif // !MISSING_LEAP_SECONDS
} // namespace date
#endif // TZ_H

View File

@ -40,6 +40,8 @@ namespace date
namespace detail
{
#if !USE_OS_TZDB
enum class tz {utc, local, standard};
//forward declare to avoid warnings in gcc 6.2
@ -254,6 +256,56 @@ struct zonelet
zonelet& operator=(const zonelet&) = delete;
};
#else // USE_OS_TZDB
struct ttinfo
{
std::int32_t tt_gmtoff;
unsigned char tt_isdst;
unsigned char tt_abbrind;
unsigned char pad[2];
};
static_assert(sizeof(ttinfo) == 8, "");
struct expanded_ttinfo
{
std::chrono::seconds offset;
std::string abbrev;
bool is_dst;
};
struct transition
{
sys_seconds timepoint;
const expanded_ttinfo* info;
transition(sys_seconds tp, const expanded_ttinfo* i = nullptr)
: timepoint(tp)
, info(i)
{}
friend
std::ostream&
operator<<(std::ostream& os, const transition& t)
{
using namespace date;
using namespace std::chrono;
os << t.timepoint << "Z ";
if (t.info->offset >= seconds{0})
os << '+';
os << make_time(t.info->offset);
if (t.info->is_dst > 0)
os << " daylight ";
else
os << " standard ";
os << t.info->abbrev;
return os;
}
};
#endif // USE_OS_TZDB
} // namespace detail
} // namespace date