123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- // Copyright 2017 The Abseil Authors.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // https://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- // The implementation of the y_absl::Time class, which is declared in
- // //y_absl/time.h.
- //
- // The representation for an y_absl::Time is an y_absl::Duration offset from the
- // epoch. We use the traditional Unix epoch (1970-01-01 00:00:00 +0000)
- // for convenience, but this is not exposed in the API and could be changed.
- //
- // NOTE: To keep type verbosity to a minimum, the following variable naming
- // conventions are used throughout this file.
- //
- // tz: An y_absl::TimeZone
- // ci: An y_absl::TimeZone::CivilInfo
- // ti: An y_absl::TimeZone::TimeInfo
- // cd: An y_absl::CivilDay or a cctz::civil_day
- // cs: An y_absl::CivilSecond or a cctz::civil_second
- // bd: An y_absl::Time::Breakdown
- // cl: A cctz::time_zone::civil_lookup
- // al: A cctz::time_zone::absolute_lookup
- #include "y_absl/time/time.h"
- #if defined(_MSC_VER)
- #include <winsock2.h> // for timeval
- #endif
- #include <cstring>
- #include <ctime>
- #include <limits>
- #include "y_absl/time/internal/cctz/include/cctz/civil_time.h"
- #include "y_absl/time/internal/cctz/include/cctz/time_zone.h"
- namespace cctz = y_absl::time_internal::cctz;
- namespace y_absl {
- Y_ABSL_NAMESPACE_BEGIN
- namespace {
- inline cctz::time_point<cctz::seconds> unix_epoch() {
- return std::chrono::time_point_cast<cctz::seconds>(
- std::chrono::system_clock::from_time_t(0));
- }
- // Floors d to the next unit boundary closer to negative infinity.
- inline int64_t FloorToUnit(y_absl::Duration d, y_absl::Duration unit) {
- y_absl::Duration rem;
- int64_t q = y_absl::IDivDuration(d, unit, &rem);
- return (q > 0 || rem >= ZeroDuration() ||
- q == std::numeric_limits<int64_t>::min())
- ? q
- : q - 1;
- }
- Y_ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING
- inline y_absl::Time::Breakdown InfiniteFutureBreakdown() {
- y_absl::Time::Breakdown bd;
- bd.year = std::numeric_limits<int64_t>::max();
- bd.month = 12;
- bd.day = 31;
- bd.hour = 23;
- bd.minute = 59;
- bd.second = 59;
- bd.subsecond = y_absl::InfiniteDuration();
- bd.weekday = 4;
- bd.yearday = 365;
- bd.offset = 0;
- bd.is_dst = false;
- bd.zone_abbr = "-00";
- return bd;
- }
- inline y_absl::Time::Breakdown InfinitePastBreakdown() {
- Time::Breakdown bd;
- bd.year = std::numeric_limits<int64_t>::min();
- bd.month = 1;
- bd.day = 1;
- bd.hour = 0;
- bd.minute = 0;
- bd.second = 0;
- bd.subsecond = -y_absl::InfiniteDuration();
- bd.weekday = 7;
- bd.yearday = 1;
- bd.offset = 0;
- bd.is_dst = false;
- bd.zone_abbr = "-00";
- return bd;
- }
- Y_ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING
- inline y_absl::TimeZone::CivilInfo InfiniteFutureCivilInfo() {
- TimeZone::CivilInfo ci;
- ci.cs = CivilSecond::max();
- ci.subsecond = InfiniteDuration();
- ci.offset = 0;
- ci.is_dst = false;
- ci.zone_abbr = "-00";
- return ci;
- }
- inline y_absl::TimeZone::CivilInfo InfinitePastCivilInfo() {
- TimeZone::CivilInfo ci;
- ci.cs = CivilSecond::min();
- ci.subsecond = -InfiniteDuration();
- ci.offset = 0;
- ci.is_dst = false;
- ci.zone_abbr = "-00";
- return ci;
- }
- Y_ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING
- inline y_absl::TimeConversion InfiniteFutureTimeConversion() {
- y_absl::TimeConversion tc;
- tc.pre = tc.trans = tc.post = y_absl::InfiniteFuture();
- tc.kind = y_absl::TimeConversion::UNIQUE;
- tc.normalized = true;
- return tc;
- }
- inline TimeConversion InfinitePastTimeConversion() {
- y_absl::TimeConversion tc;
- tc.pre = tc.trans = tc.post = y_absl::InfinitePast();
- tc.kind = y_absl::TimeConversion::UNIQUE;
- tc.normalized = true;
- return tc;
- }
- Y_ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING
- // Makes a Time from sec, overflowing to InfiniteFuture/InfinitePast as
- // necessary. If sec is min/max, then consult cs+tz to check for overflow.
- Time MakeTimeWithOverflow(const cctz::time_point<cctz::seconds>& sec,
- const cctz::civil_second& cs,
- const cctz::time_zone& tz,
- bool* normalized = nullptr) {
- const auto max = cctz::time_point<cctz::seconds>::max();
- const auto min = cctz::time_point<cctz::seconds>::min();
- if (sec == max) {
- const auto al = tz.lookup(max);
- if (cs > al.cs) {
- if (normalized) *normalized = true;
- return y_absl::InfiniteFuture();
- }
- }
- if (sec == min) {
- const auto al = tz.lookup(min);
- if (cs < al.cs) {
- if (normalized) *normalized = true;
- return y_absl::InfinitePast();
- }
- }
- const auto hi = (sec - unix_epoch()).count();
- return time_internal::FromUnixDuration(time_internal::MakeDuration(hi));
- }
- // Returns Mon=1..Sun=7.
- inline int MapWeekday(const cctz::weekday& wd) {
- switch (wd) {
- case cctz::weekday::monday:
- return 1;
- case cctz::weekday::tuesday:
- return 2;
- case cctz::weekday::wednesday:
- return 3;
- case cctz::weekday::thursday:
- return 4;
- case cctz::weekday::friday:
- return 5;
- case cctz::weekday::saturday:
- return 6;
- case cctz::weekday::sunday:
- return 7;
- }
- return 1;
- }
- bool FindTransition(const cctz::time_zone& tz,
- bool (cctz::time_zone::*find_transition)(
- const cctz::time_point<cctz::seconds>& tp,
- cctz::time_zone::civil_transition* trans) const,
- Time t, TimeZone::CivilTransition* trans) {
- // Transitions are second-aligned, so we can discard any fractional part.
- const auto tp = unix_epoch() + cctz::seconds(ToUnixSeconds(t));
- cctz::time_zone::civil_transition tr;
- if (!(tz.*find_transition)(tp, &tr)) return false;
- trans->from = CivilSecond(tr.from);
- trans->to = CivilSecond(tr.to);
- return true;
- }
- } // namespace
- //
- // Time
- //
- Y_ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING
- y_absl::Time::Breakdown Time::In(y_absl::TimeZone tz) const {
- if (*this == y_absl::InfiniteFuture()) return InfiniteFutureBreakdown();
- if (*this == y_absl::InfinitePast()) return InfinitePastBreakdown();
- const auto tp = unix_epoch() + cctz::seconds(time_internal::GetRepHi(rep_));
- const auto al = cctz::time_zone(tz).lookup(tp);
- const auto cs = al.cs;
- const auto cd = cctz::civil_day(cs);
- y_absl::Time::Breakdown bd;
- bd.year = cs.year();
- bd.month = cs.month();
- bd.day = cs.day();
- bd.hour = cs.hour();
- bd.minute = cs.minute();
- bd.second = cs.second();
- bd.subsecond = time_internal::MakeDuration(0, time_internal::GetRepLo(rep_));
- bd.weekday = MapWeekday(cctz::get_weekday(cd));
- bd.yearday = cctz::get_yearday(cd);
- bd.offset = al.offset;
- bd.is_dst = al.is_dst;
- bd.zone_abbr = al.abbr;
- return bd;
- }
- Y_ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING
- //
- // Conversions from/to other time types.
- //
- y_absl::Time FromUDate(double udate) {
- return time_internal::FromUnixDuration(y_absl::Milliseconds(udate));
- }
- y_absl::Time FromUniversal(int64_t universal) {
- return y_absl::UniversalEpoch() + 100 * y_absl::Nanoseconds(universal);
- }
- int64_t ToUnixNanos(Time t) {
- if (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >= 0 &&
- time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >> 33 == 0) {
- return (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) *
- 1000 * 1000 * 1000) +
- (time_internal::GetRepLo(time_internal::ToUnixDuration(t)) / 4);
- }
- return FloorToUnit(time_internal::ToUnixDuration(t), y_absl::Nanoseconds(1));
- }
- int64_t ToUnixMicros(Time t) {
- if (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >= 0 &&
- time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >> 43 == 0) {
- return (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) *
- 1000 * 1000) +
- (time_internal::GetRepLo(time_internal::ToUnixDuration(t)) / 4000);
- }
- return FloorToUnit(time_internal::ToUnixDuration(t), y_absl::Microseconds(1));
- }
- int64_t ToUnixMillis(Time t) {
- if (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >= 0 &&
- time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >> 53 == 0) {
- return (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) * 1000) +
- (time_internal::GetRepLo(time_internal::ToUnixDuration(t)) /
- (4000 * 1000));
- }
- return FloorToUnit(time_internal::ToUnixDuration(t), y_absl::Milliseconds(1));
- }
- int64_t ToUnixSeconds(Time t) {
- return time_internal::GetRepHi(time_internal::ToUnixDuration(t));
- }
- time_t ToTimeT(Time t) { return y_absl::ToTimespec(t).tv_sec; }
- double ToUDate(Time t) {
- return y_absl::FDivDuration(time_internal::ToUnixDuration(t),
- y_absl::Milliseconds(1));
- }
- int64_t ToUniversal(y_absl::Time t) {
- return y_absl::FloorToUnit(t - y_absl::UniversalEpoch(), y_absl::Nanoseconds(100));
- }
- y_absl::Time TimeFromTimespec(timespec ts) {
- return time_internal::FromUnixDuration(y_absl::DurationFromTimespec(ts));
- }
- y_absl::Time TimeFromTimeval(timeval tv) {
- return time_internal::FromUnixDuration(y_absl::DurationFromTimeval(tv));
- }
- timespec ToTimespec(Time t) {
- timespec ts;
- y_absl::Duration d = time_internal::ToUnixDuration(t);
- if (!time_internal::IsInfiniteDuration(d)) {
- ts.tv_sec = static_cast<decltype(ts.tv_sec)>(time_internal::GetRepHi(d));
- if (ts.tv_sec == time_internal::GetRepHi(d)) { // no time_t narrowing
- ts.tv_nsec = time_internal::GetRepLo(d) / 4; // floor
- return ts;
- }
- }
- if (d >= y_absl::ZeroDuration()) {
- ts.tv_sec = std::numeric_limits<time_t>::max();
- ts.tv_nsec = 1000 * 1000 * 1000 - 1;
- } else {
- ts.tv_sec = std::numeric_limits<time_t>::min();
- ts.tv_nsec = 0;
- }
- return ts;
- }
- timeval ToTimeval(Time t) {
- timeval tv;
- timespec ts = y_absl::ToTimespec(t);
- tv.tv_sec = static_cast<decltype(tv.tv_sec)>(ts.tv_sec);
- if (tv.tv_sec != ts.tv_sec) { // narrowing
- if (ts.tv_sec < 0) {
- tv.tv_sec = std::numeric_limits<decltype(tv.tv_sec)>::min();
- tv.tv_usec = 0;
- } else {
- tv.tv_sec = std::numeric_limits<decltype(tv.tv_sec)>::max();
- tv.tv_usec = 1000 * 1000 - 1;
- }
- return tv;
- }
- tv.tv_usec = static_cast<int>(ts.tv_nsec / 1000); // suseconds_t
- return tv;
- }
- Time FromChrono(const std::chrono::system_clock::time_point& tp) {
- return time_internal::FromUnixDuration(time_internal::FromChrono(
- tp - std::chrono::system_clock::from_time_t(0)));
- }
- std::chrono::system_clock::time_point ToChronoTime(y_absl::Time t) {
- using D = std::chrono::system_clock::duration;
- auto d = time_internal::ToUnixDuration(t);
- if (d < ZeroDuration()) d = Floor(d, FromChrono(D{1}));
- return std::chrono::system_clock::from_time_t(0) +
- time_internal::ToChronoDuration<D>(d);
- }
- //
- // TimeZone
- //
- y_absl::TimeZone::CivilInfo TimeZone::At(Time t) const {
- if (t == y_absl::InfiniteFuture()) return InfiniteFutureCivilInfo();
- if (t == y_absl::InfinitePast()) return InfinitePastCivilInfo();
- const auto ud = time_internal::ToUnixDuration(t);
- const auto tp = unix_epoch() + cctz::seconds(time_internal::GetRepHi(ud));
- const auto al = cz_.lookup(tp);
- TimeZone::CivilInfo ci;
- ci.cs = CivilSecond(al.cs);
- ci.subsecond = time_internal::MakeDuration(0, time_internal::GetRepLo(ud));
- ci.offset = al.offset;
- ci.is_dst = al.is_dst;
- ci.zone_abbr = al.abbr;
- return ci;
- }
- y_absl::TimeZone::TimeInfo TimeZone::At(CivilSecond ct) const {
- const cctz::civil_second cs(ct);
- const auto cl = cz_.lookup(cs);
- TimeZone::TimeInfo ti;
- switch (cl.kind) {
- case cctz::time_zone::civil_lookup::UNIQUE:
- ti.kind = TimeZone::TimeInfo::UNIQUE;
- break;
- case cctz::time_zone::civil_lookup::SKIPPED:
- ti.kind = TimeZone::TimeInfo::SKIPPED;
- break;
- case cctz::time_zone::civil_lookup::REPEATED:
- ti.kind = TimeZone::TimeInfo::REPEATED;
- break;
- }
- ti.pre = MakeTimeWithOverflow(cl.pre, cs, cz_);
- ti.trans = MakeTimeWithOverflow(cl.trans, cs, cz_);
- ti.post = MakeTimeWithOverflow(cl.post, cs, cz_);
- return ti;
- }
- bool TimeZone::NextTransition(Time t, CivilTransition* trans) const {
- return FindTransition(cz_, &cctz::time_zone::next_transition, t, trans);
- }
- bool TimeZone::PrevTransition(Time t, CivilTransition* trans) const {
- return FindTransition(cz_, &cctz::time_zone::prev_transition, t, trans);
- }
- //
- // Conversions involving time zones.
- //
- Y_ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING
- y_absl::TimeConversion ConvertDateTime(int64_t year, int mon, int day, int hour,
- int min, int sec, TimeZone tz) {
- // Avoids years that are too extreme for CivilSecond to normalize.
- if (year > 300000000000) return InfiniteFutureTimeConversion();
- if (year < -300000000000) return InfinitePastTimeConversion();
- const CivilSecond cs(year, mon, day, hour, min, sec);
- const auto ti = tz.At(cs);
- TimeConversion tc;
- tc.pre = ti.pre;
- tc.trans = ti.trans;
- tc.post = ti.post;
- switch (ti.kind) {
- case TimeZone::TimeInfo::UNIQUE:
- tc.kind = TimeConversion::UNIQUE;
- break;
- case TimeZone::TimeInfo::SKIPPED:
- tc.kind = TimeConversion::SKIPPED;
- break;
- case TimeZone::TimeInfo::REPEATED:
- tc.kind = TimeConversion::REPEATED;
- break;
- }
- tc.normalized = false;
- if (year != cs.year() || mon != cs.month() || day != cs.day() ||
- hour != cs.hour() || min != cs.minute() || sec != cs.second()) {
- tc.normalized = true;
- }
- return tc;
- }
- Y_ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING
- y_absl::Time FromTM(const struct tm& tm, y_absl::TimeZone tz) {
- civil_year_t tm_year = tm.tm_year;
- // Avoids years that are too extreme for CivilSecond to normalize.
- if (tm_year > 300000000000ll) return InfiniteFuture();
- if (tm_year < -300000000000ll) return InfinitePast();
- int tm_mon = tm.tm_mon;
- if (tm_mon == std::numeric_limits<int>::max()) {
- tm_mon -= 12;
- tm_year += 1;
- }
- const auto ti = tz.At(CivilSecond(tm_year + 1900, tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec));
- return tm.tm_isdst == 0 ? ti.post : ti.pre;
- }
- struct tm ToTM(y_absl::Time t, y_absl::TimeZone tz) {
- struct tm tm = {};
- const auto ci = tz.At(t);
- const auto& cs = ci.cs;
- tm.tm_sec = cs.second();
- tm.tm_min = cs.minute();
- tm.tm_hour = cs.hour();
- tm.tm_mday = cs.day();
- tm.tm_mon = cs.month() - 1;
- // Saturates tm.tm_year in cases of over/underflow, accounting for the fact
- // that tm.tm_year is years since 1900.
- if (cs.year() < std::numeric_limits<int>::min() + 1900) {
- tm.tm_year = std::numeric_limits<int>::min();
- } else if (cs.year() > std::numeric_limits<int>::max()) {
- tm.tm_year = std::numeric_limits<int>::max() - 1900;
- } else {
- tm.tm_year = static_cast<int>(cs.year() - 1900);
- }
- switch (GetWeekday(cs)) {
- case Weekday::sunday:
- tm.tm_wday = 0;
- break;
- case Weekday::monday:
- tm.tm_wday = 1;
- break;
- case Weekday::tuesday:
- tm.tm_wday = 2;
- break;
- case Weekday::wednesday:
- tm.tm_wday = 3;
- break;
- case Weekday::thursday:
- tm.tm_wday = 4;
- break;
- case Weekday::friday:
- tm.tm_wday = 5;
- break;
- case Weekday::saturday:
- tm.tm_wday = 6;
- break;
- }
- tm.tm_yday = GetYearDay(cs) - 1;
- tm.tm_isdst = ci.is_dst ? 1 : 0;
- return tm;
- }
- Y_ABSL_NAMESPACE_END
- } // namespace y_absl
|