53 namespace time_internal {
58 inline bool IsLeap(
year_t year) {
59 return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0);
63 const std::int_least32_t kDaysPerYear[2] = {365, 366};
67 const std::int_least16_t kMonthOffsets[2][1 + 12 + 1] = {
68 {-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
69 {-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
73 const std::int_least32_t kSecsPerDay = 24 * 60 * 60;
76 const std::int_least64_t kSecsPer400Years = 146097LL * kSecsPerDay;
79 const std::int_least32_t kSecsPerYear[2] = {
85 inline std::uint_fast8_t Decode8(
const char* cp) {
86 return static_cast<std::uint_fast8_t
>(*cp) & 0xff;
95 std::int_fast32_t Decode32(
const char* cp) {
96 std::uint_fast32_t
v = 0;
97 for (
int i = 0;
i != (32 / 8); ++
i) v = (v << 8) | Decode8(cp++);
98 const std::int_fast32_t s32max = 0x7fffffff;
99 const auto s32maxU =
static_cast<std::uint_fast32_t
>(s32max);
100 if (v <= s32maxU)
return static_cast<std::int_fast32_t
>(
v);
101 return static_cast<std::int_fast32_t
>(v - s32maxU - 1) - s32max - 1;
104 std::int_fast64_t Decode64(
const char* cp) {
105 std::uint_fast64_t v = 0;
106 for (
int i = 0;
i != (64 / 8); ++
i) v = (v << 8) | Decode8(cp++);
107 const std::int_fast64_t s64max = 0x7fffffffffffffff;
108 const auto s64maxU =
static_cast<std::uint_fast64_t
>(s64max);
109 if (v <= s64maxU)
return static_cast<std::int_fast64_t
>(
v);
110 return static_cast<std::int_fast64_t
>(v - s64maxU - 1) - s64max - 1;
114 std::int_fast64_t TransOffset(
bool leap_year,
int jan1_weekday,
115 const PosixTransition& pt) {
116 std::int_fast64_t days = 0;
117 switch (pt.date.fmt) {
119 days = pt.date.j.day;
120 if (!leap_year || days < kMonthOffsets[1][3]) days -= 1;
124 days = pt.date.n.day;
128 const bool last_week = (pt.date.m.week == 5);
129 days = kMonthOffsets[leap_year][pt.date.m.month + last_week];
130 const std::int_fast64_t
weekday = (jan1_weekday + days) % 7;
132 days -= (weekday + 7 - 1 - pt.date.m.weekday) % 7 + 1;
134 days += (pt.date.m.weekday + 7 -
weekday) % 7;
135 days += (pt.date.m.week - 1) * 7;
140 return (days * kSecsPerDay) + pt.time.offset;
143 inline time_zone::civil_lookup MakeUnique(
const time_point<seconds>& tp) {
144 time_zone::civil_lookup cl;
146 cl.pre = cl.trans = cl.post = tp;
150 inline time_zone::civil_lookup MakeUnique(std::int_fast64_t unix_time) {
154 inline time_zone::civil_lookup MakeSkipped(
const Transition& tr,
156 time_zone::civil_lookup cl;
164 inline time_zone::civil_lookup MakeRepeated(
const Transition& tr,
166 time_zone::civil_lookup cl;
175 return civil_second(cs.year() + shift, cs.month(), cs.day(),
176 cs.hour(), cs.minute(), cs.second());
185 tt.
utc_offset =
static_cast<std::int_least32_t
>(offset.count());
194 for (
const std::int_fast64_t unix_time : {
222 tt.civil_max =
LocalTime(seconds::max().count(), tt).
cs;
223 tt.civil_min =
LocalTime(seconds::min().count(), tt).
cs;
232 if ((v = Decode32(tzh.
tzh_timecnt)) < 0)
return false;
233 timecnt =
static_cast<std::size_t
>(
v);
234 if ((v = Decode32(tzh.
tzh_typecnt)) < 0)
return false;
235 typecnt =
static_cast<std::size_t
>(
v);
236 if ((v = Decode32(tzh.
tzh_charcnt)) < 0)
return false;
237 charcnt =
static_cast<std::size_t
>(
v);
238 if ((v = Decode32(tzh.
tzh_leapcnt)) < 0)
return false;
239 leapcnt =
static_cast<std::size_t
>(
v);
241 ttisstdcnt =
static_cast<std::size_t
>(
v);
243 ttisgmtcnt =
static_cast<std::size_t
>(
v);
251 len += (time_len + 1) * timecnt;
252 len += (4 + 1 + 1) * typecnt;
254 len += (time_len + 4) * leapcnt;
255 len += 1 * ttisstdcnt;
256 len += 1 * ttisgmtcnt;
263 std::int_fast32_t offset,
bool is_dst,
264 const std::string&
abbr)
const {
267 std::clog << name <<
": Transition" 269 << (tt.
is_dst ?
"DST" :
"STD")
271 <<
" does not match POSIX spec '" <<
future_spec_ <<
"'\n";
279 std::uint_fast8_t tt2_index)
const {
280 if (tt1_index == tt2_index)
return true;
298 std::clog << name <<
": Failed to parse '" <<
future_spec_ <<
"'\n";
302 if (extending && posix.
dst_abbr.empty()) {
312 if (extending && hdr.
timecnt < 2) {
313 std::clog << name <<
": Too few transitions for POSIX spec\n";
323 if (last.unix_time < 0) {
324 const std::uint_fast8_t type_index = last.
type_index;
361 int jan1_weekday = (
static_cast<int>(
get_weekday(jan1)) + 1) % 7;
368 std::int_fast64_t tr1_offset = TransOffset(leap_year, jan1_weekday, pt1);
369 tr->
unix_time = jan1_time + tr1_offset - tt0->utc_offset;
380 jan1_time += kSecsPerYear[leap_year];
381 jan1_weekday = (jan1_weekday + kDaysPerYear[leap_year]) % 7;
383 std::int_fast64_t tr1_offset = TransOffset(leap_year, jan1_weekday, pt1);
384 tr->
unix_time = jan1_time + tr1_offset - tt0->utc_offset;
386 std::int_fast64_t tr0_offset = TransOffset(leap_year, jan1_weekday, pt0);
387 tr->
unix_time = jan1_time + tr0_offset - tt1->utc_offset;
396 if (zip->
Read(&tzh,
sizeof(tzh)) !=
sizeof(tzh))
403 std::size_t time_len = 4;
409 if (zip->
Read(&tzh,
sizeof(tzh)) !=
sizeof(tzh))
435 std::vector<char> tbuf(len);
436 if (zip->
Read(tbuf.data(),
len) != len)
438 const char* bp = tbuf.data();
443 for (std::size_t
i = 0;
i != hdr.
timecnt; ++
i) {
444 transitions_[
i].unix_time = (time_len == 4) ? Decode32(bp) : Decode64(bp);
452 bool seen_type_0 =
false;
453 for (std::size_t
i = 0;
i != hdr.
timecnt; ++
i) {
463 for (std::size_t
i = 0;
i != hdr.
typecnt; ++
i) {
465 static_cast<std::int_least32_t
>(Decode32(bp));
478 if (seen_type_0 && hdr.
timecnt != 0) {
479 std::uint_fast8_t index = 0;
502 assert(bp == tbuf.data() + tbuf.size());
510 return (zip->
Read(&ch, 1) == 1) ? ch : EOF;
512 if (get_char(zip) !=
'\n')
514 for (
int c = get_char(zip); c !=
'\n'; c = get_char(zip)) {
578 tt.civil_max =
LocalTime(seconds::max().count(), tt).
cs;
579 tt.civil_min =
LocalTime(seconds::min().count(), tt).
cs;
589 inline FILE* FOpen(
const char*
path,
const char* mode) {
590 #if defined(_MSC_VER) 592 if (fopen_s(&fp, path, mode) != 0) fp =
nullptr;
595 return fopen(path, mode);
602 static std::unique_ptr<ZoneInfoSource> Open(
const std::string&
name);
604 std::size_t Read(
void*
ptr, std::size_t
size)
override {
605 size = std::min(size,
len_);
606 std::size_t nread = fread(ptr, 1, size,
fp_.get());
610 int Skip(std::size_t offset)
override {
611 offset = std::min(offset,
len_);
612 int rc = fseek(
fp_.get(),
static_cast<long>(offset), SEEK_CUR);
613 if (rc == 0)
len_ -= offset;
616 std::string
Version()
const override {
618 return std::string();
622 explicit FileZoneInfoSource(
623 FILE* fp, std::size_t
len = std::numeric_limits<std::size_t>::max())
627 std::unique_ptr<FILE, int(*)(FILE*)>
fp_;
631 std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open(
632 const std::string&
name) {
634 if (name.compare(0, 5,
"file:") == 0)
return Open(name.substr(5));
638 if (name.empty() || name[0] !=
'/') {
639 const char* tzdir =
"/usr/share/zoneinfo";
640 char* tzdir_env =
nullptr;
641 #if defined(_MSC_VER) 642 _dupenv_s(&tzdir_env,
nullptr,
"TZDIR");
644 tzdir_env = std::getenv(
"TZDIR");
646 if (tzdir_env && *tzdir_env) tzdir = tzdir_env;
649 #if defined(_MSC_VER) 656 FILE* fp = FOpen(path.c_str(),
"rb");
657 if (fp ==
nullptr)
return nullptr;
659 if (fseek(fp, 0, SEEK_END) == 0) {
660 long pos = ftell(fp);
662 length =
static_cast<std::size_t
>(pos);
666 return std::unique_ptr<ZoneInfoSource>(
new FileZoneInfoSource(fp, length));
669 class AndroidZoneInfoSource :
public FileZoneInfoSource {
671 static std::unique_ptr<ZoneInfoSource> Open(
const std::string&
name);
675 explicit AndroidZoneInfoSource(FILE* fp, std::size_t
len,
const char* vers)
676 : FileZoneInfoSource(fp, len),
version_(vers) {}
680 std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
681 const std::string&
name) {
683 if (name.compare(0, 5,
"file:") == 0)
return Open(name.substr(5));
685 #if defined(__ANDROID__) 687 for (
const char* tzdata : {
"/data/misc/zoneinfo/current/tzdata",
688 "/system/usr/share/zoneinfo/tzdata"}) {
689 std::unique_ptr<FILE, int (*)(FILE*)> fp(FOpen(tzdata,
"rb"), fclose);
690 if (fp.get() ==
nullptr)
continue;
693 if (fread(hbuf, 1,
sizeof(hbuf), fp.get()) !=
sizeof(hbuf))
continue;
694 if (strncmp(hbuf,
"tzdata", 6) != 0)
continue;
695 const char* vers = (hbuf[11] ==
'\0') ? hbuf + 6 :
"";
696 const std::int_fast32_t index_offset = Decode32(hbuf + 12);
697 const std::int_fast32_t data_offset = Decode32(hbuf + 16);
698 if (index_offset < 0 || data_offset < index_offset)
continue;
699 if (fseek(fp.get(),
static_cast<long>(index_offset), SEEK_SET) != 0)
703 const std::size_t index_size =
704 static_cast<std::size_t
>(data_offset - index_offset);
705 const std::size_t zonecnt = index_size /
sizeof(ebuf);
706 if (zonecnt *
sizeof(ebuf) != index_size)
continue;
707 for (std::size_t
i = 0;
i != zonecnt; ++
i) {
708 if (fread(ebuf, 1,
sizeof(ebuf), fp.get()) !=
sizeof(ebuf))
break;
709 const std::int_fast32_t start = data_offset + Decode32(ebuf + 40);
710 const std::int_fast32_t
length = Decode32(ebuf + 44);
711 if (start < 0 || length < 0)
break;
713 if (strcmp(name.c_str(), ebuf) == 0) {
714 if (fseek(fp.get(),
static_cast<long>(start), SEEK_SET) != 0)
break;
715 return std::unique_ptr<ZoneInfoSource>(
new AndroidZoneInfoSource(
716 fp.release(),
static_cast<std::size_t
>(
length), vers));
720 #endif // __ANDROID__ 731 auto offset = seconds::zero();
738 name, [](
const std::string& name) -> std::unique_ptr<ZoneInfoSource> {
739 if (
auto zip = FileZoneInfoSource::Open(name))
return zip;
740 if (
auto zip = AndroidZoneInfoSource::Open(name))
return zip;
743 return zip !=
nullptr &&
Load(name, zip.get());
758 std::int_fast64_t unix_time,
const Transition& tr)
const {
771 if (c4_shift > seconds::max().count() / kSecsPer400Years) {
774 const auto offset =
seconds(c4_shift * kSecsPer400Years);
776 for (
auto* tp : {&cl.pre, &cl.trans, &cl.post}) {
791 assert(timecnt != 0);
801 const std::int_fast64_t diff =
803 const year_t shift = diff / kSecsPer400Years + 1;
804 const auto d =
seconds(shift * kSecsPer400Years);
806 al.
cs = YearShift(al.
cs, shift * 400);
813 if (0 < hint && hint < timecnt) {
823 const Transition* tr = std::upper_bound(begin, begin + timecnt, target,
826 std::memory_order_relaxed);
832 assert(timecnt != 0);
838 if (cs < begin->civil_sec) {
844 if (0 < hint && hint < timecnt) {
855 std::memory_order_relaxed);
867 return MakeSkipped(*tr, cs);
871 if (cs > (--tr)->prev_civil_sec) {
877 return TimeLocal(YearShift(cs, shift * -400), shift);
884 return MakeRepeated(*tr, cs);
889 return MakeSkipped(*tr, cs);
892 if (cs <= (--tr)->prev_civil_sec) {
894 return MakeRepeated(*tr, cs);
906 std::ostringstream oss;
925 const Transition* tr = std::upper_bound(begin, end, target,
927 for (; tr !=
end; ++tr) {
928 std::uint_fast8_t prev_type_index =
933 if (tr == end)
return false;
951 if (unix_time == std::numeric_limits<std::int_fast64_t>::max()) {
952 if (end == begin)
return false;
953 trans->
from = (--
end)->prev_civil_sec + 1;
960 const Transition* tr = std::lower_bound(begin, end, target,
962 for (; tr !=
begin; --tr) {
963 std::uint_fast8_t prev_type_index =
968 if (tr == begin)
return false;
969 trans->
from = (--tr)->prev_civil_sec + 1;
bool ParsePosixSpec(const std::string &spec, PosixTimeZone *res)
time_zone::civil_lookup TimeLocal(const civil_second &cs, year_t c4_shift) const
void ExtendTransitions(const std::string &name, const Header &hdr)
std::uint_least8_t type_index
ZoneInfoSourceFactory zone_info_source_factory
virtual std::string Version() const
std::string abbreviations_
time_point< seconds > FromUnixSeconds(std::int_fast64_t t)
std::int_least64_t unix_time
PosixTransition dst_start
civil_second prev_civil_sec
std::string Description() const override
std::string FixedOffsetToAbbr(const seconds &offset)
std::string Version() const override
virtual int Skip(std::size_t offset)=0
CONSTEXPR_F weekday get_weekday(const civil_day &cd) noexcept
std::chrono::duration< std::int_fast64_t > seconds
time_zone::civil_lookup MakeTime(const civil_second &cs) const override
bool Load(const std::string &name)
GraphId path[kMaxDeadlockPathLen]
std::unique_ptr< FILE, int(*)(FILE *)> fp_
CONSTEXPR_M year_t year() const noexcept
virtual std::size_t Read(void *ptr, std::size_t size)=0
bool NextTransition(const time_point< seconds > &tp, time_zone::civil_transition *trans) const override
bool EquivTransitions(std::uint_fast8_t tt1_index, std::uint_fast8_t tt2_index) const
time_zone::absolute_lookup BreakTime(const time_point< seconds > &tp) const override
time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time, const TransitionType &tt) const
std::int_least32_t utc_offset
std::int_fast32_t std_offset
static PerThreadSynch * Skip(PerThreadSynch *x)
std::chrono::time_point< std::chrono::system_clock, D > time_point
bool PrevTransition(const time_point< seconds > &tp, time_zone::civil_transition *trans) const override
std::int_fast32_t dst_offset
std::uint_least8_t abbr_index
bool FixedOffsetFromName(const std::string &name, seconds *offset)
std::vector< Transition > transitions_
std::atomic< std::size_t > local_time_hint_
std::vector< TransitionType > transition_types_
void CheckTransition(const std::string &name, const TransitionType &tt, std::int_fast32_t offset, bool is_dst, const std::string &abbr) const
std::int_fast64_t ToUnixSeconds(const time_point< seconds > &tp)
bool ResetToBuiltinUTC(const seconds &offset)
std::atomic< std::size_t > time_local_hint_
std::uint_fast8_t default_transition_type_
detail::civil_second civil_second