Create local and system time types for timezone functions.

* Add sys_time.
* Add sys_days.
* Add sys_seconds.
* Add local_time.
* Add local_days.
* Add local_seconds.
* Rename day_point to sys_days.
* Rename Zone to time_zone.
This commit is contained in:
Howard Hinnant
2016-05-03 23:19:09 -04:00
parent 95271f8337
commit 1e5d2fa8dd
8 changed files with 906 additions and 414 deletions
+66 -57
View File
@@ -20,6 +20,10 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Our apologies. When the previous paragraph was written, lowercase had not yet
// been invented (that woud involve another several millennia of evolution).
// We did not mean to shout.
#include "tz_private.h"
@@ -118,6 +122,8 @@ CONSTDATA auto max_year = date::year::max();
// Arbitrary day of the year that will be away from any limits.
// Used with year::min() and year::max().
CONSTDATA auto boring_day = date::aug/18;
CONSTDATA auto min_day = date::jan/1;
CONSTDATA auto max_day = date::dec/31;
// +-------------------+
// | End Configuration |
@@ -549,7 +555,7 @@ parse_signed_time(std::istream& in)
// MonthDayTime
MonthDayTime::MonthDayTime(second_point tp, tz timezone)
MonthDayTime::MonthDayTime(local_seconds tp, tz timezone)
: zone_(timezone)
{
using namespace date;
@@ -606,8 +612,8 @@ MonthDayTime::compare(date::year y, const MonthDayTime& x, date::year yx,
{
if (zone_ != x.zone_)
{
auto dp0 = to_day_point(y);
auto dp1 = x.to_day_point(yx);
auto dp0 = to_sys_days(y);
auto dp1 = x.to_sys_days(yx);
if (std::abs((dp0-dp1).count()) > 1)
return dp0 < dp1 ? -1 : 1;
if (zone_ == tz::local)
@@ -642,7 +648,7 @@ MonthDayTime::compare(date::year y, const MonthDayTime& x, date::year yx,
return t0 < t1 ? -1 : t0 == t1 ? 0 : 1;
}
second_point
sys_seconds
MonthDayTime::to_sys(date::year y, std::chrono::seconds offset,
std::chrono::seconds save) const
{
@@ -677,23 +683,23 @@ MonthDayTime::U::operator=(const pair& x)
return *this;
}
date::day_point
MonthDayTime::to_day_point(date::year y) const
date::sys_days
MonthDayTime::to_sys_days(date::year y) const
{
using namespace std::chrono;
using namespace date;
switch (type_)
{
case month_day:
return day_point(y/u.month_day_);
return sys_days(y/u.month_day_);
case month_last_dow:
return day_point(y/u.month_weekday_last_);
return sys_days(y/u.month_weekday_last_);
case lteq:
{
auto const x = y/u.month_day_weekday_.month_day_;
auto const wd1 = weekday(x);
auto const wd0 = u.month_day_weekday_.weekday_;
return day_point(x) - (wd1-wd0);
return sys_days(x) - (wd1-wd0);
}
case gteq:
break;
@@ -701,13 +707,13 @@ MonthDayTime::to_day_point(date::year y) const
auto const x = y/u.month_day_weekday_.month_day_;
auto const wd1 = u.month_day_weekday_.weekday_;
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
{
return to_day_point(y) + h_ + m_ + s_;
return to_sys_days(y) + h_ + m_ + s_;
}
void
@@ -721,7 +727,7 @@ MonthDayTime::canonicalize(date::year y)
return;
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();
type_ = month_day;
return;
@@ -731,7 +737,7 @@ MonthDayTime::canonicalize(date::year y)
auto const x = y/u.month_day_weekday_.month_day_;
auto const wd1 = weekday(x);
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();
type_ = month_day;
return;
@@ -741,7 +747,7 @@ MonthDayTime::canonicalize(date::year y)
auto const x = y/u.month_day_weekday_.month_day_;
auto const wd1 = u.month_day_weekday_.weekday_;
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();
type_ = month_day;
return;
@@ -1218,9 +1224,9 @@ Rule::split_overlaps(std::vector<Rule>& rules)
rules.shrink_to_fit();
}
// Zone
// time_zone
Zone::zonelet::~zonelet()
time_zone::zonelet::~zonelet()
{
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
using minutes = std::chrono::minutes;
@@ -1232,14 +1238,14 @@ Zone::zonelet::~zonelet()
#endif
}
Zone::zonelet::zonelet()
time_zone::zonelet::zonelet()
{
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
::new(&u.rule_) std::string();
#endif
}
Zone::zonelet::zonelet(const zonelet& i)
time_zone::zonelet::zonelet(const zonelet& i)
: gmtoff_(i.gmtoff_)
, tag_(i.tag_)
, format_(i.format_)
@@ -1266,7 +1272,7 @@ Zone::zonelet::zonelet(const zonelet& i)
#endif
}
Zone::Zone(const std::string& s)
time_zone::time_zone(const std::string& s)
#if LAZY_INIT
: adjusted_(new std::once_flag{})
#endif
@@ -1290,7 +1296,7 @@ Zone::Zone(const std::string& s)
}
void
Zone::add(const std::string& s)
time_zone::add(const std::string& s)
{
try
{
@@ -1310,7 +1316,7 @@ Zone::add(const std::string& s)
}
void
Zone::parse_info(std::istream& in)
time_zone::parse_info(std::istream& in)
{
using namespace date;
using namespace std::chrono;
@@ -1326,7 +1332,7 @@ Zone::parse_info(std::istream& in)
if (in.eof() || in.peek() == '#')
{
zonelet.until_year_ = year::max();
zonelet.until_date_ = MonthDayTime(boring_day, tz::utc);
zonelet.until_date_ = MonthDayTime(max_day, tz::utc);
}
else
{
@@ -1489,8 +1495,9 @@ find_rule_for_zone(const std::pair<const Rule*, const Rule*>& eqr,
static
std::pair<const Rule*, date::year>
find_rule_for_zone(const std::pair<const Rule*, const Rule*>& eqr,
const second_point& tp_utc, const second_point& tp_std,
const second_point& tp_loc)
const sys_seconds& tp_utc,
const local_seconds& tp_std,
const local_seconds& tp_loc)
{
using namespace std::chrono;
using namespace date;
@@ -1508,10 +1515,10 @@ find_rule_for_zone(const std::pair<const Rule*, const Rule*>& eqr,
found = tp_utc < r->mdt().to_time_point(ry);
break;
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;
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;
}
if (found)
@@ -1536,7 +1543,7 @@ find_rule(const std::pair<const Rule*, date::year>& first_rule,
using namespace date;
auto r = first_rule.first;
auto ry = first_rule.second;
Info x{day_point(year::min()/boring_day), day_point(year::max()/boring_day),
Info x{sys_days(year::min()/min_day), sys_days(year::max()/max_day),
seconds{0}, initial_save, initial_abbrev};
while (r != nullptr)
{
@@ -1569,7 +1576,7 @@ find_rule(const std::pair<const Rule*, date::year>& first_rule,
x.end = r->mdt().to_sys(ry, offset, x.save);
}
else
x.end = day_point(year::max()/boring_day);
x.end = sys_days(year::max()/max_day);
break;
}
x.save = r->save();
@@ -1580,7 +1587,7 @@ find_rule(const std::pair<const Rule*, date::year>& first_rule,
}
void
Zone::adjust_infos(const std::vector<Rule>& rules)
time_zone::adjust_infos(const std::vector<Rule>& rules)
{
using namespace std::chrono;
using namespace date;
@@ -1640,7 +1647,7 @@ Zone::adjust_infos(const std::vector<Rule>& rules)
final_save = z.last_rule_.first->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;
if (z.tag_ == zonelet::has_rule)
@@ -1751,7 +1758,7 @@ format_abbrev(std::string format, const std::string& variable, std::chrono::seco
}
Info
Zone::get_info(std::chrono::system_clock::time_point tp, tz timezone) const
time_zone::get_info_impl(sys_seconds tp, tz timezone) const
{
using namespace std::chrono;
using namespace date;
@@ -1761,17 +1768,18 @@ Zone::get_info(std::chrono::system_clock::time_point tp, tz timezone) const
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)) + ", "
+ std::to_string(static_cast<int>(max_year)) + "]");
auto tps = floor<seconds>(tp);
#if LAZY_INIT
std::call_once(*adjusted_, [this]()
{
const_cast<Zone*>(this)->adjust_infos(get_tzdb().rules);
});
std::call_once(*adjusted_,
[this]()
{
const_cast<time_zone*>(this)->adjust_infos(get_tzdb().rules);
});
#endif
auto i = std::upper_bound(zonelets_.begin(), zonelets_.end(), tps,
[timezone](second_point t, const zonelet& zl)
auto i = std::upper_bound(zonelets_.begin(), zonelets_.end(), tp,
[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{};
@@ -1782,7 +1790,7 @@ Zone::get_info(std::chrono::system_clock::time_point tp, tz timezone) const
if (i != zonelets_.begin())
r.begin = i[-1].until_utc_;
else
r.begin = day_point(year::min()/boring_day);
r.begin = sys_days(year::min()/min_day);
r.end = i->until_utc_;
r.offset = i->gmtoff_ + i->u.save_;
r.save = i->u.save_;
@@ -1792,15 +1800,15 @@ Zone::get_info(std::chrono::system_clock::time_point tp, tz timezone) const
if (i != zonelets_.begin())
r.begin = i[-1].until_utc_;
else
r.begin = day_point(year::min()/boring_day);
r.begin = sys_days(year::min()/min_day);
r.end = i->until_utc_;
r.offset = i->gmtoff_;
}
else
{
r = find_rule(i->first_rule_, i->last_rule_, y, i->gmtoff_,
MonthDayTime(tps, timezone), i->initial_save_,
i->initial_abbrev_);
MonthDayTime(local_seconds{tp.time_since_epoch()}, timezone),
i->initial_save_, i->initial_abbrev_);
r.offset = i->gmtoff_ + r.save;
if (i != zonelets_.begin() && r.begin < i[-1].until_utc_)
r.begin = i[-1].until_utc_;
@@ -1814,7 +1822,7 @@ Zone::get_info(std::chrono::system_clock::time_point tp, tz timezone) const
}
std::ostream&
operator<<(std::ostream& os, const Zone& z)
operator<<(std::ostream& os, const time_zone& z)
{
using namespace date;
using namespace std::chrono;
@@ -1822,10 +1830,11 @@ operator<<(std::ostream& os, const Zone& z)
os.fill(' ');
os.flags(std::ios::dec | std::ios::left);
#if LAZY_INIT
std::call_once(*z.adjusted_, [&z]()
{
const_cast<Zone&>(z).adjust_infos(get_tzdb().rules);
});
std::call_once(*z.adjusted_,
[&z]()
{
const_cast<time_zone&>(z).adjust_infos(get_tzdb().rules);
});
#endif
os.width(35);
os << z.name_;
@@ -1837,7 +1846,7 @@ operator<<(std::ostream& os, const Zone& z)
os << ' ';
os << make_time(s.gmtoff_) << " ";
os.width(15);
if (s.tag_ != Zone::zonelet::has_save)
if (s.tag_ != time_zone::zonelet::has_save)
os << s.u.rule_;
else
{
@@ -2120,7 +2129,7 @@ init_tzdb()
}
else if (word == "Zone")
{
db.zones.push_back(Zone(line));
db.zones.push_back(time_zone(line));
continue_zone = true;
}
else if (line[0] == '\t' && continue_zone)
@@ -2182,12 +2191,12 @@ get_tzdb()
return ref;
}
const Zone*
const time_zone*
locate_zone(const std::string& tz_name)
{
const auto& db = get_tzdb();
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;
});
@@ -2201,7 +2210,7 @@ locate_zone(const std::string& tz_name)
if (li != db.links.end() && li->name() == tz_name)
{
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;
});
@@ -2215,7 +2224,7 @@ locate_zone(const std::string& tz_name)
#ifdef TZ_TEST
#ifdef _WIN32
const Zone*
const time_zone*
locate_native_zone(const std::string& native_tz_name)
{
std::string standard_tz_name;
@@ -2305,7 +2314,7 @@ operator<<(std::ostream& os, const Info& r)
#ifdef _WIN32
const Zone*
const time_zone*
current_zone()
{
#if TIMEZONE_MAPPING
@@ -2346,7 +2355,7 @@ current_zone()
#else // ! WIN32
const Zone*
const time_zone*
current_zone()
{
// On some versions of some linux distro's (e.g. Ubuntu),