create
This commit is contained in:
94
Pods/abseil/absl/time/internal/cctz/src/civil_time_detail.cc
generated
Normal file
94
Pods/abseil/absl/time/internal/cctz/src/civil_time_detail.cc
generated
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time_detail.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
namespace detail {
|
||||
|
||||
// Output stream operators output a format matching YYYY-MM-DDThh:mm:ss,
|
||||
// while omitting fields inferior to the type's alignment. For example,
|
||||
// civil_day is formatted only as YYYY-MM-DD.
|
||||
std::ostream& operator<<(std::ostream& os, const civil_year& y) {
|
||||
std::stringstream ss;
|
||||
ss << y.year(); // No padding.
|
||||
return os << ss.str();
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, const civil_month& m) {
|
||||
std::stringstream ss;
|
||||
ss << civil_year(m) << '-';
|
||||
ss << std::setfill('0') << std::setw(2) << m.month();
|
||||
return os << ss.str();
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, const civil_day& d) {
|
||||
std::stringstream ss;
|
||||
ss << civil_month(d) << '-';
|
||||
ss << std::setfill('0') << std::setw(2) << d.day();
|
||||
return os << ss.str();
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, const civil_hour& h) {
|
||||
std::stringstream ss;
|
||||
ss << civil_day(h) << 'T';
|
||||
ss << std::setfill('0') << std::setw(2) << h.hour();
|
||||
return os << ss.str();
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, const civil_minute& m) {
|
||||
std::stringstream ss;
|
||||
ss << civil_hour(m) << ':';
|
||||
ss << std::setfill('0') << std::setw(2) << m.minute();
|
||||
return os << ss.str();
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, const civil_second& s) {
|
||||
std::stringstream ss;
|
||||
ss << civil_minute(s) << ':';
|
||||
ss << std::setfill('0') << std::setw(2) << s.second();
|
||||
return os << ss.str();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, weekday wd) {
|
||||
switch (wd) {
|
||||
case weekday::monday:
|
||||
return os << "Monday";
|
||||
case weekday::tuesday:
|
||||
return os << "Tuesday";
|
||||
case weekday::wednesday:
|
||||
return os << "Wednesday";
|
||||
case weekday::thursday:
|
||||
return os << "Thursday";
|
||||
case weekday::friday:
|
||||
return os << "Friday";
|
||||
case weekday::saturday:
|
||||
return os << "Saturday";
|
||||
case weekday::sunday:
|
||||
return os << "Sunday";
|
||||
}
|
||||
return os; // Should never get here, but -Wreturn-type may warn without this.
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
140
Pods/abseil/absl/time/internal/cctz/src/time_zone_fixed.cc
generated
Normal file
140
Pods/abseil/absl/time/internal/cctz/src/time_zone_fixed.cc
generated
Normal file
@@ -0,0 +1,140 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "time_zone_fixed.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
namespace {
|
||||
|
||||
// The prefix used for the internal names of fixed-offset zones.
|
||||
const char kFixedZonePrefix[] = "Fixed/UTC";
|
||||
|
||||
const char kDigits[] = "0123456789";
|
||||
|
||||
char* Format02d(char* p, int v) {
|
||||
*p++ = kDigits[(v / 10) % 10];
|
||||
*p++ = kDigits[v % 10];
|
||||
return p;
|
||||
}
|
||||
|
||||
int Parse02d(const char* p) {
|
||||
if (const char* ap = std::strchr(kDigits, *p)) {
|
||||
int v = static_cast<int>(ap - kDigits);
|
||||
if (const char* bp = std::strchr(kDigits, *++p)) {
|
||||
return (v * 10) + static_cast<int>(bp - kDigits);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool FixedOffsetFromName(const std::string& name, seconds* offset) {
|
||||
if (name == "UTC" || name == "UTC0") {
|
||||
*offset = seconds::zero();
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
|
||||
const char* const ep = kFixedZonePrefix + prefix_len;
|
||||
if (name.size() != prefix_len + 9) // <prefix>+99:99:99
|
||||
return false;
|
||||
if (!std::equal(kFixedZonePrefix, ep, name.begin())) return false;
|
||||
const char* np = name.data() + prefix_len;
|
||||
if (np[0] != '+' && np[0] != '-') return false;
|
||||
if (np[3] != ':' || np[6] != ':') // see note below about large offsets
|
||||
return false;
|
||||
|
||||
int hours = Parse02d(np + 1);
|
||||
if (hours == -1) return false;
|
||||
int mins = Parse02d(np + 4);
|
||||
if (mins == -1) return false;
|
||||
int secs = Parse02d(np + 7);
|
||||
if (secs == -1) return false;
|
||||
|
||||
secs += ((hours * 60) + mins) * 60;
|
||||
if (secs > 24 * 60 * 60) return false; // outside supported offset range
|
||||
*offset = seconds(secs * (np[0] == '-' ? -1 : 1)); // "-" means west
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string FixedOffsetToName(const seconds& offset) {
|
||||
if (offset == seconds::zero()) return "UTC";
|
||||
if (offset < std::chrono::hours(-24) || offset > std::chrono::hours(24)) {
|
||||
// We don't support fixed-offset zones more than 24 hours
|
||||
// away from UTC to avoid complications in rendering such
|
||||
// offsets and to (somewhat) limit the total number of zones.
|
||||
return "UTC";
|
||||
}
|
||||
int offset_seconds = static_cast<int>(offset.count());
|
||||
const char sign = (offset_seconds < 0 ? '-' : '+');
|
||||
int offset_minutes = offset_seconds / 60;
|
||||
offset_seconds %= 60;
|
||||
if (sign == '-') {
|
||||
if (offset_seconds > 0) {
|
||||
offset_seconds -= 60;
|
||||
offset_minutes += 1;
|
||||
}
|
||||
offset_seconds = -offset_seconds;
|
||||
offset_minutes = -offset_minutes;
|
||||
}
|
||||
int offset_hours = offset_minutes / 60;
|
||||
offset_minutes %= 60;
|
||||
const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
|
||||
char buf[prefix_len + sizeof("-24:00:00")];
|
||||
char* ep = std::copy_n(kFixedZonePrefix, prefix_len, buf);
|
||||
*ep++ = sign;
|
||||
ep = Format02d(ep, offset_hours);
|
||||
*ep++ = ':';
|
||||
ep = Format02d(ep, offset_minutes);
|
||||
*ep++ = ':';
|
||||
ep = Format02d(ep, offset_seconds);
|
||||
*ep++ = '\0';
|
||||
assert(ep == buf + sizeof(buf));
|
||||
return buf;
|
||||
}
|
||||
|
||||
std::string FixedOffsetToAbbr(const seconds& offset) {
|
||||
std::string abbr = FixedOffsetToName(offset);
|
||||
const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
|
||||
if (abbr.size() == prefix_len + 9) { // <prefix>+99:99:99
|
||||
abbr.erase(0, prefix_len); // +99:99:99
|
||||
abbr.erase(6, 1); // +99:9999
|
||||
abbr.erase(3, 1); // +999999
|
||||
if (abbr[5] == '0' && abbr[6] == '0') { // +999900
|
||||
abbr.erase(5, 2); // +9999
|
||||
if (abbr[3] == '0' && abbr[4] == '0') { // +9900
|
||||
abbr.erase(3, 2); // +99
|
||||
}
|
||||
}
|
||||
}
|
||||
return abbr;
|
||||
}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
52
Pods/abseil/absl/time/internal/cctz/src/time_zone_fixed.h
generated
Normal file
52
Pods/abseil/absl/time/internal/cctz/src/time_zone_fixed.h
generated
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// Helper functions for dealing with the names and abbreviations
|
||||
// of time zones that are a fixed offset (seconds east) from UTC.
|
||||
// FixedOffsetFromName() extracts the offset from a valid fixed-offset
|
||||
// name, while FixedOffsetToName() and FixedOffsetToAbbr() generate
|
||||
// the canonical zone name and abbreviation respectively for the given
|
||||
// offset.
|
||||
//
|
||||
// A fixed-offset name looks like "Fixed/UTC<+-><hours>:<mins>:<secs>".
|
||||
// Its abbreviation is of the form "UTC(<+->H?H(MM(SS)?)?)?" where the
|
||||
// optional pieces are omitted when their values are zero. (Note that
|
||||
// the sign is the opposite of that used in a POSIX TZ specification.)
|
||||
//
|
||||
// Note: FixedOffsetFromName() fails on syntax errors or when the parsed
|
||||
// offset exceeds 24 hours. FixedOffsetToName() and FixedOffsetToAbbr()
|
||||
// both produce "UTC" when the argument offset exceeds 24 hours.
|
||||
bool FixedOffsetFromName(const std::string& name, seconds* offset);
|
||||
std::string FixedOffsetToName(const seconds& offset);
|
||||
std::string FixedOffsetToAbbr(const seconds& offset);
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_
|
||||
1029
Pods/abseil/absl/time/internal/cctz/src/time_zone_format.cc
generated
Normal file
1029
Pods/abseil/absl/time/internal/cctz/src/time_zone_format.cc
generated
Normal file
@@ -0,0 +1,1029 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#if !defined(HAS_STRPTIME)
|
||||
#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__VXWORKS__)
|
||||
#define HAS_STRPTIME 1 // Assume everyone else has strptime().
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(HAS_STRPTIME) && HAS_STRPTIME
|
||||
#if !defined(_XOPEN_SOURCE) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||
#define _XOPEN_SOURCE 500 // Exposes definitions for SUSv2 (UNIX 98).
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
|
||||
// Include time.h directly since, by C++ standards, ctime doesn't have to
|
||||
// declare strptime.
|
||||
#include <time.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#if !HAS_STRPTIME
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#endif
|
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
||||
#include "time_zone_if.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
namespace detail {
|
||||
|
||||
namespace {
|
||||
|
||||
#if !HAS_STRPTIME
|
||||
// Build a strptime() using C++11's std::get_time().
|
||||
char* strptime(const char* s, const char* fmt, std::tm* tm) {
|
||||
std::istringstream input(s);
|
||||
input >> std::get_time(tm, fmt);
|
||||
if (input.fail()) return nullptr;
|
||||
return const_cast<char*>(s) +
|
||||
(input.eof() ? strlen(s) : static_cast<std::size_t>(input.tellg()));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Convert a cctz::weekday to a tm_wday value (0-6, Sunday = 0).
|
||||
int ToTmWday(weekday wd) {
|
||||
switch (wd) {
|
||||
case weekday::sunday:
|
||||
return 0;
|
||||
case weekday::monday:
|
||||
return 1;
|
||||
case weekday::tuesday:
|
||||
return 2;
|
||||
case weekday::wednesday:
|
||||
return 3;
|
||||
case weekday::thursday:
|
||||
return 4;
|
||||
case weekday::friday:
|
||||
return 5;
|
||||
case weekday::saturday:
|
||||
return 6;
|
||||
}
|
||||
return 0; /*NOTREACHED*/
|
||||
}
|
||||
|
||||
// Convert a tm_wday value (0-6, Sunday = 0) to a cctz::weekday.
|
||||
weekday FromTmWday(int tm_wday) {
|
||||
switch (tm_wday) {
|
||||
case 0:
|
||||
return weekday::sunday;
|
||||
case 1:
|
||||
return weekday::monday;
|
||||
case 2:
|
||||
return weekday::tuesday;
|
||||
case 3:
|
||||
return weekday::wednesday;
|
||||
case 4:
|
||||
return weekday::thursday;
|
||||
case 5:
|
||||
return weekday::friday;
|
||||
case 6:
|
||||
return weekday::saturday;
|
||||
}
|
||||
return weekday::sunday; /*NOTREACHED*/
|
||||
}
|
||||
|
||||
std::tm ToTM(const time_zone::absolute_lookup& al) {
|
||||
std::tm tm{};
|
||||
tm.tm_sec = al.cs.second();
|
||||
tm.tm_min = al.cs.minute();
|
||||
tm.tm_hour = al.cs.hour();
|
||||
tm.tm_mday = al.cs.day();
|
||||
tm.tm_mon = al.cs.month() - 1;
|
||||
|
||||
// Saturate tm.tm_year is cases of over/underflow.
|
||||
if (al.cs.year() < std::numeric_limits<int>::min() + 1900) {
|
||||
tm.tm_year = std::numeric_limits<int>::min();
|
||||
} else if (al.cs.year() - 1900 > std::numeric_limits<int>::max()) {
|
||||
tm.tm_year = std::numeric_limits<int>::max();
|
||||
} else {
|
||||
tm.tm_year = static_cast<int>(al.cs.year() - 1900);
|
||||
}
|
||||
|
||||
tm.tm_wday = ToTmWday(get_weekday(al.cs));
|
||||
tm.tm_yday = get_yearday(al.cs) - 1;
|
||||
tm.tm_isdst = al.is_dst ? 1 : 0;
|
||||
return tm;
|
||||
}
|
||||
|
||||
// Returns the week of the year [0:53] given a civil day and the day on
|
||||
// which weeks are defined to start.
|
||||
int ToWeek(const civil_day& cd, weekday week_start) {
|
||||
const civil_day d(cd.year() % 400, cd.month(), cd.day());
|
||||
return static_cast<int>((d - prev_weekday(civil_year(d), week_start)) / 7);
|
||||
}
|
||||
|
||||
const char kDigits[] = "0123456789";
|
||||
|
||||
// Formats a 64-bit integer in the given field width. Note that it is up
|
||||
// to the caller of Format64() [and Format02d()/FormatOffset()] to ensure
|
||||
// that there is sufficient space before ep to hold the conversion.
|
||||
char* Format64(char* ep, int width, std::int_fast64_t v) {
|
||||
bool neg = false;
|
||||
if (v < 0) {
|
||||
--width;
|
||||
neg = true;
|
||||
if (v == std::numeric_limits<std::int_fast64_t>::min()) {
|
||||
// Avoid negating minimum value.
|
||||
std::int_fast64_t last_digit = -(v % 10);
|
||||
v /= 10;
|
||||
if (last_digit < 0) {
|
||||
++v;
|
||||
last_digit += 10;
|
||||
}
|
||||
--width;
|
||||
*--ep = kDigits[last_digit];
|
||||
}
|
||||
v = -v;
|
||||
}
|
||||
do {
|
||||
--width;
|
||||
*--ep = kDigits[v % 10];
|
||||
} while (v /= 10);
|
||||
while (--width >= 0) *--ep = '0'; // zero pad
|
||||
if (neg) *--ep = '-';
|
||||
return ep;
|
||||
}
|
||||
|
||||
// Formats [0 .. 99] as %02d.
|
||||
char* Format02d(char* ep, int v) {
|
||||
*--ep = kDigits[v % 10];
|
||||
*--ep = kDigits[(v / 10) % 10];
|
||||
return ep;
|
||||
}
|
||||
|
||||
// Formats a UTC offset, like +00:00.
|
||||
char* FormatOffset(char* ep, int offset, const char* mode) {
|
||||
// TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
|
||||
// generate a "negative zero" when we're formatting a zero offset
|
||||
// as the result of a failed load_time_zone().
|
||||
char sign = '+';
|
||||
if (offset < 0) {
|
||||
offset = -offset; // bounded by 24h so no overflow
|
||||
sign = '-';
|
||||
}
|
||||
const int seconds = offset % 60;
|
||||
const int minutes = (offset /= 60) % 60;
|
||||
const int hours = offset /= 60;
|
||||
const char sep = mode[0];
|
||||
const bool ext = (sep != '\0' && mode[1] == '*');
|
||||
const bool ccc = (ext && mode[2] == ':');
|
||||
if (ext && (!ccc || seconds != 0)) {
|
||||
ep = Format02d(ep, seconds);
|
||||
*--ep = sep;
|
||||
} else {
|
||||
// If we're not rendering seconds, sub-minute negative offsets
|
||||
// should get a positive sign (e.g., offset=-10s => "+00:00").
|
||||
if (hours == 0 && minutes == 0) sign = '+';
|
||||
}
|
||||
if (!ccc || minutes != 0 || seconds != 0) {
|
||||
ep = Format02d(ep, minutes);
|
||||
if (sep != '\0') *--ep = sep;
|
||||
}
|
||||
ep = Format02d(ep, hours);
|
||||
*--ep = sign;
|
||||
return ep;
|
||||
}
|
||||
|
||||
// Formats a std::tm using strftime(3).
|
||||
void FormatTM(std::string* out, const std::string& fmt, const std::tm& tm) {
|
||||
// strftime(3) returns the number of characters placed in the output
|
||||
// array (which may be 0 characters). It also returns 0 to indicate
|
||||
// an error, like the array wasn't large enough. To accommodate this,
|
||||
// the following code grows the buffer size from 2x the format string
|
||||
// length up to 32x.
|
||||
for (std::size_t i = 2; i != 32; i *= 2) {
|
||||
std::size_t buf_size = fmt.size() * i;
|
||||
std::vector<char> buf(buf_size);
|
||||
if (std::size_t len = strftime(&buf[0], buf_size, fmt.c_str(), &tm)) {
|
||||
out->append(&buf[0], len);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used for %E#S/%E#f specifiers and for data values in parse().
|
||||
template <typename T>
|
||||
const char* ParseInt(const char* dp, int width, T min, T max, T* vp) {
|
||||
if (dp != nullptr) {
|
||||
const T kmin = std::numeric_limits<T>::min();
|
||||
bool erange = false;
|
||||
bool neg = false;
|
||||
T value = 0;
|
||||
if (*dp == '-') {
|
||||
neg = true;
|
||||
if (width <= 0 || --width != 0) {
|
||||
++dp;
|
||||
} else {
|
||||
dp = nullptr; // width was 1
|
||||
}
|
||||
}
|
||||
if (const char* const bp = dp) {
|
||||
while (const char* cp = strchr(kDigits, *dp)) {
|
||||
int d = static_cast<int>(cp - kDigits);
|
||||
if (d >= 10) break;
|
||||
if (value < kmin / 10) {
|
||||
erange = true;
|
||||
break;
|
||||
}
|
||||
value *= 10;
|
||||
if (value < kmin + d) {
|
||||
erange = true;
|
||||
break;
|
||||
}
|
||||
value -= d;
|
||||
dp += 1;
|
||||
if (width > 0 && --width == 0) break;
|
||||
}
|
||||
if (dp != bp && !erange && (neg || value != kmin)) {
|
||||
if (!neg || value != 0) {
|
||||
if (!neg) value = -value; // make positive
|
||||
if (min <= value && value <= max) {
|
||||
*vp = value;
|
||||
} else {
|
||||
dp = nullptr;
|
||||
}
|
||||
} else {
|
||||
dp = nullptr;
|
||||
}
|
||||
} else {
|
||||
dp = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
// The number of base-10 digits that can be represented by a signed 64-bit
|
||||
// integer. That is, 10^kDigits10_64 <= 2^63 - 1 < 10^(kDigits10_64 + 1).
|
||||
const int kDigits10_64 = 18;
|
||||
|
||||
// 10^n for everything that can be represented by a signed 64-bit integer.
|
||||
const std::int_fast64_t kExp10[kDigits10_64 + 1] = {
|
||||
1,
|
||||
10,
|
||||
100,
|
||||
1000,
|
||||
10000,
|
||||
100000,
|
||||
1000000,
|
||||
10000000,
|
||||
100000000,
|
||||
1000000000,
|
||||
10000000000,
|
||||
100000000000,
|
||||
1000000000000,
|
||||
10000000000000,
|
||||
100000000000000,
|
||||
1000000000000000,
|
||||
10000000000000000,
|
||||
100000000000000000,
|
||||
1000000000000000000,
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// Uses strftime(3) to format the given Time. The following extended format
|
||||
// specifiers are also supported:
|
||||
//
|
||||
// - %Ez - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm)
|
||||
// - %E*z - Full-resolution numeric UTC offset (+hh:mm:ss or -hh:mm:ss)
|
||||
// - %E#S - Seconds with # digits of fractional precision
|
||||
// - %E*S - Seconds with full fractional precision (a literal '*')
|
||||
// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
|
||||
// - %ET - The RFC3339 "date-time" separator "T"
|
||||
//
|
||||
// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
|
||||
// handled internally for performance reasons. strftime(3) is slow due to
|
||||
// a POSIX requirement to respect changes to ${TZ}.
|
||||
//
|
||||
// The TZ/GNU %s extension is handled internally because strftime() has
|
||||
// to use mktime() to generate it, and that assumes the local time zone.
|
||||
//
|
||||
// We also handle the %z and %Z specifiers to accommodate platforms that do
|
||||
// not support the tm_gmtoff and tm_zone extensions to std::tm.
|
||||
//
|
||||
// Requires that zero() <= fs < seconds(1).
|
||||
std::string format(const std::string& format, const time_point<seconds>& tp,
|
||||
const detail::femtoseconds& fs, const time_zone& tz) {
|
||||
std::string result;
|
||||
result.reserve(format.size()); // A reasonable guess for the result size.
|
||||
const time_zone::absolute_lookup al = tz.lookup(tp);
|
||||
const std::tm tm = ToTM(al);
|
||||
|
||||
// Scratch buffer for internal conversions.
|
||||
char buf[3 + kDigits10_64]; // enough for longest conversion
|
||||
char* const ep = buf + sizeof(buf);
|
||||
char* bp; // works back from ep
|
||||
|
||||
// Maintain three, disjoint subsequences that span format.
|
||||
// [format.begin() ... pending) : already formatted into result
|
||||
// [pending ... cur) : formatting pending, but no special cases
|
||||
// [cur ... format.end()) : unexamined
|
||||
// Initially, everything is in the unexamined part.
|
||||
const char* pending = format.c_str(); // NUL terminated
|
||||
const char* cur = pending;
|
||||
const char* end = pending + format.length();
|
||||
|
||||
while (cur != end) { // while something is unexamined
|
||||
// Moves cur to the next percent sign.
|
||||
const char* start = cur;
|
||||
while (cur != end && *cur != '%') ++cur;
|
||||
|
||||
// If the new pending text is all ordinary, copy it out.
|
||||
if (cur != start && pending == start) {
|
||||
result.append(pending, static_cast<std::size_t>(cur - pending));
|
||||
pending = start = cur;
|
||||
}
|
||||
|
||||
// Span the sequential percent signs.
|
||||
const char* percent = cur;
|
||||
while (cur != end && *cur == '%') ++cur;
|
||||
|
||||
// If the new pending text is all percents, copy out one
|
||||
// percent for every matched pair, then skip those pairs.
|
||||
if (cur != start && pending == start) {
|
||||
std::size_t escaped = static_cast<std::size_t>(cur - pending) / 2;
|
||||
result.append(pending, escaped);
|
||||
pending += escaped * 2;
|
||||
// Also copy out a single trailing percent.
|
||||
if (pending != cur && cur == end) {
|
||||
result.push_back(*pending++);
|
||||
}
|
||||
}
|
||||
|
||||
// Loop unless we have an unescaped percent.
|
||||
if (cur == end || (cur - percent) % 2 == 0) continue;
|
||||
|
||||
// Simple specifiers that we handle ourselves.
|
||||
if (strchr("YmdeUuWwHMSzZs%", *cur)) {
|
||||
if (cur - 1 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 1), tm);
|
||||
}
|
||||
switch (*cur) {
|
||||
case 'Y':
|
||||
// This avoids the tm.tm_year overflow problem for %Y, however
|
||||
// tm.tm_year will still be used by other specifiers like %D.
|
||||
bp = Format64(ep, 0, al.cs.year());
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'm':
|
||||
bp = Format02d(ep, al.cs.month());
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'd':
|
||||
case 'e':
|
||||
bp = Format02d(ep, al.cs.day());
|
||||
if (*cur == 'e' && *bp == '0') *bp = ' '; // for Windows
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'U':
|
||||
bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::sunday));
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'u':
|
||||
bp = Format64(ep, 0, tm.tm_wday ? tm.tm_wday : 7);
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'W':
|
||||
bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::monday));
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'w':
|
||||
bp = Format64(ep, 0, tm.tm_wday);
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'H':
|
||||
bp = Format02d(ep, al.cs.hour());
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'M':
|
||||
bp = Format02d(ep, al.cs.minute());
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'S':
|
||||
bp = Format02d(ep, al.cs.second());
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'z':
|
||||
bp = FormatOffset(ep, al.offset, "");
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'Z':
|
||||
result.append(al.abbr);
|
||||
break;
|
||||
case 's':
|
||||
bp = Format64(ep, 0, ToUnixSeconds(tp));
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case '%':
|
||||
result.push_back('%');
|
||||
break;
|
||||
}
|
||||
pending = ++cur;
|
||||
continue;
|
||||
}
|
||||
|
||||
// More complex specifiers that we handle ourselves.
|
||||
if (*cur == ':' && cur + 1 != end) {
|
||||
if (*(cur + 1) == 'z') {
|
||||
// Formats %:z.
|
||||
if (cur - 1 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 1), tm);
|
||||
}
|
||||
bp = FormatOffset(ep, al.offset, ":");
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
pending = cur += 2;
|
||||
continue;
|
||||
}
|
||||
if (*(cur + 1) == ':' && cur + 2 != end) {
|
||||
if (*(cur + 2) == 'z') {
|
||||
// Formats %::z.
|
||||
if (cur - 1 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 1), tm);
|
||||
}
|
||||
bp = FormatOffset(ep, al.offset, ":*");
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
pending = cur += 3;
|
||||
continue;
|
||||
}
|
||||
if (*(cur + 2) == ':' && cur + 3 != end) {
|
||||
if (*(cur + 3) == 'z') {
|
||||
// Formats %:::z.
|
||||
if (cur - 1 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 1), tm);
|
||||
}
|
||||
bp = FormatOffset(ep, al.offset, ":*:");
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
pending = cur += 4;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop if there is no E modifier.
|
||||
if (*cur != 'E' || ++cur == end) continue;
|
||||
|
||||
// Format our extensions.
|
||||
if (*cur == 'T') {
|
||||
// Formats %ET.
|
||||
if (cur - 2 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 2), tm);
|
||||
}
|
||||
result.append("T");
|
||||
pending = ++cur;
|
||||
} else if (*cur == 'z') {
|
||||
// Formats %Ez.
|
||||
if (cur - 2 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 2), tm);
|
||||
}
|
||||
bp = FormatOffset(ep, al.offset, ":");
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
pending = ++cur;
|
||||
} else if (*cur == '*' && cur + 1 != end && *(cur + 1) == 'z') {
|
||||
// Formats %E*z.
|
||||
if (cur - 2 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 2), tm);
|
||||
}
|
||||
bp = FormatOffset(ep, al.offset, ":*");
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
pending = cur += 2;
|
||||
} else if (*cur == '*' && cur + 1 != end &&
|
||||
(*(cur + 1) == 'S' || *(cur + 1) == 'f')) {
|
||||
// Formats %E*S or %E*F.
|
||||
if (cur - 2 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 2), tm);
|
||||
}
|
||||
char* cp = ep;
|
||||
bp = Format64(cp, 15, fs.count());
|
||||
while (cp != bp && cp[-1] == '0') --cp;
|
||||
switch (*(cur + 1)) {
|
||||
case 'S':
|
||||
if (cp != bp) *--bp = '.';
|
||||
bp = Format02d(bp, al.cs.second());
|
||||
break;
|
||||
case 'f':
|
||||
if (cp == bp) *--bp = '0';
|
||||
break;
|
||||
}
|
||||
result.append(bp, static_cast<std::size_t>(cp - bp));
|
||||
pending = cur += 2;
|
||||
} else if (*cur == '4' && cur + 1 != end && *(cur + 1) == 'Y') {
|
||||
// Formats %E4Y.
|
||||
if (cur - 2 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 2), tm);
|
||||
}
|
||||
bp = Format64(ep, 4, al.cs.year());
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
pending = cur += 2;
|
||||
} else if (std::isdigit(*cur)) {
|
||||
// Possibly found %E#S or %E#f.
|
||||
int n = 0;
|
||||
if (const char* np = ParseInt(cur, 0, 0, 1024, &n)) {
|
||||
if (*np == 'S' || *np == 'f') {
|
||||
// Formats %E#S or %E#f.
|
||||
if (cur - 2 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 2), tm);
|
||||
}
|
||||
bp = ep;
|
||||
if (n > 0) {
|
||||
if (n > kDigits10_64) n = kDigits10_64;
|
||||
bp = Format64(bp, n,
|
||||
(n > 15) ? fs.count() * kExp10[n - 15]
|
||||
: fs.count() / kExp10[15 - n]);
|
||||
if (*np == 'S') *--bp = '.';
|
||||
}
|
||||
if (*np == 'S') bp = Format02d(bp, al.cs.second());
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
pending = cur = ++np;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Formats any remaining data.
|
||||
if (end != pending) {
|
||||
FormatTM(&result, std::string(pending, end), tm);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const char* ParseOffset(const char* dp, const char* mode, int* offset) {
|
||||
if (dp != nullptr) {
|
||||
const char first = *dp++;
|
||||
if (first == '+' || first == '-') {
|
||||
char sep = mode[0];
|
||||
int hours = 0;
|
||||
int minutes = 0;
|
||||
int seconds = 0;
|
||||
const char* ap = ParseInt(dp, 2, 0, 23, &hours);
|
||||
if (ap != nullptr && ap - dp == 2) {
|
||||
dp = ap;
|
||||
if (sep != '\0' && *ap == sep) ++ap;
|
||||
const char* bp = ParseInt(ap, 2, 0, 59, &minutes);
|
||||
if (bp != nullptr && bp - ap == 2) {
|
||||
dp = bp;
|
||||
if (sep != '\0' && *bp == sep) ++bp;
|
||||
const char* cp = ParseInt(bp, 2, 0, 59, &seconds);
|
||||
if (cp != nullptr && cp - bp == 2) dp = cp;
|
||||
}
|
||||
*offset = ((hours * 60 + minutes) * 60) + seconds;
|
||||
if (first == '-') *offset = -*offset;
|
||||
} else {
|
||||
dp = nullptr;
|
||||
}
|
||||
} else if (first == 'Z' || first == 'z') { // Zulu
|
||||
*offset = 0;
|
||||
} else {
|
||||
dp = nullptr;
|
||||
}
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
const char* ParseZone(const char* dp, std::string* zone) {
|
||||
zone->clear();
|
||||
if (dp != nullptr) {
|
||||
while (*dp != '\0' && !std::isspace(*dp)) zone->push_back(*dp++);
|
||||
if (zone->empty()) dp = nullptr;
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
const char* ParseSubSeconds(const char* dp, detail::femtoseconds* subseconds) {
|
||||
if (dp != nullptr) {
|
||||
std::int_fast64_t v = 0;
|
||||
std::int_fast64_t exp = 0;
|
||||
const char* const bp = dp;
|
||||
while (const char* cp = strchr(kDigits, *dp)) {
|
||||
int d = static_cast<int>(cp - kDigits);
|
||||
if (d >= 10) break;
|
||||
if (exp < 15) {
|
||||
exp += 1;
|
||||
v *= 10;
|
||||
v += d;
|
||||
}
|
||||
++dp;
|
||||
}
|
||||
if (dp != bp) {
|
||||
v *= kExp10[15 - exp];
|
||||
*subseconds = detail::femtoseconds(v);
|
||||
} else {
|
||||
dp = nullptr;
|
||||
}
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
// Parses a string into a std::tm using strptime(3).
|
||||
const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) {
|
||||
if (dp != nullptr) {
|
||||
dp = strptime(dp, fmt, tm);
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
// Sets year, tm_mon and tm_mday given the year, week_num, and tm_wday,
|
||||
// and the day on which weeks are defined to start. Returns false if year
|
||||
// would need to move outside its bounds.
|
||||
bool FromWeek(int week_num, weekday week_start, year_t* year, std::tm* tm) {
|
||||
const civil_year y(*year % 400);
|
||||
civil_day cd = prev_weekday(y, week_start); // week 0
|
||||
cd = next_weekday(cd - 1, FromTmWday(tm->tm_wday)) + (week_num * 7);
|
||||
if (const year_t shift = cd.year() - y.year()) {
|
||||
if (shift > 0) {
|
||||
if (*year > std::numeric_limits<year_t>::max() - shift) return false;
|
||||
} else {
|
||||
if (*year < std::numeric_limits<year_t>::min() - shift) return false;
|
||||
}
|
||||
*year += shift;
|
||||
}
|
||||
tm->tm_mon = cd.month() - 1;
|
||||
tm->tm_mday = cd.day();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Uses strptime(3) to parse the given input. Supports the same extended
|
||||
// format specifiers as format(), although %E#S and %E*S are treated
|
||||
// identically (and similarly for %E#f and %E*f). %Ez and %E*z also accept
|
||||
// the same inputs. %ET accepts either 'T' or 't'.
|
||||
//
|
||||
// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
|
||||
// handled internally so that we can normally avoid strptime() altogether
|
||||
// (which is particularly helpful when the native implementation is broken).
|
||||
//
|
||||
// The TZ/GNU %s extension is handled internally because strptime() has to
|
||||
// use localtime_r() to generate it, and that assumes the local time zone.
|
||||
//
|
||||
// We also handle the %z specifier to accommodate platforms that do not
|
||||
// support the tm_gmtoff extension to std::tm. %Z is parsed but ignored.
|
||||
bool parse(const std::string& format, const std::string& input,
|
||||
const time_zone& tz, time_point<seconds>* sec,
|
||||
detail::femtoseconds* fs, std::string* err) {
|
||||
// The unparsed input.
|
||||
const char* data = input.c_str(); // NUL terminated
|
||||
|
||||
// Skips leading whitespace.
|
||||
while (std::isspace(*data)) ++data;
|
||||
|
||||
const year_t kyearmax = std::numeric_limits<year_t>::max();
|
||||
const year_t kyearmin = std::numeric_limits<year_t>::min();
|
||||
|
||||
// Sets default values for unspecified fields.
|
||||
bool saw_year = false;
|
||||
year_t year = 1970;
|
||||
std::tm tm{};
|
||||
tm.tm_year = 1970 - 1900;
|
||||
tm.tm_mon = 1 - 1; // Jan
|
||||
tm.tm_mday = 1;
|
||||
tm.tm_hour = 0;
|
||||
tm.tm_min = 0;
|
||||
tm.tm_sec = 0;
|
||||
tm.tm_wday = 4; // Thu
|
||||
tm.tm_yday = 0;
|
||||
tm.tm_isdst = 0;
|
||||
auto subseconds = detail::femtoseconds::zero();
|
||||
bool saw_offset = false;
|
||||
int offset = 0; // No offset from passed tz.
|
||||
std::string zone = "UTC";
|
||||
|
||||
const char* fmt = format.c_str(); // NUL terminated
|
||||
bool twelve_hour = false;
|
||||
bool afternoon = false;
|
||||
int week_num = -1;
|
||||
weekday week_start = weekday::sunday;
|
||||
|
||||
bool saw_percent_s = false;
|
||||
std::int_fast64_t percent_s = 0;
|
||||
|
||||
// Steps through format, one specifier at a time.
|
||||
while (data != nullptr && *fmt != '\0') {
|
||||
if (std::isspace(*fmt)) {
|
||||
while (std::isspace(*data)) ++data;
|
||||
while (std::isspace(*++fmt)) continue;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*fmt != '%') {
|
||||
if (*data == *fmt) {
|
||||
++data;
|
||||
++fmt;
|
||||
} else {
|
||||
data = nullptr;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const char* percent = fmt;
|
||||
if (*++fmt == '\0') {
|
||||
data = nullptr;
|
||||
continue;
|
||||
}
|
||||
switch (*fmt++) {
|
||||
case 'Y':
|
||||
// Symmetrically with FormatTime(), directly handing %Y avoids the
|
||||
// tm.tm_year overflow problem. However, tm.tm_year will still be
|
||||
// used by other specifiers like %D.
|
||||
data = ParseInt(data, 0, kyearmin, kyearmax, &year);
|
||||
if (data != nullptr) saw_year = true;
|
||||
continue;
|
||||
case 'm':
|
||||
data = ParseInt(data, 2, 1, 12, &tm.tm_mon);
|
||||
if (data != nullptr) tm.tm_mon -= 1;
|
||||
week_num = -1;
|
||||
continue;
|
||||
case 'd':
|
||||
case 'e':
|
||||
data = ParseInt(data, 2, 1, 31, &tm.tm_mday);
|
||||
week_num = -1;
|
||||
continue;
|
||||
case 'U':
|
||||
data = ParseInt(data, 0, 0, 53, &week_num);
|
||||
week_start = weekday::sunday;
|
||||
continue;
|
||||
case 'W':
|
||||
data = ParseInt(data, 0, 0, 53, &week_num);
|
||||
week_start = weekday::monday;
|
||||
continue;
|
||||
case 'u':
|
||||
data = ParseInt(data, 0, 1, 7, &tm.tm_wday);
|
||||
if (data != nullptr) tm.tm_wday %= 7;
|
||||
continue;
|
||||
case 'w':
|
||||
data = ParseInt(data, 0, 0, 6, &tm.tm_wday);
|
||||
continue;
|
||||
case 'H':
|
||||
data = ParseInt(data, 2, 0, 23, &tm.tm_hour);
|
||||
twelve_hour = false;
|
||||
continue;
|
||||
case 'M':
|
||||
data = ParseInt(data, 2, 0, 59, &tm.tm_min);
|
||||
continue;
|
||||
case 'S':
|
||||
data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
|
||||
continue;
|
||||
case 'I':
|
||||
case 'l':
|
||||
case 'r': // probably uses %I
|
||||
twelve_hour = true;
|
||||
break;
|
||||
case 'R': // uses %H
|
||||
case 'T': // uses %H
|
||||
case 'c': // probably uses %H
|
||||
case 'X': // probably uses %H
|
||||
twelve_hour = false;
|
||||
break;
|
||||
case 'z':
|
||||
data = ParseOffset(data, "", &offset);
|
||||
if (data != nullptr) saw_offset = true;
|
||||
continue;
|
||||
case 'Z': // ignored; zone abbreviations are ambiguous
|
||||
data = ParseZone(data, &zone);
|
||||
continue;
|
||||
case 's':
|
||||
data =
|
||||
ParseInt(data, 0, std::numeric_limits<std::int_fast64_t>::min(),
|
||||
std::numeric_limits<std::int_fast64_t>::max(), &percent_s);
|
||||
if (data != nullptr) saw_percent_s = true;
|
||||
continue;
|
||||
case ':':
|
||||
if (fmt[0] == 'z' ||
|
||||
(fmt[0] == ':' &&
|
||||
(fmt[1] == 'z' || (fmt[1] == ':' && fmt[2] == 'z')))) {
|
||||
data = ParseOffset(data, ":", &offset);
|
||||
if (data != nullptr) saw_offset = true;
|
||||
fmt += (fmt[0] == 'z') ? 1 : (fmt[1] == 'z') ? 2 : 3;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case '%':
|
||||
data = (*data == '%' ? data + 1 : nullptr);
|
||||
continue;
|
||||
case 'E':
|
||||
if (fmt[0] == 'T') {
|
||||
if (*data == 'T' || *data == 't') {
|
||||
++data;
|
||||
++fmt;
|
||||
} else {
|
||||
data = nullptr;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (fmt[0] == 'z' || (fmt[0] == '*' && fmt[1] == 'z')) {
|
||||
data = ParseOffset(data, ":", &offset);
|
||||
if (data != nullptr) saw_offset = true;
|
||||
fmt += (fmt[0] == 'z') ? 1 : 2;
|
||||
continue;
|
||||
}
|
||||
if (fmt[0] == '*' && fmt[1] == 'S') {
|
||||
data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
|
||||
if (data != nullptr && *data == '.') {
|
||||
data = ParseSubSeconds(data + 1, &subseconds);
|
||||
}
|
||||
fmt += 2;
|
||||
continue;
|
||||
}
|
||||
if (fmt[0] == '*' && fmt[1] == 'f') {
|
||||
if (data != nullptr && std::isdigit(*data)) {
|
||||
data = ParseSubSeconds(data, &subseconds);
|
||||
}
|
||||
fmt += 2;
|
||||
continue;
|
||||
}
|
||||
if (fmt[0] == '4' && fmt[1] == 'Y') {
|
||||
const char* bp = data;
|
||||
data = ParseInt(data, 4, year_t{-999}, year_t{9999}, &year);
|
||||
if (data != nullptr) {
|
||||
if (data - bp == 4) {
|
||||
saw_year = true;
|
||||
} else {
|
||||
data = nullptr; // stopped too soon
|
||||
}
|
||||
}
|
||||
fmt += 2;
|
||||
continue;
|
||||
}
|
||||
if (std::isdigit(*fmt)) {
|
||||
int n = 0; // value ignored
|
||||
if (const char* np = ParseInt(fmt, 0, 0, 1024, &n)) {
|
||||
if (*np == 'S') {
|
||||
data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
|
||||
if (data != nullptr && *data == '.') {
|
||||
data = ParseSubSeconds(data + 1, &subseconds);
|
||||
}
|
||||
fmt = ++np;
|
||||
continue;
|
||||
}
|
||||
if (*np == 'f') {
|
||||
if (data != nullptr && std::isdigit(*data)) {
|
||||
data = ParseSubSeconds(data, &subseconds);
|
||||
}
|
||||
fmt = ++np;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*fmt == 'c') twelve_hour = false; // probably uses %H
|
||||
if (*fmt == 'X') twelve_hour = false; // probably uses %H
|
||||
if (*fmt != '\0') ++fmt;
|
||||
break;
|
||||
case 'O':
|
||||
if (*fmt == 'H') twelve_hour = false;
|
||||
if (*fmt == 'I') twelve_hour = true;
|
||||
if (*fmt != '\0') ++fmt;
|
||||
break;
|
||||
}
|
||||
|
||||
// Parses the current specifier.
|
||||
const char* orig_data = data;
|
||||
std::string spec(percent, static_cast<std::size_t>(fmt - percent));
|
||||
data = ParseTM(data, spec.c_str(), &tm);
|
||||
|
||||
// If we successfully parsed %p we need to remember whether the result
|
||||
// was AM or PM so that we can adjust tm_hour before time_zone::lookup().
|
||||
// So reparse the input with a known AM hour, and check if it is shifted
|
||||
// to a PM hour.
|
||||
if (spec == "%p" && data != nullptr) {
|
||||
std::string test_input = "1";
|
||||
test_input.append(orig_data, static_cast<std::size_t>(data - orig_data));
|
||||
const char* test_data = test_input.c_str();
|
||||
std::tm tmp{};
|
||||
ParseTM(test_data, "%I%p", &tmp);
|
||||
afternoon = (tmp.tm_hour == 13);
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust a 12-hour tm_hour value if it should be in the afternoon.
|
||||
if (twelve_hour && afternoon && tm.tm_hour < 12) {
|
||||
tm.tm_hour += 12;
|
||||
}
|
||||
|
||||
if (data == nullptr) {
|
||||
if (err != nullptr) *err = "Failed to parse input";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip any remaining whitespace.
|
||||
while (std::isspace(*data)) ++data;
|
||||
|
||||
// parse() must consume the entire input string.
|
||||
if (*data != '\0') {
|
||||
if (err != nullptr) *err = "Illegal trailing data in input string";
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we saw %s then we ignore anything else and return that time.
|
||||
if (saw_percent_s) {
|
||||
*sec = FromUnixSeconds(percent_s);
|
||||
*fs = detail::femtoseconds::zero();
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we saw %z, %Ez, or %E*z then we want to interpret the parsed fields
|
||||
// in UTC and then shift by that offset. Otherwise we want to interpret
|
||||
// the fields directly in the passed time_zone.
|
||||
time_zone ptz = saw_offset ? utc_time_zone() : tz;
|
||||
|
||||
// Allows a leap second of 60 to normalize forward to the following ":00".
|
||||
if (tm.tm_sec == 60) {
|
||||
tm.tm_sec -= 1;
|
||||
offset -= 1;
|
||||
subseconds = detail::femtoseconds::zero();
|
||||
}
|
||||
|
||||
if (!saw_year) {
|
||||
year = year_t{tm.tm_year};
|
||||
if (year > kyearmax - 1900) {
|
||||
// Platform-dependent, maybe unreachable.
|
||||
if (err != nullptr) *err = "Out-of-range year";
|
||||
return false;
|
||||
}
|
||||
year += 1900;
|
||||
}
|
||||
|
||||
// Compute year, tm.tm_mon and tm.tm_mday if we parsed a week number.
|
||||
if (week_num != -1) {
|
||||
if (!FromWeek(week_num, week_start, &year, &tm)) {
|
||||
if (err != nullptr) *err = "Out-of-range field";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const int month = tm.tm_mon + 1;
|
||||
civil_second cs(year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||
|
||||
// parse() should not allow normalization. Due to the restricted field
|
||||
// ranges above (see ParseInt()), the only possibility is for days to roll
|
||||
// into months. That is, parsing "Sep 31" should not produce "Oct 1".
|
||||
if (cs.month() != month || cs.day() != tm.tm_mday) {
|
||||
if (err != nullptr) *err = "Out-of-range field";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Accounts for the offset adjustment before converting to absolute time.
|
||||
if ((offset < 0 && cs > civil_second::max() + offset) ||
|
||||
(offset > 0 && cs < civil_second::min() + offset)) {
|
||||
if (err != nullptr) *err = "Out-of-range field";
|
||||
return false;
|
||||
}
|
||||
cs -= offset;
|
||||
|
||||
const auto tp = ptz.lookup(cs).pre;
|
||||
// Checks for overflow/underflow and returns an error as necessary.
|
||||
if (tp == time_point<seconds>::max()) {
|
||||
const auto al = ptz.lookup(time_point<seconds>::max());
|
||||
if (cs > al.cs) {
|
||||
if (err != nullptr) *err = "Out-of-range field";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (tp == time_point<seconds>::min()) {
|
||||
const auto al = ptz.lookup(time_point<seconds>::min());
|
||||
if (cs < al.cs) {
|
||||
if (err != nullptr) *err = "Out-of-range field";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*sec = tp;
|
||||
*fs = subseconds;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
47
Pods/abseil/absl/time/internal/cctz/src/time_zone_if.cc
generated
Normal file
47
Pods/abseil/absl/time/internal/cctz/src/time_zone_if.cc
generated
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "time_zone_if.h"
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "time_zone_info.h"
|
||||
#include "time_zone_libc.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
std::unique_ptr<TimeZoneIf> TimeZoneIf::UTC() { return TimeZoneInfo::UTC(); }
|
||||
|
||||
std::unique_ptr<TimeZoneIf> TimeZoneIf::Make(const std::string& name) {
|
||||
// Support "libc:localtime" and "libc:*" to access the legacy
|
||||
// localtime and UTC support respectively from the C library.
|
||||
// NOTE: The "libc:*" zones are internal, test-only interfaces, and
|
||||
// are subject to change/removal without notice. Do not use them.
|
||||
if (name.compare(0, 5, "libc:") == 0) {
|
||||
return TimeZoneLibC::Make(name.substr(5));
|
||||
}
|
||||
|
||||
// Otherwise use the "zoneinfo" implementation.
|
||||
return TimeZoneInfo::Make(name);
|
||||
}
|
||||
|
||||
// Defined out-of-line to avoid emitting a weak vtable in all TUs.
|
||||
TimeZoneIf::~TimeZoneIf() {}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
80
Pods/abseil/absl/time/internal/cctz/src/time_zone_if.h
generated
Normal file
80
Pods/abseil/absl/time/internal/cctz/src/time_zone_if.h
generated
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// A simple interface used to hide time-zone complexities from time_zone::Impl.
|
||||
// Subclasses implement the functions for civil-time conversions in the zone.
|
||||
class TimeZoneIf {
|
||||
public:
|
||||
// Factory functions for TimeZoneIf implementations.
|
||||
static std::unique_ptr<TimeZoneIf> UTC(); // never fails
|
||||
static std::unique_ptr<TimeZoneIf> Make(const std::string& name);
|
||||
|
||||
virtual ~TimeZoneIf();
|
||||
|
||||
virtual time_zone::absolute_lookup BreakTime(
|
||||
const time_point<seconds>& tp) const = 0;
|
||||
virtual time_zone::civil_lookup MakeTime(const civil_second& cs) const = 0;
|
||||
|
||||
virtual bool NextTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const = 0;
|
||||
virtual bool PrevTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const = 0;
|
||||
|
||||
virtual std::string Version() const = 0;
|
||||
virtual std::string Description() const = 0;
|
||||
|
||||
protected:
|
||||
TimeZoneIf() = default;
|
||||
TimeZoneIf(const TimeZoneIf&) = delete;
|
||||
TimeZoneIf& operator=(const TimeZoneIf&) = delete;
|
||||
};
|
||||
|
||||
// Convert between time_point<seconds> and a count of seconds since the
|
||||
// Unix epoch. We assume that the std::chrono::system_clock and the
|
||||
// Unix clock are second aligned, and that the results are representable.
|
||||
// (That is, that they share an epoch, which is required since C++20.)
|
||||
inline std::int_fast64_t ToUnixSeconds(const time_point<seconds>& tp) {
|
||||
return (tp - std::chrono::time_point_cast<seconds>(
|
||||
std::chrono::system_clock::from_time_t(0)))
|
||||
.count();
|
||||
}
|
||||
inline time_point<seconds> FromUnixSeconds(std::int_fast64_t t) {
|
||||
return std::chrono::time_point_cast<seconds>(
|
||||
std::chrono::system_clock::from_time_t(0)) +
|
||||
seconds(t);
|
||||
}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_
|
||||
115
Pods/abseil/absl/time/internal/cctz/src/time_zone_impl.cc
generated
Normal file
115
Pods/abseil/absl/time/internal/cctz/src/time_zone_impl.cc
generated
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "time_zone_impl.h"
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "time_zone_fixed.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
namespace {
|
||||
|
||||
// time_zone::Impls are linked into a map to support fast lookup by name.
|
||||
using TimeZoneImplByName =
|
||||
std::unordered_map<std::string, const time_zone::Impl*>;
|
||||
TimeZoneImplByName* time_zone_map = nullptr;
|
||||
|
||||
// Mutual exclusion for time_zone_map.
|
||||
std::mutex& TimeZoneMutex() {
|
||||
// This mutex is intentionally "leaked" to avoid the static deinitialization
|
||||
// order fiasco (std::mutex's destructor is not trivial on many platforms).
|
||||
static std::mutex* time_zone_mutex = new std::mutex;
|
||||
return *time_zone_mutex;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
time_zone time_zone::Impl::UTC() { return time_zone(UTCImpl()); }
|
||||
|
||||
bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
|
||||
const Impl* const utc_impl = UTCImpl();
|
||||
|
||||
// Check for UTC (which is never a key in time_zone_map).
|
||||
auto offset = seconds::zero();
|
||||
if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) {
|
||||
*tz = time_zone(utc_impl);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check whether the time zone has already been loaded.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(TimeZoneMutex());
|
||||
if (time_zone_map != nullptr) {
|
||||
TimeZoneImplByName::const_iterator itr = time_zone_map->find(name);
|
||||
if (itr != time_zone_map->end()) {
|
||||
*tz = time_zone(itr->second);
|
||||
return itr->second != utc_impl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load the new time zone (outside the lock).
|
||||
std::unique_ptr<const Impl> new_impl(new Impl(name));
|
||||
|
||||
// Add the new time zone to the map.
|
||||
std::lock_guard<std::mutex> lock(TimeZoneMutex());
|
||||
if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName;
|
||||
const Impl*& impl = (*time_zone_map)[name];
|
||||
if (impl == nullptr) { // this thread won any load race
|
||||
impl = new_impl->zone_ ? new_impl.release() : utc_impl;
|
||||
}
|
||||
*tz = time_zone(impl);
|
||||
return impl != utc_impl;
|
||||
}
|
||||
|
||||
void time_zone::Impl::ClearTimeZoneMapTestOnly() {
|
||||
std::lock_guard<std::mutex> lock(TimeZoneMutex());
|
||||
if (time_zone_map != nullptr) {
|
||||
// Existing time_zone::Impl* entries are in the wild, so we can't delete
|
||||
// them. Instead, we move them to a private container, where they are
|
||||
// logically unreachable but not "leaked". Future requests will result
|
||||
// in reloading the data.
|
||||
static auto* cleared = new std::deque<const time_zone::Impl*>;
|
||||
for (const auto& element : *time_zone_map) {
|
||||
cleared->push_back(element.second);
|
||||
}
|
||||
time_zone_map->clear();
|
||||
}
|
||||
}
|
||||
|
||||
time_zone::Impl::Impl() : name_("UTC"), zone_(TimeZoneIf::UTC()) {}
|
||||
|
||||
time_zone::Impl::Impl(const std::string& name)
|
||||
: name_(name), zone_(TimeZoneIf::Make(name_)) {}
|
||||
|
||||
const time_zone::Impl* time_zone::Impl::UTCImpl() {
|
||||
static const Impl* utc_impl = new Impl;
|
||||
return utc_impl;
|
||||
}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
97
Pods/abseil/absl/time/internal/cctz/src/time_zone_impl.h
generated
Normal file
97
Pods/abseil/absl/time/internal/cctz/src/time_zone_impl.h
generated
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
#include "time_zone_if.h"
|
||||
#include "time_zone_info.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// time_zone::Impl is the internal object referenced by a cctz::time_zone.
|
||||
class time_zone::Impl {
|
||||
public:
|
||||
// The UTC time zone. Also used for other time zones that fail to load.
|
||||
static time_zone UTC();
|
||||
|
||||
// Load a named time zone. Returns false if the name is invalid, or if
|
||||
// some other kind of error occurs. Note that loading "UTC" never fails.
|
||||
static bool LoadTimeZone(const std::string& name, time_zone* tz);
|
||||
|
||||
// Clears the map of cached time zones. Primarily for use in benchmarks
|
||||
// that gauge the performance of loading/parsing the time-zone data.
|
||||
static void ClearTimeZoneMapTestOnly();
|
||||
|
||||
// The primary key is the time-zone ID (e.g., "America/New_York").
|
||||
const std::string& Name() const {
|
||||
// TODO: It would nice if the zoneinfo data included the zone name.
|
||||
return name_;
|
||||
}
|
||||
|
||||
// Breaks a time_point down to civil-time components in this time zone.
|
||||
time_zone::absolute_lookup BreakTime(const time_point<seconds>& tp) const {
|
||||
return zone_->BreakTime(tp);
|
||||
}
|
||||
|
||||
// Converts the civil-time components in this time zone into a time_point.
|
||||
// That is, the opposite of BreakTime(). The requested civil time may be
|
||||
// ambiguous or illegal due to a change of UTC offset.
|
||||
time_zone::civil_lookup MakeTime(const civil_second& cs) const {
|
||||
return zone_->MakeTime(cs);
|
||||
}
|
||||
|
||||
// Finds the time of the next/previous offset change in this time zone.
|
||||
bool NextTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const {
|
||||
return zone_->NextTransition(tp, trans);
|
||||
}
|
||||
bool PrevTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const {
|
||||
return zone_->PrevTransition(tp, trans);
|
||||
}
|
||||
|
||||
// Returns an implementation-defined version string for this time zone.
|
||||
std::string Version() const { return zone_->Version(); }
|
||||
|
||||
// Returns an implementation-defined description of this time zone.
|
||||
std::string Description() const { return zone_->Description(); }
|
||||
|
||||
private:
|
||||
Impl();
|
||||
explicit Impl(const std::string& name);
|
||||
Impl(const Impl&) = delete;
|
||||
Impl& operator=(const Impl&) = delete;
|
||||
|
||||
static const Impl* UTCImpl();
|
||||
|
||||
const std::string name_;
|
||||
std::unique_ptr<TimeZoneIf> zone_;
|
||||
};
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_
|
||||
1070
Pods/abseil/absl/time/internal/cctz/src/time_zone_info.cc
generated
Normal file
1070
Pods/abseil/absl/time/internal/cctz/src/time_zone_info.cc
generated
Normal file
@@ -0,0 +1,1070 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// This file implements the TimeZoneIf interface using the "zoneinfo"
|
||||
// data provided by the IANA Time Zone Database (i.e., the only real game
|
||||
// in town).
|
||||
//
|
||||
// TimeZoneInfo represents the history of UTC-offset changes within a time
|
||||
// zone. Most changes are due to daylight-saving rules, but occasionally
|
||||
// shifts are made to the time-zone's base offset. The database only attempts
|
||||
// to be definitive for times since 1970, so be wary of local-time conversions
|
||||
// before that. Also, rule and zone-boundary changes are made at the whim
|
||||
// of governments, so the conversion of future times needs to be taken with
|
||||
// a grain of salt.
|
||||
//
|
||||
// For more information see tzfile(5), http://www.iana.org/time-zones, or
|
||||
// https://en.wikipedia.org/wiki/Zoneinfo.
|
||||
//
|
||||
// Note that we assume the proleptic Gregorian calendar and 60-second
|
||||
// minutes throughout.
|
||||
|
||||
#include "time_zone_info.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
||||
#include "time_zone_fixed.h"
|
||||
#include "time_zone_posix.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
namespace {
|
||||
|
||||
inline bool IsLeap(year_t year) {
|
||||
return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0);
|
||||
}
|
||||
|
||||
// The number of days in non-leap and leap years respectively.
|
||||
const std::int_least32_t kDaysPerYear[2] = {365, 366};
|
||||
|
||||
// The day offsets of the beginning of each (1-based) month in non-leap and
|
||||
// leap years respectively (e.g., 335 days before December in a leap year).
|
||||
const std::int_least16_t kMonthOffsets[2][1 + 12 + 1] = {
|
||||
{-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
|
||||
{-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
|
||||
};
|
||||
|
||||
// We reject leap-second encoded zoneinfo and so assume 60-second minutes.
|
||||
const std::int_least32_t kSecsPerDay = 24 * 60 * 60;
|
||||
|
||||
// 400-year chunks always have 146097 days (20871 weeks).
|
||||
const std::int_least64_t kSecsPer400Years = 146097LL * kSecsPerDay;
|
||||
|
||||
// Like kDaysPerYear[] but scaled up by a factor of kSecsPerDay.
|
||||
const std::int_least32_t kSecsPerYear[2] = {
|
||||
365 * kSecsPerDay,
|
||||
366 * kSecsPerDay,
|
||||
};
|
||||
|
||||
// Convert a cctz::weekday to a POSIX TZ weekday number (0==Sun, ..., 6=Sat).
|
||||
inline int ToPosixWeekday(weekday wd) {
|
||||
switch (wd) {
|
||||
case weekday::sunday:
|
||||
return 0;
|
||||
case weekday::monday:
|
||||
return 1;
|
||||
case weekday::tuesday:
|
||||
return 2;
|
||||
case weekday::wednesday:
|
||||
return 3;
|
||||
case weekday::thursday:
|
||||
return 4;
|
||||
case weekday::friday:
|
||||
return 5;
|
||||
case weekday::saturday:
|
||||
return 6;
|
||||
}
|
||||
return 0; /*NOTREACHED*/
|
||||
}
|
||||
|
||||
// Single-byte, unsigned numeric values are encoded directly.
|
||||
inline std::uint_fast8_t Decode8(const char* cp) {
|
||||
return static_cast<std::uint_fast8_t>(*cp) & 0xff;
|
||||
}
|
||||
|
||||
// Multi-byte, numeric values are encoded using a MSB first,
|
||||
// twos-complement representation. These helpers decode, from
|
||||
// the given address, 4-byte and 8-byte values respectively.
|
||||
// Note: If int_fastXX_t == intXX_t and this machine is not
|
||||
// twos complement, then there will be at least one input value
|
||||
// we cannot represent.
|
||||
std::int_fast32_t Decode32(const char* cp) {
|
||||
std::uint_fast32_t v = 0;
|
||||
for (int i = 0; i != (32 / 8); ++i) v = (v << 8) | Decode8(cp++);
|
||||
const std::int_fast32_t s32max = 0x7fffffff;
|
||||
const auto s32maxU = static_cast<std::uint_fast32_t>(s32max);
|
||||
if (v <= s32maxU) return static_cast<std::int_fast32_t>(v);
|
||||
return static_cast<std::int_fast32_t>(v - s32maxU - 1) - s32max - 1;
|
||||
}
|
||||
|
||||
std::int_fast64_t Decode64(const char* cp) {
|
||||
std::uint_fast64_t v = 0;
|
||||
for (int i = 0; i != (64 / 8); ++i) v = (v << 8) | Decode8(cp++);
|
||||
const std::int_fast64_t s64max = 0x7fffffffffffffff;
|
||||
const auto s64maxU = static_cast<std::uint_fast64_t>(s64max);
|
||||
if (v <= s64maxU) return static_cast<std::int_fast64_t>(v);
|
||||
return static_cast<std::int_fast64_t>(v - s64maxU - 1) - s64max - 1;
|
||||
}
|
||||
|
||||
struct Header { // counts of:
|
||||
std::size_t timecnt; // transition times
|
||||
std::size_t typecnt; // transition types
|
||||
std::size_t charcnt; // zone abbreviation characters
|
||||
std::size_t leapcnt; // leap seconds (we expect none)
|
||||
std::size_t ttisstdcnt; // UTC/local indicators (unused)
|
||||
std::size_t ttisutcnt; // standard/wall indicators (unused)
|
||||
|
||||
bool Build(const tzhead& tzh);
|
||||
std::size_t DataLength(std::size_t time_len) const;
|
||||
};
|
||||
|
||||
// Builds the in-memory header using the raw bytes from the file.
|
||||
bool Header::Build(const tzhead& tzh) {
|
||||
std::int_fast32_t v;
|
||||
if ((v = Decode32(tzh.tzh_timecnt)) < 0) return false;
|
||||
timecnt = static_cast<std::size_t>(v);
|
||||
if ((v = Decode32(tzh.tzh_typecnt)) < 0) return false;
|
||||
typecnt = static_cast<std::size_t>(v);
|
||||
if ((v = Decode32(tzh.tzh_charcnt)) < 0) return false;
|
||||
charcnt = static_cast<std::size_t>(v);
|
||||
if ((v = Decode32(tzh.tzh_leapcnt)) < 0) return false;
|
||||
leapcnt = static_cast<std::size_t>(v);
|
||||
if ((v = Decode32(tzh.tzh_ttisstdcnt)) < 0) return false;
|
||||
ttisstdcnt = static_cast<std::size_t>(v);
|
||||
if ((v = Decode32(tzh.tzh_ttisutcnt)) < 0) return false;
|
||||
ttisutcnt = static_cast<std::size_t>(v);
|
||||
return true;
|
||||
}
|
||||
|
||||
// How many bytes of data are associated with this header. The result
|
||||
// depends upon whether this is a section with 4-byte or 8-byte times.
|
||||
std::size_t Header::DataLength(std::size_t time_len) const {
|
||||
std::size_t len = 0;
|
||||
len += (time_len + 1) * timecnt; // unix_time + type_index
|
||||
len += (4 + 1 + 1) * typecnt; // utc_offset + is_dst + abbr_index
|
||||
len += 1 * charcnt; // abbreviations
|
||||
len += (time_len + 4) * leapcnt; // leap-time + TAI-UTC
|
||||
len += 1 * ttisstdcnt; // UTC/local indicators
|
||||
len += 1 * ttisutcnt; // standard/wall indicators
|
||||
return len;
|
||||
}
|
||||
|
||||
// Does the rule for future transitions call for year-round daylight time?
|
||||
// See tz/zic.c:stringzone() for the details on how such rules are encoded.
|
||||
bool AllYearDST(const PosixTimeZone& posix) {
|
||||
if (posix.dst_start.date.fmt != PosixTransition::N) return false;
|
||||
if (posix.dst_start.date.n.day != 0) return false;
|
||||
if (posix.dst_start.time.offset != 0) return false;
|
||||
|
||||
if (posix.dst_end.date.fmt != PosixTransition::J) return false;
|
||||
if (posix.dst_end.date.j.day != kDaysPerYear[0]) return false;
|
||||
const auto offset = posix.std_offset - posix.dst_offset;
|
||||
if (posix.dst_end.time.offset + offset != kSecsPerDay) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Generate a year-relative offset for a PosixTransition.
|
||||
std::int_fast64_t TransOffset(bool leap_year, int jan1_weekday,
|
||||
const PosixTransition& pt) {
|
||||
std::int_fast64_t days = 0;
|
||||
switch (pt.date.fmt) {
|
||||
case PosixTransition::J: {
|
||||
days = pt.date.j.day;
|
||||
if (!leap_year || days < kMonthOffsets[1][3]) days -= 1;
|
||||
break;
|
||||
}
|
||||
case PosixTransition::N: {
|
||||
days = pt.date.n.day;
|
||||
break;
|
||||
}
|
||||
case PosixTransition::M: {
|
||||
const bool last_week = (pt.date.m.week == 5);
|
||||
days = kMonthOffsets[leap_year][pt.date.m.month + last_week];
|
||||
const std::int_fast64_t weekday = (jan1_weekday + days) % 7;
|
||||
if (last_week) {
|
||||
days -= (weekday + 7 - 1 - pt.date.m.weekday) % 7 + 1;
|
||||
} else {
|
||||
days += (pt.date.m.weekday + 7 - weekday) % 7;
|
||||
days += (pt.date.m.week - 1) * 7;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (days * kSecsPerDay) + pt.time.offset;
|
||||
}
|
||||
|
||||
inline time_zone::civil_lookup MakeUnique(const time_point<seconds>& tp) {
|
||||
time_zone::civil_lookup cl;
|
||||
cl.kind = time_zone::civil_lookup::UNIQUE;
|
||||
cl.pre = cl.trans = cl.post = tp;
|
||||
return cl;
|
||||
}
|
||||
|
||||
inline time_zone::civil_lookup MakeUnique(std::int_fast64_t unix_time) {
|
||||
return MakeUnique(FromUnixSeconds(unix_time));
|
||||
}
|
||||
|
||||
inline time_zone::civil_lookup MakeSkipped(const Transition& tr,
|
||||
const civil_second& cs) {
|
||||
time_zone::civil_lookup cl;
|
||||
cl.kind = time_zone::civil_lookup::SKIPPED;
|
||||
cl.pre = FromUnixSeconds(tr.unix_time - 1 + (cs - tr.prev_civil_sec));
|
||||
cl.trans = FromUnixSeconds(tr.unix_time);
|
||||
cl.post = FromUnixSeconds(tr.unix_time - (tr.civil_sec - cs));
|
||||
return cl;
|
||||
}
|
||||
|
||||
inline time_zone::civil_lookup MakeRepeated(const Transition& tr,
|
||||
const civil_second& cs) {
|
||||
time_zone::civil_lookup cl;
|
||||
cl.kind = time_zone::civil_lookup::REPEATED;
|
||||
cl.pre = FromUnixSeconds(tr.unix_time - 1 - (tr.prev_civil_sec - cs));
|
||||
cl.trans = FromUnixSeconds(tr.unix_time);
|
||||
cl.post = FromUnixSeconds(tr.unix_time + (cs - tr.civil_sec));
|
||||
return cl;
|
||||
}
|
||||
|
||||
inline civil_second YearShift(const civil_second& cs, year_t shift) {
|
||||
return civil_second(cs.year() + shift, cs.month(), cs.day(), cs.hour(),
|
||||
cs.minute(), cs.second());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Find/make a transition type with these attributes.
|
||||
bool TimeZoneInfo::GetTransitionType(std::int_fast32_t utc_offset, bool is_dst,
|
||||
const std::string& abbr,
|
||||
std::uint_least8_t* index) {
|
||||
std::size_t type_index = 0;
|
||||
std::size_t abbr_index = abbreviations_.size();
|
||||
for (; type_index != transition_types_.size(); ++type_index) {
|
||||
const TransitionType& tt(transition_types_[type_index]);
|
||||
const char* tt_abbr = &abbreviations_[tt.abbr_index];
|
||||
if (tt_abbr == abbr) abbr_index = tt.abbr_index;
|
||||
if (tt.utc_offset == utc_offset && tt.is_dst == is_dst) {
|
||||
if (abbr_index == tt.abbr_index) break; // reuse
|
||||
}
|
||||
}
|
||||
if (type_index > 255 || abbr_index > 255) {
|
||||
// No index space (8 bits) available for a new type or abbreviation.
|
||||
return false;
|
||||
}
|
||||
if (type_index == transition_types_.size()) {
|
||||
TransitionType& tt(*transition_types_.emplace(transition_types_.end()));
|
||||
tt.utc_offset = static_cast<std::int_least32_t>(utc_offset);
|
||||
tt.is_dst = is_dst;
|
||||
if (abbr_index == abbreviations_.size()) {
|
||||
abbreviations_.append(abbr);
|
||||
abbreviations_.append(1, '\0');
|
||||
}
|
||||
tt.abbr_index = static_cast<std::uint_least8_t>(abbr_index);
|
||||
}
|
||||
*index = static_cast<std::uint_least8_t>(type_index);
|
||||
return true;
|
||||
}
|
||||
|
||||
// zic(8) can generate no-op transitions when a zone changes rules at an
|
||||
// instant when there is actually no discontinuity. So we check whether
|
||||
// two transitions have equivalent types (same offset/is_dst/abbr).
|
||||
bool TimeZoneInfo::EquivTransitions(std::uint_fast8_t tt1_index,
|
||||
std::uint_fast8_t tt2_index) const {
|
||||
if (tt1_index == tt2_index) return true;
|
||||
const TransitionType& tt1(transition_types_[tt1_index]);
|
||||
const TransitionType& tt2(transition_types_[tt2_index]);
|
||||
if (tt1.utc_offset != tt2.utc_offset) return false;
|
||||
if (tt1.is_dst != tt2.is_dst) return false;
|
||||
if (tt1.abbr_index != tt2.abbr_index) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use the POSIX-TZ-environment-variable-style string to handle times
|
||||
// in years after the last transition stored in the zoneinfo data.
|
||||
bool TimeZoneInfo::ExtendTransitions() {
|
||||
extended_ = false;
|
||||
if (future_spec_.empty()) return true; // last transition prevails
|
||||
|
||||
PosixTimeZone posix;
|
||||
if (!ParsePosixSpec(future_spec_, &posix)) return false;
|
||||
|
||||
// Find transition type for the future std specification.
|
||||
std::uint_least8_t std_ti;
|
||||
if (!GetTransitionType(posix.std_offset, false, posix.std_abbr, &std_ti))
|
||||
return false;
|
||||
|
||||
if (posix.dst_abbr.empty()) { // std only
|
||||
// The future specification should match the last transition, and
|
||||
// that means that handling the future will fall out naturally.
|
||||
return EquivTransitions(transitions_.back().type_index, std_ti);
|
||||
}
|
||||
|
||||
// Find transition type for the future dst specification.
|
||||
std::uint_least8_t dst_ti;
|
||||
if (!GetTransitionType(posix.dst_offset, true, posix.dst_abbr, &dst_ti))
|
||||
return false;
|
||||
|
||||
if (AllYearDST(posix)) { // dst only
|
||||
// The future specification should match the last transition, and
|
||||
// that means that handling the future will fall out naturally.
|
||||
return EquivTransitions(transitions_.back().type_index, dst_ti);
|
||||
}
|
||||
|
||||
// Extend the transitions for an additional 401 years using the future
|
||||
// specification. Years beyond those can be handled by mapping back to
|
||||
// a cycle-equivalent year within that range. Note that we need 401
|
||||
// (well, at least the first transition in the 401st year) so that the
|
||||
// end of the 400th year is mapped back to an extended year. And first
|
||||
// we may also need two additional transitions for the current year.
|
||||
transitions_.reserve(transitions_.size() + 2 + 401 * 2);
|
||||
extended_ = true;
|
||||
|
||||
const Transition& last(transitions_.back());
|
||||
const std::int_fast64_t last_time = last.unix_time;
|
||||
const TransitionType& last_tt(transition_types_[last.type_index]);
|
||||
last_year_ = LocalTime(last_time, last_tt).cs.year();
|
||||
bool leap_year = IsLeap(last_year_);
|
||||
const civil_second jan1(last_year_);
|
||||
std::int_fast64_t jan1_time = jan1 - civil_second();
|
||||
int jan1_weekday = ToPosixWeekday(get_weekday(jan1));
|
||||
|
||||
Transition dst = {0, dst_ti, civil_second(), civil_second()};
|
||||
Transition std = {0, std_ti, civil_second(), civil_second()};
|
||||
for (const year_t limit = last_year_ + 401;; ++last_year_) {
|
||||
auto dst_trans_off = TransOffset(leap_year, jan1_weekday, posix.dst_start);
|
||||
auto std_trans_off = TransOffset(leap_year, jan1_weekday, posix.dst_end);
|
||||
dst.unix_time = jan1_time + dst_trans_off - posix.std_offset;
|
||||
std.unix_time = jan1_time + std_trans_off - posix.dst_offset;
|
||||
const auto* ta = dst.unix_time < std.unix_time ? &dst : &std;
|
||||
const auto* tb = dst.unix_time < std.unix_time ? &std : &dst;
|
||||
if (last_time < tb->unix_time) {
|
||||
if (last_time < ta->unix_time) transitions_.push_back(*ta);
|
||||
transitions_.push_back(*tb);
|
||||
}
|
||||
if (last_year_ == limit) break;
|
||||
jan1_time += kSecsPerYear[leap_year];
|
||||
jan1_weekday = (jan1_weekday + kDaysPerYear[leap_year]) % 7;
|
||||
leap_year = !leap_year && IsLeap(last_year_ + 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
using FilePtr = std::unique_ptr<FILE, int (*)(FILE*)>;
|
||||
|
||||
// fopen(3) adaptor.
|
||||
inline FilePtr FOpen(const char* path, const char* mode) {
|
||||
#if defined(_MSC_VER)
|
||||
FILE* fp;
|
||||
if (fopen_s(&fp, path, mode) != 0) fp = nullptr;
|
||||
return FilePtr(fp, fclose);
|
||||
#else
|
||||
// TODO: Enable the close-on-exec flag.
|
||||
return FilePtr(fopen(path, mode), fclose);
|
||||
#endif
|
||||
}
|
||||
|
||||
// A stdio(3)-backed implementation of ZoneInfoSource.
|
||||
class FileZoneInfoSource : public ZoneInfoSource {
|
||||
public:
|
||||
static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
|
||||
|
||||
std::size_t Read(void* ptr, std::size_t size) override {
|
||||
size = std::min(size, len_);
|
||||
std::size_t nread = fread(ptr, 1, size, fp_.get());
|
||||
len_ -= nread;
|
||||
return nread;
|
||||
}
|
||||
int Skip(std::size_t offset) override {
|
||||
offset = std::min(offset, len_);
|
||||
int rc = fseek(fp_.get(), static_cast<long>(offset), SEEK_CUR);
|
||||
if (rc == 0) len_ -= offset;
|
||||
return rc;
|
||||
}
|
||||
std::string Version() const override {
|
||||
// TODO: It would nice if the zoneinfo data included the tzdb version.
|
||||
return std::string();
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit FileZoneInfoSource(
|
||||
FilePtr fp, std::size_t len = std::numeric_limits<std::size_t>::max())
|
||||
: fp_(std::move(fp)), len_(len) {}
|
||||
|
||||
private:
|
||||
FilePtr fp_;
|
||||
std::size_t len_;
|
||||
};
|
||||
|
||||
std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open(
|
||||
const std::string& name) {
|
||||
// Use of the "file:" prefix is intended for testing purposes only.
|
||||
const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
|
||||
|
||||
// Map the time-zone name to a path name.
|
||||
std::string path;
|
||||
if (pos == name.size() || name[pos] != '/') {
|
||||
const char* tzdir = "/usr/share/zoneinfo";
|
||||
char* tzdir_env = nullptr;
|
||||
#if defined(_MSC_VER)
|
||||
_dupenv_s(&tzdir_env, nullptr, "TZDIR");
|
||||
#else
|
||||
tzdir_env = std::getenv("TZDIR");
|
||||
#endif
|
||||
if (tzdir_env && *tzdir_env) tzdir = tzdir_env;
|
||||
path += tzdir;
|
||||
path += '/';
|
||||
#if defined(_MSC_VER)
|
||||
free(tzdir_env);
|
||||
#endif
|
||||
}
|
||||
path.append(name, pos, std::string::npos);
|
||||
|
||||
// Open the zoneinfo file.
|
||||
auto fp = FOpen(path.c_str(), "rb");
|
||||
if (fp == nullptr) return nullptr;
|
||||
return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(std::move(fp)));
|
||||
}
|
||||
|
||||
class AndroidZoneInfoSource : public FileZoneInfoSource {
|
||||
public:
|
||||
static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
|
||||
std::string Version() const override { return version_; }
|
||||
|
||||
private:
|
||||
explicit AndroidZoneInfoSource(FilePtr fp, std::size_t len,
|
||||
std::string version)
|
||||
: FileZoneInfoSource(std::move(fp), len), version_(std::move(version)) {}
|
||||
std::string version_;
|
||||
};
|
||||
|
||||
std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
|
||||
const std::string& name) {
|
||||
// Use of the "file:" prefix is intended for testing purposes only.
|
||||
const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
|
||||
|
||||
// See Android's libc/tzcode/bionic.cpp for additional information.
|
||||
for (const char* tzdata : {"/apex/com.android.tzdata/etc/tz/tzdata",
|
||||
"/data/misc/zoneinfo/current/tzdata",
|
||||
"/system/usr/share/zoneinfo/tzdata"}) {
|
||||
auto fp = FOpen(tzdata, "rb");
|
||||
if (fp == nullptr) continue;
|
||||
|
||||
char hbuf[24]; // covers header.zonetab_offset too
|
||||
if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue;
|
||||
if (strncmp(hbuf, "tzdata", 6) != 0) continue;
|
||||
const char* vers = (hbuf[11] == '\0') ? hbuf + 6 : "";
|
||||
const std::int_fast32_t index_offset = Decode32(hbuf + 12);
|
||||
const std::int_fast32_t data_offset = Decode32(hbuf + 16);
|
||||
if (index_offset < 0 || data_offset < index_offset) continue;
|
||||
if (fseek(fp.get(), static_cast<long>(index_offset), SEEK_SET) != 0)
|
||||
continue;
|
||||
|
||||
char ebuf[52]; // covers entry.unused too
|
||||
const std::size_t index_size =
|
||||
static_cast<std::size_t>(data_offset - index_offset);
|
||||
const std::size_t zonecnt = index_size / sizeof(ebuf);
|
||||
if (zonecnt * sizeof(ebuf) != index_size) continue;
|
||||
for (std::size_t i = 0; i != zonecnt; ++i) {
|
||||
if (fread(ebuf, 1, sizeof(ebuf), fp.get()) != sizeof(ebuf)) break;
|
||||
const std::int_fast32_t start = data_offset + Decode32(ebuf + 40);
|
||||
const std::int_fast32_t length = Decode32(ebuf + 44);
|
||||
if (start < 0 || length < 0) break;
|
||||
ebuf[40] = '\0'; // ensure zone name is NUL terminated
|
||||
if (strcmp(name.c_str() + pos, ebuf) == 0) {
|
||||
if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break;
|
||||
return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource(
|
||||
std::move(fp), static_cast<std::size_t>(length), vers));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// A zoneinfo source for use inside Fuchsia components. This attempts to
|
||||
// read zoneinfo files from one of several known paths in a component's
|
||||
// incoming namespace. [Config data][1] is preferred, but package-specific
|
||||
// resources are also supported.
|
||||
//
|
||||
// Fuchsia's implementation supports `FileZoneInfoSource::Version()`.
|
||||
//
|
||||
// [1]:
|
||||
// https://fuchsia.dev/fuchsia-src/development/components/data#using_config_data_in_your_component
|
||||
class FuchsiaZoneInfoSource : public FileZoneInfoSource {
|
||||
public:
|
||||
static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
|
||||
std::string Version() const override { return version_; }
|
||||
|
||||
private:
|
||||
explicit FuchsiaZoneInfoSource(FilePtr fp, std::string version)
|
||||
: FileZoneInfoSource(std::move(fp)), version_(std::move(version)) {}
|
||||
std::string version_;
|
||||
};
|
||||
|
||||
std::unique_ptr<ZoneInfoSource> FuchsiaZoneInfoSource::Open(
|
||||
const std::string& name) {
|
||||
// Use of the "file:" prefix is intended for testing purposes only.
|
||||
const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
|
||||
|
||||
// Prefixes where a Fuchsia component might find zoneinfo files,
|
||||
// in descending order of preference.
|
||||
const auto kTzdataPrefixes = {
|
||||
// The tzdata from `config-data`.
|
||||
"/config/data/tzdata/",
|
||||
// The tzdata bundled in the component's package.
|
||||
"/pkg/data/tzdata/",
|
||||
// General data storage.
|
||||
"/data/tzdata/",
|
||||
// The recommended path for routed-in tzdata files.
|
||||
// See for details:
|
||||
// https://fuchsia.dev/fuchsia-src/concepts/process/namespaces?hl=en#typical_directory_structure
|
||||
"/config/tzdata/",
|
||||
};
|
||||
const auto kEmptyPrefix = {""};
|
||||
const bool name_absolute = (pos != name.size() && name[pos] == '/');
|
||||
const auto prefixes = name_absolute ? kEmptyPrefix : kTzdataPrefixes;
|
||||
|
||||
// Fuchsia builds place zoneinfo files at "<prefix><format><name>".
|
||||
for (const std::string prefix : prefixes) {
|
||||
std::string path = prefix;
|
||||
if (!prefix.empty()) path += "zoneinfo/tzif2/"; // format
|
||||
path.append(name, pos, std::string::npos);
|
||||
|
||||
auto fp = FOpen(path.c_str(), "rb");
|
||||
if (fp == nullptr) continue;
|
||||
|
||||
std::string version;
|
||||
if (!prefix.empty()) {
|
||||
// Fuchsia builds place the version in "<prefix>revision.txt".
|
||||
std::ifstream version_stream(prefix + "revision.txt");
|
||||
if (version_stream.is_open()) {
|
||||
// revision.txt should contain no newlines, but to be
|
||||
// defensive we read just the first line.
|
||||
std::getline(version_stream, version);
|
||||
}
|
||||
}
|
||||
|
||||
return std::unique_ptr<ZoneInfoSource>(
|
||||
new FuchsiaZoneInfoSource(std::move(fp), std::move(version)));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// What (no leap-seconds) UTC+seconds zoneinfo would look like.
|
||||
bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
|
||||
transition_types_.resize(1);
|
||||
TransitionType& tt(transition_types_.back());
|
||||
tt.utc_offset = static_cast<std::int_least32_t>(offset.count());
|
||||
tt.is_dst = false;
|
||||
tt.abbr_index = 0;
|
||||
|
||||
// We temporarily add some redundant, contemporary (2015 through 2025)
|
||||
// transitions for performance reasons. See TimeZoneInfo::LocalTime().
|
||||
// TODO: Fix the performance issue and remove the extra transitions.
|
||||
transitions_.clear();
|
||||
transitions_.reserve(12);
|
||||
for (const std::int_fast64_t unix_time : {
|
||||
-(1LL << 59), // a "first half" transition
|
||||
1420070400LL, // 2015-01-01T00:00:00+00:00
|
||||
1451606400LL, // 2016-01-01T00:00:00+00:00
|
||||
1483228800LL, // 2017-01-01T00:00:00+00:00
|
||||
1514764800LL, // 2018-01-01T00:00:00+00:00
|
||||
1546300800LL, // 2019-01-01T00:00:00+00:00
|
||||
1577836800LL, // 2020-01-01T00:00:00+00:00
|
||||
1609459200LL, // 2021-01-01T00:00:00+00:00
|
||||
1640995200LL, // 2022-01-01T00:00:00+00:00
|
||||
1672531200LL, // 2023-01-01T00:00:00+00:00
|
||||
1704067200LL, // 2024-01-01T00:00:00+00:00
|
||||
1735689600LL, // 2025-01-01T00:00:00+00:00
|
||||
}) {
|
||||
Transition& tr(*transitions_.emplace(transitions_.end()));
|
||||
tr.unix_time = unix_time;
|
||||
tr.type_index = 0;
|
||||
tr.civil_sec = LocalTime(tr.unix_time, tt).cs;
|
||||
tr.prev_civil_sec = tr.civil_sec - 1;
|
||||
}
|
||||
|
||||
default_transition_type_ = 0;
|
||||
abbreviations_ = FixedOffsetToAbbr(offset);
|
||||
abbreviations_.append(1, '\0');
|
||||
future_spec_.clear(); // never needed for a fixed-offset zone
|
||||
extended_ = false;
|
||||
|
||||
tt.civil_max = LocalTime(seconds::max().count(), tt).cs;
|
||||
tt.civil_min = LocalTime(seconds::min().count(), tt).cs;
|
||||
|
||||
transitions_.shrink_to_fit();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimeZoneInfo::Load(ZoneInfoSource* zip) {
|
||||
// Read and validate the header.
|
||||
tzhead tzh;
|
||||
if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false;
|
||||
if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0)
|
||||
return false;
|
||||
Header hdr;
|
||||
if (!hdr.Build(tzh)) return false;
|
||||
std::size_t time_len = 4;
|
||||
if (tzh.tzh_version[0] != '\0') {
|
||||
// Skip the 4-byte data.
|
||||
if (zip->Skip(hdr.DataLength(time_len)) != 0) return false;
|
||||
// Read and validate the header for the 8-byte data.
|
||||
if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false;
|
||||
if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0)
|
||||
return false;
|
||||
if (tzh.tzh_version[0] == '\0') return false;
|
||||
if (!hdr.Build(tzh)) return false;
|
||||
time_len = 8;
|
||||
}
|
||||
if (hdr.typecnt == 0) return false;
|
||||
if (hdr.leapcnt != 0) {
|
||||
// This code assumes 60-second minutes so we do not want
|
||||
// the leap-second encoded zoneinfo. We could reverse the
|
||||
// compensation, but the "right" encoding is rarely used
|
||||
// so currently we simply reject such data.
|
||||
return false;
|
||||
}
|
||||
if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt) return false;
|
||||
if (hdr.ttisutcnt != 0 && hdr.ttisutcnt != hdr.typecnt) return false;
|
||||
|
||||
// Read the data into a local buffer.
|
||||
std::size_t len = hdr.DataLength(time_len);
|
||||
std::vector<char> tbuf(len);
|
||||
if (zip->Read(tbuf.data(), len) != len) return false;
|
||||
const char* bp = tbuf.data();
|
||||
|
||||
// Decode and validate the transitions.
|
||||
transitions_.reserve(hdr.timecnt + 2);
|
||||
transitions_.resize(hdr.timecnt);
|
||||
for (std::size_t i = 0; i != hdr.timecnt; ++i) {
|
||||
transitions_[i].unix_time = (time_len == 4) ? Decode32(bp) : Decode64(bp);
|
||||
bp += time_len;
|
||||
if (i != 0) {
|
||||
// Check that the transitions are ordered by time (as zic guarantees).
|
||||
if (!Transition::ByUnixTime()(transitions_[i - 1], transitions_[i]))
|
||||
return false; // out of order
|
||||
}
|
||||
}
|
||||
bool seen_type_0 = false;
|
||||
for (std::size_t i = 0; i != hdr.timecnt; ++i) {
|
||||
transitions_[i].type_index = Decode8(bp++);
|
||||
if (transitions_[i].type_index >= hdr.typecnt) return false;
|
||||
if (transitions_[i].type_index == 0) seen_type_0 = true;
|
||||
}
|
||||
|
||||
// Decode and validate the transition types.
|
||||
transition_types_.reserve(hdr.typecnt + 2);
|
||||
transition_types_.resize(hdr.typecnt);
|
||||
for (std::size_t i = 0; i != hdr.typecnt; ++i) {
|
||||
transition_types_[i].utc_offset =
|
||||
static_cast<std::int_least32_t>(Decode32(bp));
|
||||
if (transition_types_[i].utc_offset >= kSecsPerDay ||
|
||||
transition_types_[i].utc_offset <= -kSecsPerDay)
|
||||
return false;
|
||||
bp += 4;
|
||||
transition_types_[i].is_dst = (Decode8(bp++) != 0);
|
||||
transition_types_[i].abbr_index = Decode8(bp++);
|
||||
if (transition_types_[i].abbr_index >= hdr.charcnt) return false;
|
||||
}
|
||||
|
||||
// Determine the before-first-transition type.
|
||||
default_transition_type_ = 0;
|
||||
if (seen_type_0 && hdr.timecnt != 0) {
|
||||
std::uint_fast8_t index = 0;
|
||||
if (transition_types_[0].is_dst) {
|
||||
index = transitions_[0].type_index;
|
||||
while (index != 0 && transition_types_[index].is_dst) --index;
|
||||
}
|
||||
while (index != hdr.typecnt && transition_types_[index].is_dst) ++index;
|
||||
if (index != hdr.typecnt) default_transition_type_ = index;
|
||||
}
|
||||
|
||||
// Copy all the abbreviations.
|
||||
abbreviations_.reserve(hdr.charcnt + 10);
|
||||
abbreviations_.assign(bp, hdr.charcnt);
|
||||
bp += hdr.charcnt;
|
||||
|
||||
// Skip the unused portions. We've already dispensed with leap-second
|
||||
// encoded zoneinfo. The ttisstd/ttisgmt indicators only apply when
|
||||
// interpreting a POSIX spec that does not include start/end rules, and
|
||||
// that isn't the case here (see "zic -p").
|
||||
bp += (time_len + 4) * hdr.leapcnt; // leap-time + TAI-UTC
|
||||
bp += 1 * hdr.ttisstdcnt; // UTC/local indicators
|
||||
bp += 1 * hdr.ttisutcnt; // standard/wall indicators
|
||||
assert(bp == tbuf.data() + tbuf.size());
|
||||
|
||||
future_spec_.clear();
|
||||
if (tzh.tzh_version[0] != '\0') {
|
||||
// Snarf up the NL-enclosed future POSIX spec. Note
|
||||
// that version '3' files utilize an extended format.
|
||||
auto get_char = [](ZoneInfoSource* azip) -> int {
|
||||
unsigned char ch; // all non-EOF results are positive
|
||||
return (azip->Read(&ch, 1) == 1) ? ch : EOF;
|
||||
};
|
||||
if (get_char(zip) != '\n') return false;
|
||||
for (int c = get_char(zip); c != '\n'; c = get_char(zip)) {
|
||||
if (c == EOF) return false;
|
||||
future_spec_.push_back(static_cast<char>(c));
|
||||
}
|
||||
}
|
||||
|
||||
// We don't check for EOF so that we're forwards compatible.
|
||||
|
||||
// If we did not find version information during the standard loading
|
||||
// process (as of tzh_version '3' that is unsupported), then ask the
|
||||
// ZoneInfoSource for any out-of-bound version string it may be privy to.
|
||||
if (version_.empty()) {
|
||||
version_ = zip->Version();
|
||||
}
|
||||
|
||||
// Ensure that there is always a transition in the first half of the
|
||||
// time line (the second half is handled below) so that the signed
|
||||
// difference between a civil_second and the civil_second of its
|
||||
// previous transition is always representable, without overflow.
|
||||
if (transitions_.empty() || transitions_.front().unix_time >= 0) {
|
||||
Transition& tr(*transitions_.emplace(transitions_.begin()));
|
||||
tr.unix_time = -(1LL << 59); // -18267312070-10-26T17:01:52+00:00
|
||||
tr.type_index = default_transition_type_;
|
||||
}
|
||||
|
||||
// Extend the transitions using the future specification.
|
||||
if (!ExtendTransitions()) return false;
|
||||
|
||||
// Ensure that there is always a transition in the second half of the
|
||||
// time line (the first half is handled above) so that the signed
|
||||
// difference between a civil_second and the civil_second of its
|
||||
// previous transition is always representable, without overflow.
|
||||
const Transition& last(transitions_.back());
|
||||
if (last.unix_time < 0) {
|
||||
const std::uint_fast8_t type_index = last.type_index;
|
||||
Transition& tr(*transitions_.emplace(transitions_.end()));
|
||||
tr.unix_time = 2147483647; // 2038-01-19T03:14:07+00:00
|
||||
tr.type_index = type_index;
|
||||
}
|
||||
|
||||
// Compute the local civil time for each transition and the preceding
|
||||
// second. These will be used for reverse conversions in MakeTime().
|
||||
const TransitionType* ttp = &transition_types_[default_transition_type_];
|
||||
for (std::size_t i = 0; i != transitions_.size(); ++i) {
|
||||
Transition& tr(transitions_[i]);
|
||||
tr.prev_civil_sec = LocalTime(tr.unix_time, *ttp).cs - 1;
|
||||
ttp = &transition_types_[tr.type_index];
|
||||
tr.civil_sec = LocalTime(tr.unix_time, *ttp).cs;
|
||||
if (i != 0) {
|
||||
// Check that the transitions are ordered by civil time. Essentially
|
||||
// this means that an offset change cannot cross another such change.
|
||||
// No one does this in practice, and we depend on it in MakeTime().
|
||||
if (!Transition::ByCivilTime()(transitions_[i - 1], tr))
|
||||
return false; // out of order
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the maximum/minimum civil times that can be converted to a
|
||||
// time_point<seconds> for each of the zone's transition types.
|
||||
for (auto& tt : transition_types_) {
|
||||
tt.civil_max = LocalTime(seconds::max().count(), tt).cs;
|
||||
tt.civil_min = LocalTime(seconds::min().count(), tt).cs;
|
||||
}
|
||||
|
||||
transitions_.shrink_to_fit();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimeZoneInfo::Load(const std::string& name) {
|
||||
// We can ensure that the loading of UTC or any other fixed-offset
|
||||
// zone never fails because the simple, fixed-offset state can be
|
||||
// internally generated. Note that this depends on our choice to not
|
||||
// accept leap-second encoded ("right") zoneinfo.
|
||||
auto offset = seconds::zero();
|
||||
if (FixedOffsetFromName(name, &offset)) {
|
||||
return ResetToBuiltinUTC(offset);
|
||||
}
|
||||
|
||||
// Find and use a ZoneInfoSource to load the named zone.
|
||||
auto zip = cctz_extension::zone_info_source_factory(
|
||||
name, [](const std::string& n) -> std::unique_ptr<ZoneInfoSource> {
|
||||
if (auto z = FileZoneInfoSource::Open(n)) return z;
|
||||
if (auto z = AndroidZoneInfoSource::Open(n)) return z;
|
||||
if (auto z = FuchsiaZoneInfoSource::Open(n)) return z;
|
||||
return nullptr;
|
||||
});
|
||||
return zip != nullptr && Load(zip.get());
|
||||
}
|
||||
|
||||
std::unique_ptr<TimeZoneInfo> TimeZoneInfo::UTC() {
|
||||
auto tz = std::unique_ptr<TimeZoneInfo>(new TimeZoneInfo);
|
||||
tz->ResetToBuiltinUTC(seconds::zero());
|
||||
return tz;
|
||||
}
|
||||
|
||||
std::unique_ptr<TimeZoneInfo> TimeZoneInfo::Make(const std::string& name) {
|
||||
auto tz = std::unique_ptr<TimeZoneInfo>(new TimeZoneInfo);
|
||||
if (!tz->Load(name)) tz.reset(); // fallback to UTC
|
||||
return tz;
|
||||
}
|
||||
|
||||
// BreakTime() translation for a particular transition type.
|
||||
time_zone::absolute_lookup TimeZoneInfo::LocalTime(
|
||||
std::int_fast64_t unix_time, const TransitionType& tt) const {
|
||||
// A civil time in "+offset" looks like (time+offset) in UTC.
|
||||
// Note: We perform two additions in the civil_second domain to
|
||||
// sidestep the chance of overflow in (unix_time + tt.utc_offset).
|
||||
return {(civil_second() + unix_time) + tt.utc_offset, tt.utc_offset,
|
||||
tt.is_dst, &abbreviations_[tt.abbr_index]};
|
||||
}
|
||||
|
||||
// BreakTime() translation for a particular transition.
|
||||
time_zone::absolute_lookup TimeZoneInfo::LocalTime(std::int_fast64_t unix_time,
|
||||
const Transition& tr) const {
|
||||
const TransitionType& tt = transition_types_[tr.type_index];
|
||||
// Note: (unix_time - tr.unix_time) will never overflow as we
|
||||
// have ensured that there is always a "nearby" transition.
|
||||
return {tr.civil_sec + (unix_time - tr.unix_time), // TODO: Optimize.
|
||||
tt.utc_offset, tt.is_dst, &abbreviations_[tt.abbr_index]};
|
||||
}
|
||||
|
||||
// MakeTime() translation with a conversion-preserving +N * 400-year shift.
|
||||
time_zone::civil_lookup TimeZoneInfo::TimeLocal(const civil_second& cs,
|
||||
year_t c4_shift) const {
|
||||
assert(last_year_ - 400 < cs.year() && cs.year() <= last_year_);
|
||||
time_zone::civil_lookup cl = MakeTime(cs);
|
||||
if (c4_shift > seconds::max().count() / kSecsPer400Years) {
|
||||
cl.pre = cl.trans = cl.post = time_point<seconds>::max();
|
||||
} else {
|
||||
const auto offset = seconds(c4_shift * kSecsPer400Years);
|
||||
const auto limit = time_point<seconds>::max() - offset;
|
||||
for (auto* tp : {&cl.pre, &cl.trans, &cl.post}) {
|
||||
if (*tp > limit) {
|
||||
*tp = time_point<seconds>::max();
|
||||
} else {
|
||||
*tp += offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cl;
|
||||
}
|
||||
|
||||
time_zone::absolute_lookup TimeZoneInfo::BreakTime(
|
||||
const time_point<seconds>& tp) const {
|
||||
std::int_fast64_t unix_time = ToUnixSeconds(tp);
|
||||
const std::size_t timecnt = transitions_.size();
|
||||
assert(timecnt != 0); // We always add a transition.
|
||||
|
||||
if (unix_time < transitions_[0].unix_time) {
|
||||
return LocalTime(unix_time, transition_types_[default_transition_type_]);
|
||||
}
|
||||
if (unix_time >= transitions_[timecnt - 1].unix_time) {
|
||||
// After the last transition. If we extended the transitions using
|
||||
// future_spec_, shift back to a supported year using the 400-year
|
||||
// cycle of calendaric equivalence and then compensate accordingly.
|
||||
if (extended_) {
|
||||
const std::int_fast64_t diff =
|
||||
unix_time - transitions_[timecnt - 1].unix_time;
|
||||
const year_t shift = diff / kSecsPer400Years + 1;
|
||||
const auto d = seconds(shift * kSecsPer400Years);
|
||||
time_zone::absolute_lookup al = BreakTime(tp - d);
|
||||
al.cs = YearShift(al.cs, shift * 400);
|
||||
return al;
|
||||
}
|
||||
return LocalTime(unix_time, transitions_[timecnt - 1]);
|
||||
}
|
||||
|
||||
const std::size_t hint = local_time_hint_.load(std::memory_order_relaxed);
|
||||
if (0 < hint && hint < timecnt) {
|
||||
if (transitions_[hint - 1].unix_time <= unix_time) {
|
||||
if (unix_time < transitions_[hint].unix_time) {
|
||||
return LocalTime(unix_time, transitions_[hint - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Transition target = {unix_time, 0, civil_second(), civil_second()};
|
||||
const Transition* begin = &transitions_[0];
|
||||
const Transition* tr = std::upper_bound(begin, begin + timecnt, target,
|
||||
Transition::ByUnixTime());
|
||||
local_time_hint_.store(static_cast<std::size_t>(tr - begin),
|
||||
std::memory_order_relaxed);
|
||||
return LocalTime(unix_time, *--tr);
|
||||
}
|
||||
|
||||
time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const {
|
||||
const std::size_t timecnt = transitions_.size();
|
||||
assert(timecnt != 0); // We always add a transition.
|
||||
|
||||
// Find the first transition after our target civil time.
|
||||
const Transition* tr = nullptr;
|
||||
const Transition* begin = &transitions_[0];
|
||||
const Transition* end = begin + timecnt;
|
||||
if (cs < begin->civil_sec) {
|
||||
tr = begin;
|
||||
} else if (cs >= transitions_[timecnt - 1].civil_sec) {
|
||||
tr = end;
|
||||
} else {
|
||||
const std::size_t hint = time_local_hint_.load(std::memory_order_relaxed);
|
||||
if (0 < hint && hint < timecnt) {
|
||||
if (transitions_[hint - 1].civil_sec <= cs) {
|
||||
if (cs < transitions_[hint].civil_sec) {
|
||||
tr = begin + hint;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tr == nullptr) {
|
||||
const Transition target = {0, 0, cs, civil_second()};
|
||||
tr = std::upper_bound(begin, end, target, Transition::ByCivilTime());
|
||||
time_local_hint_.store(static_cast<std::size_t>(tr - begin),
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
if (tr == begin) {
|
||||
if (tr->prev_civil_sec >= cs) {
|
||||
// Before first transition, so use the default offset.
|
||||
const TransitionType& tt(transition_types_[default_transition_type_]);
|
||||
if (cs < tt.civil_min) return MakeUnique(time_point<seconds>::min());
|
||||
return MakeUnique(cs - (civil_second() + tt.utc_offset));
|
||||
}
|
||||
// tr->prev_civil_sec < cs < tr->civil_sec
|
||||
return MakeSkipped(*tr, cs);
|
||||
}
|
||||
|
||||
if (tr == end) {
|
||||
if (cs > (--tr)->prev_civil_sec) {
|
||||
// After the last transition. If we extended the transitions using
|
||||
// future_spec_, shift back to a supported year using the 400-year
|
||||
// cycle of calendaric equivalence and then compensate accordingly.
|
||||
if (extended_ && cs.year() > last_year_) {
|
||||
const year_t shift = (cs.year() - last_year_ - 1) / 400 + 1;
|
||||
return TimeLocal(YearShift(cs, shift * -400), shift);
|
||||
}
|
||||
const TransitionType& tt(transition_types_[tr->type_index]);
|
||||
if (cs > tt.civil_max) return MakeUnique(time_point<seconds>::max());
|
||||
return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
|
||||
}
|
||||
// tr->civil_sec <= cs <= tr->prev_civil_sec
|
||||
return MakeRepeated(*tr, cs);
|
||||
}
|
||||
|
||||
if (tr->prev_civil_sec < cs) {
|
||||
// tr->prev_civil_sec < cs < tr->civil_sec
|
||||
return MakeSkipped(*tr, cs);
|
||||
}
|
||||
|
||||
if (cs <= (--tr)->prev_civil_sec) {
|
||||
// tr->civil_sec <= cs <= tr->prev_civil_sec
|
||||
return MakeRepeated(*tr, cs);
|
||||
}
|
||||
|
||||
// In between transitions.
|
||||
return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
|
||||
}
|
||||
|
||||
std::string TimeZoneInfo::Version() const { return version_; }
|
||||
|
||||
std::string TimeZoneInfo::Description() const {
|
||||
std::ostringstream oss;
|
||||
oss << "#trans=" << transitions_.size();
|
||||
oss << " #types=" << transition_types_.size();
|
||||
oss << " spec='" << future_spec_ << "'";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
bool TimeZoneInfo::NextTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const {
|
||||
if (transitions_.empty()) return false;
|
||||
const Transition* begin = &transitions_[0];
|
||||
const Transition* end = begin + transitions_.size();
|
||||
if (begin->unix_time <= -(1LL << 59)) {
|
||||
// Do not report the BIG_BANG found in some zoneinfo data as it is
|
||||
// really a sentinel, not a transition. See pre-2018f tz/zic.c.
|
||||
++begin;
|
||||
}
|
||||
std::int_fast64_t unix_time = ToUnixSeconds(tp);
|
||||
const Transition target = {unix_time, 0, civil_second(), civil_second()};
|
||||
const Transition* tr =
|
||||
std::upper_bound(begin, end, target, Transition::ByUnixTime());
|
||||
for (; tr != end; ++tr) { // skip no-op transitions
|
||||
std::uint_fast8_t prev_type_index =
|
||||
(tr == begin) ? default_transition_type_ : tr[-1].type_index;
|
||||
if (!EquivTransitions(prev_type_index, tr[0].type_index)) break;
|
||||
}
|
||||
// When tr == end we return false, ignoring future_spec_.
|
||||
if (tr == end) return false;
|
||||
trans->from = tr->prev_civil_sec + 1;
|
||||
trans->to = tr->civil_sec;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const {
|
||||
if (transitions_.empty()) return false;
|
||||
const Transition* begin = &transitions_[0];
|
||||
const Transition* end = begin + transitions_.size();
|
||||
if (begin->unix_time <= -(1LL << 59)) {
|
||||
// Do not report the BIG_BANG found in some zoneinfo data as it is
|
||||
// really a sentinel, not a transition. See pre-2018f tz/zic.c.
|
||||
++begin;
|
||||
}
|
||||
std::int_fast64_t unix_time = ToUnixSeconds(tp);
|
||||
if (FromUnixSeconds(unix_time) != tp) {
|
||||
if (unix_time == std::numeric_limits<std::int_fast64_t>::max()) {
|
||||
if (end == begin) return false; // Ignore future_spec_.
|
||||
trans->from = (--end)->prev_civil_sec + 1;
|
||||
trans->to = end->civil_sec;
|
||||
return true;
|
||||
}
|
||||
unix_time += 1; // ceils
|
||||
}
|
||||
const Transition target = {unix_time, 0, civil_second(), civil_second()};
|
||||
const Transition* tr =
|
||||
std::lower_bound(begin, end, target, Transition::ByUnixTime());
|
||||
for (; tr != begin; --tr) { // skip no-op transitions
|
||||
std::uint_fast8_t prev_type_index =
|
||||
(tr - 1 == begin) ? default_transition_type_ : tr[-2].type_index;
|
||||
if (!EquivTransitions(prev_type_index, tr[-1].type_index)) break;
|
||||
}
|
||||
// When tr == end we return the "last" transition, ignoring future_spec_.
|
||||
if (tr == begin) return false;
|
||||
trans->from = (--tr)->prev_civil_sec + 1;
|
||||
trans->to = tr->civil_sec;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
128
Pods/abseil/absl/time/internal/cctz/src/time_zone_info.h
generated
Normal file
128
Pods/abseil/absl/time/internal/cctz/src/time_zone_info.h
generated
Normal file
@@ -0,0 +1,128 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/zone_info_source.h"
|
||||
#include "time_zone_if.h"
|
||||
#include "tzfile.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// A transition to a new UTC offset.
|
||||
struct Transition {
|
||||
std::int_least64_t unix_time; // the instant of this transition
|
||||
std::uint_least8_t type_index; // index of the transition type
|
||||
civil_second civil_sec; // local civil time of transition
|
||||
civil_second prev_civil_sec; // local civil time one second earlier
|
||||
|
||||
struct ByUnixTime {
|
||||
inline bool operator()(const Transition& lhs, const Transition& rhs) const {
|
||||
return lhs.unix_time < rhs.unix_time;
|
||||
}
|
||||
};
|
||||
struct ByCivilTime {
|
||||
inline bool operator()(const Transition& lhs, const Transition& rhs) const {
|
||||
return lhs.civil_sec < rhs.civil_sec;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// The characteristics of a particular transition.
|
||||
struct TransitionType {
|
||||
std::int_least32_t utc_offset; // the new prevailing UTC offset
|
||||
civil_second civil_max; // max convertible civil time for offset
|
||||
civil_second civil_min; // min convertible civil time for offset
|
||||
bool is_dst; // did we move into daylight-saving time
|
||||
std::uint_least8_t abbr_index; // index of the new abbreviation
|
||||
};
|
||||
|
||||
// A time zone backed by the IANA Time Zone Database (zoneinfo).
|
||||
class TimeZoneInfo : public TimeZoneIf {
|
||||
public:
|
||||
// Factories.
|
||||
static std::unique_ptr<TimeZoneInfo> UTC(); // never fails
|
||||
static std::unique_ptr<TimeZoneInfo> Make(const std::string& name);
|
||||
|
||||
// TimeZoneIf implementations.
|
||||
time_zone::absolute_lookup BreakTime(
|
||||
const time_point<seconds>& tp) const override;
|
||||
time_zone::civil_lookup MakeTime(const civil_second& cs) const override;
|
||||
bool NextTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const override;
|
||||
bool PrevTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const override;
|
||||
std::string Version() const override;
|
||||
std::string Description() const override;
|
||||
|
||||
private:
|
||||
TimeZoneInfo() = default;
|
||||
TimeZoneInfo(const TimeZoneInfo&) = delete;
|
||||
TimeZoneInfo& operator=(const TimeZoneInfo&) = delete;
|
||||
|
||||
bool GetTransitionType(std::int_fast32_t utc_offset, bool is_dst,
|
||||
const std::string& abbr, std::uint_least8_t* index);
|
||||
bool EquivTransitions(std::uint_fast8_t tt1_index,
|
||||
std::uint_fast8_t tt2_index) const;
|
||||
bool ExtendTransitions();
|
||||
|
||||
bool ResetToBuiltinUTC(const seconds& offset);
|
||||
bool Load(const std::string& name);
|
||||
bool Load(ZoneInfoSource* zip);
|
||||
|
||||
// Helpers for BreakTime() and MakeTime().
|
||||
time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time,
|
||||
const TransitionType& tt) const;
|
||||
time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time,
|
||||
const Transition& tr) const;
|
||||
time_zone::civil_lookup TimeLocal(const civil_second& cs,
|
||||
year_t c4_shift) const;
|
||||
|
||||
std::vector<Transition> transitions_; // ordered by unix_time and civil_sec
|
||||
std::vector<TransitionType> transition_types_; // distinct transition types
|
||||
std::uint_fast8_t default_transition_type_; // for before first transition
|
||||
std::string abbreviations_; // all the NUL-terminated abbreviations
|
||||
|
||||
std::string version_; // the tzdata version if available
|
||||
std::string future_spec_; // for after the last zic transition
|
||||
bool extended_; // future_spec_ was used to generate transitions
|
||||
year_t last_year_; // the final year of the generated transitions
|
||||
|
||||
// We remember the transitions found during the last BreakTime() and
|
||||
// MakeTime() calls. If the next request is for the same transition we
|
||||
// will avoid re-searching.
|
||||
mutable std::atomic<std::size_t> local_time_hint_ = {}; // BreakTime() hint
|
||||
mutable std::atomic<std::size_t> time_local_hint_ = {}; // MakeTime() hint
|
||||
};
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_
|
||||
333
Pods/abseil/absl/time/internal/cctz/src/time_zone_libc.cc
generated
Normal file
333
Pods/abseil/absl/time/internal/cctz/src/time_zone_libc.cc
generated
Normal file
@@ -0,0 +1,333 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
#endif
|
||||
|
||||
#include "time_zone_libc.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
|
||||
#if defined(_AIX)
|
||||
extern "C" {
|
||||
extern long altzone;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
// Uses the globals: '_timezone', '_dstbias' and '_tzname'.
|
||||
auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + _dstbias) {
|
||||
const bool is_dst = tm.tm_isdst > 0;
|
||||
return _timezone + (is_dst ? _dstbias : 0);
|
||||
}
|
||||
auto tm_zone(const std::tm& tm) -> decltype(_tzname[0]) {
|
||||
const bool is_dst = tm.tm_isdst > 0;
|
||||
return _tzname[is_dst];
|
||||
}
|
||||
#elif defined(__sun) || defined(_AIX)
|
||||
// Uses the globals: 'timezone', 'altzone' and 'tzname'.
|
||||
auto tm_gmtoff(const std::tm& tm) -> decltype(timezone) {
|
||||
const bool is_dst = tm.tm_isdst > 0;
|
||||
return is_dst ? altzone : timezone;
|
||||
}
|
||||
auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
|
||||
const bool is_dst = tm.tm_isdst > 0;
|
||||
return tzname[is_dst];
|
||||
}
|
||||
#elif defined(__native_client__) || defined(__myriad2__) || \
|
||||
defined(__EMSCRIPTEN__)
|
||||
// Uses the globals: '_timezone' and 'tzname'.
|
||||
auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + 0) {
|
||||
const bool is_dst = tm.tm_isdst > 0;
|
||||
return _timezone + (is_dst ? 60 * 60 : 0);
|
||||
}
|
||||
auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
|
||||
const bool is_dst = tm.tm_isdst > 0;
|
||||
return tzname[is_dst];
|
||||
}
|
||||
#elif defined(__VXWORKS__)
|
||||
// Uses the globals: 'timezone' and 'tzname'.
|
||||
auto tm_gmtoff(const std::tm& tm) -> decltype(timezone + 0) {
|
||||
const bool is_dst = tm.tm_isdst > 0;
|
||||
return timezone + (is_dst ? 60 * 60 : 0);
|
||||
}
|
||||
auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
|
||||
const bool is_dst = tm.tm_isdst > 0;
|
||||
return tzname[is_dst];
|
||||
}
|
||||
#else
|
||||
// Adapt to different spellings of the struct std::tm extension fields.
|
||||
#if defined(tm_gmtoff)
|
||||
auto tm_gmtoff(const std::tm& tm) -> decltype(tm.tm_gmtoff) {
|
||||
return tm.tm_gmtoff;
|
||||
}
|
||||
#elif defined(__tm_gmtoff)
|
||||
auto tm_gmtoff(const std::tm& tm) -> decltype(tm.__tm_gmtoff) {
|
||||
return tm.__tm_gmtoff;
|
||||
}
|
||||
#else
|
||||
template <typename T>
|
||||
auto tm_gmtoff(const T& tm) -> decltype(tm.tm_gmtoff) {
|
||||
return tm.tm_gmtoff;
|
||||
}
|
||||
template <typename T>
|
||||
auto tm_gmtoff(const T& tm) -> decltype(tm.__tm_gmtoff) {
|
||||
return tm.__tm_gmtoff;
|
||||
}
|
||||
#endif // tm_gmtoff
|
||||
#if defined(tm_zone)
|
||||
auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) { return tm.tm_zone; }
|
||||
#elif defined(__tm_zone)
|
||||
auto tm_zone(const std::tm& tm) -> decltype(tm.__tm_zone) {
|
||||
return tm.__tm_zone;
|
||||
}
|
||||
#else
|
||||
template <typename T>
|
||||
auto tm_zone(const T& tm) -> decltype(tm.tm_zone) {
|
||||
return tm.tm_zone;
|
||||
}
|
||||
template <typename T>
|
||||
auto tm_zone(const T& tm) -> decltype(tm.__tm_zone) {
|
||||
return tm.__tm_zone;
|
||||
}
|
||||
#endif // tm_zone
|
||||
#endif
|
||||
using tm_gmtoff_t = decltype(tm_gmtoff(std::tm{}));
|
||||
|
||||
inline std::tm* gm_time(const std::time_t* timep, std::tm* result) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return gmtime_s(result, timep) ? nullptr : result;
|
||||
#else
|
||||
return gmtime_r(timep, result);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline std::tm* local_time(const std::time_t* timep, std::tm* result) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return localtime_s(result, timep) ? nullptr : result;
|
||||
#else
|
||||
return localtime_r(timep, result);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Converts a civil second and "dst" flag into a time_t and a struct tm.
|
||||
// Returns false if time_t cannot represent the requested civil second.
|
||||
// Caller must have already checked that cs.year() will fit into a tm_year.
|
||||
bool make_time(const civil_second& cs, int is_dst, std::time_t* t,
|
||||
std::tm* tm) {
|
||||
tm->tm_year = static_cast<int>(cs.year() - year_t{1900});
|
||||
tm->tm_mon = cs.month() - 1;
|
||||
tm->tm_mday = cs.day();
|
||||
tm->tm_hour = cs.hour();
|
||||
tm->tm_min = cs.minute();
|
||||
tm->tm_sec = cs.second();
|
||||
tm->tm_isdst = is_dst;
|
||||
*t = std::mktime(tm);
|
||||
if (*t == std::time_t{-1}) {
|
||||
std::tm tm2;
|
||||
const std::tm* tmp = local_time(t, &tm2);
|
||||
if (tmp == nullptr || tmp->tm_year != tm->tm_year ||
|
||||
tmp->tm_mon != tm->tm_mon || tmp->tm_mday != tm->tm_mday ||
|
||||
tmp->tm_hour != tm->tm_hour || tmp->tm_min != tm->tm_min ||
|
||||
tmp->tm_sec != tm->tm_sec) {
|
||||
// A true error (not just one second before the epoch).
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find the least time_t in [lo:hi] where local time matches offset, given:
|
||||
// (1) lo doesn't match, (2) hi does, and (3) there is only one transition.
|
||||
std::time_t find_trans(std::time_t lo, std::time_t hi, tm_gmtoff_t offset) {
|
||||
std::tm tm;
|
||||
while (lo + 1 != hi) {
|
||||
const std::time_t mid = lo + (hi - lo) / 2;
|
||||
std::tm* tmp = local_time(&mid, &tm);
|
||||
if (tmp != nullptr) {
|
||||
if (tm_gmtoff(*tmp) == offset) {
|
||||
hi = mid;
|
||||
} else {
|
||||
lo = mid;
|
||||
}
|
||||
} else {
|
||||
// If std::tm cannot hold some result we resort to a linear search,
|
||||
// ignoring all failed conversions. Slow, but never really happens.
|
||||
while (++lo != hi) {
|
||||
tmp = local_time(&lo, &tm);
|
||||
if (tmp != nullptr) {
|
||||
if (tm_gmtoff(*tmp) == offset) break;
|
||||
}
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
}
|
||||
return hi;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<TimeZoneLibC> TimeZoneLibC::Make(const std::string& name) {
|
||||
return std::unique_ptr<TimeZoneLibC>(new TimeZoneLibC(name));
|
||||
}
|
||||
|
||||
time_zone::absolute_lookup TimeZoneLibC::BreakTime(
|
||||
const time_point<seconds>& tp) const {
|
||||
time_zone::absolute_lookup al;
|
||||
al.offset = 0;
|
||||
al.is_dst = false;
|
||||
al.abbr = "-00";
|
||||
|
||||
const std::int_fast64_t s = ToUnixSeconds(tp);
|
||||
|
||||
// If std::time_t cannot hold the input we saturate the output.
|
||||
if (s < std::numeric_limits<std::time_t>::min()) {
|
||||
al.cs = civil_second::min();
|
||||
return al;
|
||||
}
|
||||
if (s > std::numeric_limits<std::time_t>::max()) {
|
||||
al.cs = civil_second::max();
|
||||
return al;
|
||||
}
|
||||
|
||||
const std::time_t t = static_cast<std::time_t>(s);
|
||||
std::tm tm;
|
||||
std::tm* tmp = local_ ? local_time(&t, &tm) : gm_time(&t, &tm);
|
||||
|
||||
// If std::tm cannot hold the result we saturate the output.
|
||||
if (tmp == nullptr) {
|
||||
al.cs = (s < 0) ? civil_second::min() : civil_second::max();
|
||||
return al;
|
||||
}
|
||||
|
||||
const year_t year = tmp->tm_year + year_t{1900};
|
||||
al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour,
|
||||
tmp->tm_min, tmp->tm_sec);
|
||||
al.offset = static_cast<int>(tm_gmtoff(*tmp));
|
||||
al.abbr = local_ ? tm_zone(*tmp) : "UTC"; // as expected by cctz
|
||||
al.is_dst = tmp->tm_isdst > 0;
|
||||
return al;
|
||||
}
|
||||
|
||||
time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
|
||||
if (!local_) {
|
||||
// If time_point<seconds> cannot hold the result we saturate.
|
||||
static const civil_second min_tp_cs =
|
||||
civil_second() + ToUnixSeconds(time_point<seconds>::min());
|
||||
static const civil_second max_tp_cs =
|
||||
civil_second() + ToUnixSeconds(time_point<seconds>::max());
|
||||
const time_point<seconds> tp = (cs < min_tp_cs) ? time_point<seconds>::min()
|
||||
: (cs > max_tp_cs)
|
||||
? time_point<seconds>::max()
|
||||
: FromUnixSeconds(cs - civil_second());
|
||||
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
||||
}
|
||||
|
||||
// If tm_year cannot hold the requested year we saturate the result.
|
||||
if (cs.year() < 0) {
|
||||
if (cs.year() < std::numeric_limits<int>::min() + year_t{1900}) {
|
||||
const time_point<seconds> tp = time_point<seconds>::min();
|
||||
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
||||
}
|
||||
} else {
|
||||
if (cs.year() - year_t{1900} > std::numeric_limits<int>::max()) {
|
||||
const time_point<seconds> tp = time_point<seconds>::max();
|
||||
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
||||
}
|
||||
}
|
||||
|
||||
// We probe with "is_dst" values of 0 and 1 to try to distinguish unique
|
||||
// civil seconds from skipped or repeated ones. This is not always possible
|
||||
// however, as the "dst" flag does not change over some offset transitions.
|
||||
// We are also subject to the vagaries of mktime() implementations. For
|
||||
// example, some implementations treat "tm_isdst" as a demand (useless),
|
||||
// and some as a disambiguator (useful).
|
||||
std::time_t t0, t1;
|
||||
std::tm tm0, tm1;
|
||||
if (make_time(cs, 0, &t0, &tm0) && make_time(cs, 1, &t1, &tm1)) {
|
||||
if (tm0.tm_isdst == tm1.tm_isdst) {
|
||||
// The civil time was singular (pre == trans == post).
|
||||
const time_point<seconds> tp = FromUnixSeconds(tm0.tm_isdst ? t1 : t0);
|
||||
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
||||
}
|
||||
|
||||
tm_gmtoff_t offset = tm_gmtoff(tm0);
|
||||
if (t0 < t1) { // negative DST
|
||||
std::swap(t0, t1);
|
||||
offset = tm_gmtoff(tm1);
|
||||
}
|
||||
|
||||
const std::time_t tt = find_trans(t1, t0, offset);
|
||||
const time_point<seconds> trans = FromUnixSeconds(tt);
|
||||
|
||||
if (tm0.tm_isdst) {
|
||||
// The civil time did not exist (pre >= trans > post).
|
||||
const time_point<seconds> pre = FromUnixSeconds(t0);
|
||||
const time_point<seconds> post = FromUnixSeconds(t1);
|
||||
return {time_zone::civil_lookup::SKIPPED, pre, trans, post};
|
||||
}
|
||||
|
||||
// The civil time was ambiguous (pre < trans <= post).
|
||||
const time_point<seconds> pre = FromUnixSeconds(t1);
|
||||
const time_point<seconds> post = FromUnixSeconds(t0);
|
||||
return {time_zone::civil_lookup::REPEATED, pre, trans, post};
|
||||
}
|
||||
|
||||
// make_time() failed somehow so we saturate the result.
|
||||
const time_point<seconds> tp = (cs < civil_second())
|
||||
? time_point<seconds>::min()
|
||||
: time_point<seconds>::max();
|
||||
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
||||
}
|
||||
|
||||
bool TimeZoneLibC::NextTransition(const time_point<seconds>&,
|
||||
time_zone::civil_transition*) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TimeZoneLibC::PrevTransition(const time_point<seconds>&,
|
||||
time_zone::civil_transition*) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string TimeZoneLibC::Version() const {
|
||||
return std::string(); // unknown
|
||||
}
|
||||
|
||||
std::string TimeZoneLibC::Description() const {
|
||||
return local_ ? "localtime" : "UTC";
|
||||
}
|
||||
|
||||
TimeZoneLibC::TimeZoneLibC(const std::string& name)
|
||||
: local_(name == "localtime") {}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
60
Pods/abseil/absl/time/internal/cctz/src/time_zone_libc.h
generated
Normal file
60
Pods/abseil/absl/time/internal/cctz/src/time_zone_libc.h
generated
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "time_zone_if.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// A time zone backed by gmtime_r(3), localtime_r(3), and mktime(3),
|
||||
// and which therefore only supports UTC and the local time zone.
|
||||
class TimeZoneLibC : public TimeZoneIf {
|
||||
public:
|
||||
// Factory.
|
||||
static std::unique_ptr<TimeZoneLibC> Make(const std::string& name);
|
||||
|
||||
// TimeZoneIf implementations.
|
||||
time_zone::absolute_lookup BreakTime(
|
||||
const time_point<seconds>& tp) const override;
|
||||
time_zone::civil_lookup MakeTime(const civil_second& cs) const override;
|
||||
bool NextTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const override;
|
||||
bool PrevTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const override;
|
||||
std::string Version() const override;
|
||||
std::string Description() const override;
|
||||
|
||||
private:
|
||||
explicit TimeZoneLibC(const std::string& name);
|
||||
TimeZoneLibC(const TimeZoneLibC&) = delete;
|
||||
TimeZoneLibC& operator=(const TimeZoneLibC&) = delete;
|
||||
|
||||
const bool local_; // localtime or UTC
|
||||
};
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_
|
||||
363
Pods/abseil/absl/time/internal/cctz/src/time_zone_lookup.cc
generated
Normal file
363
Pods/abseil/absl/time/internal/cctz/src/time_zone_lookup.cc
generated
Normal file
@@ -0,0 +1,363 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <sys/system_properties.h>
|
||||
#if defined(__ANDROID_API__) && __ANDROID_API__ >= 21
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <CoreFoundation/CFTimeZone.h>
|
||||
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
#if defined(__Fuchsia__)
|
||||
#include <fuchsia/intl/cpp/fidl.h>
|
||||
#include <lib/async-loop/cpp/loop.h>
|
||||
#include <lib/fdio/directory.h>
|
||||
#include <zircon/types.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <sdkddkver.h>
|
||||
// Include only when the SDK is for Windows 10 (and later), and the binary is
|
||||
// targeted for Windows XP and later.
|
||||
// Note: The Windows SDK added windows.globalization.h file for Windows 10, but
|
||||
// MinGW did not add it until NTDDI_WIN10_NI (SDK version 10.0.22621.0).
|
||||
#if ((defined(_WIN32_WINNT_WIN10) && !defined(__MINGW32__)) || \
|
||||
(defined(NTDDI_WIN10_NI) && NTDDI_VERSION >= NTDDI_WIN10_NI)) && \
|
||||
(_WIN32_WINNT >= _WIN32_WINNT_WINXP)
|
||||
#define USE_WIN32_LOCAL_TIME_ZONE
|
||||
#include <roapi.h>
|
||||
#include <tchar.h>
|
||||
#include <wchar.h>
|
||||
#include <windows.globalization.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "time_zone_fixed.h"
|
||||
#include "time_zone_impl.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
namespace {
|
||||
#if defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21
|
||||
// Android 'L' removes __system_property_get() from the NDK, however
|
||||
// it is still a hidden symbol in libc so we use dlsym() to access it.
|
||||
// See Chromium's base/sys_info_android.cc for a similar example.
|
||||
|
||||
using property_get_func = int (*)(const char*, char*);
|
||||
|
||||
property_get_func LoadSystemPropertyGet() {
|
||||
int flag = RTLD_LAZY | RTLD_GLOBAL;
|
||||
#if defined(RTLD_NOLOAD)
|
||||
flag |= RTLD_NOLOAD; // libc.so should already be resident
|
||||
#endif
|
||||
if (void* handle = dlopen("libc.so", flag)) {
|
||||
void* sym = dlsym(handle, "__system_property_get");
|
||||
dlclose(handle);
|
||||
return reinterpret_cast<property_get_func>(sym);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int __system_property_get(const char* name, char* value) {
|
||||
static property_get_func system_property_get = LoadSystemPropertyGet();
|
||||
return system_property_get ? system_property_get(name, value) : -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(USE_WIN32_LOCAL_TIME_ZONE)
|
||||
// Calls the WinRT Calendar.GetTimeZone method to obtain the IANA ID of the
|
||||
// local time zone. Returns an empty vector in case of an error.
|
||||
std::string win32_local_time_zone(const HMODULE combase) {
|
||||
std::string result;
|
||||
const auto ro_activate_instance =
|
||||
reinterpret_cast<decltype(&RoActivateInstance)>(
|
||||
GetProcAddress(combase, "RoActivateInstance"));
|
||||
if (!ro_activate_instance) {
|
||||
return result;
|
||||
}
|
||||
const auto windows_create_string_reference =
|
||||
reinterpret_cast<decltype(&WindowsCreateStringReference)>(
|
||||
GetProcAddress(combase, "WindowsCreateStringReference"));
|
||||
if (!windows_create_string_reference) {
|
||||
return result;
|
||||
}
|
||||
const auto windows_delete_string =
|
||||
reinterpret_cast<decltype(&WindowsDeleteString)>(
|
||||
GetProcAddress(combase, "WindowsDeleteString"));
|
||||
if (!windows_delete_string) {
|
||||
return result;
|
||||
}
|
||||
const auto windows_get_string_raw_buffer =
|
||||
reinterpret_cast<decltype(&WindowsGetStringRawBuffer)>(
|
||||
GetProcAddress(combase, "WindowsGetStringRawBuffer"));
|
||||
if (!windows_get_string_raw_buffer) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// The string returned by WindowsCreateStringReference doesn't need to be
|
||||
// deleted.
|
||||
HSTRING calendar_class_id;
|
||||
HSTRING_HEADER calendar_class_id_header;
|
||||
HRESULT hr = windows_create_string_reference(
|
||||
RuntimeClass_Windows_Globalization_Calendar,
|
||||
sizeof(RuntimeClass_Windows_Globalization_Calendar) / sizeof(wchar_t) - 1,
|
||||
&calendar_class_id_header, &calendar_class_id);
|
||||
if (FAILED(hr)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
IInspectable* calendar;
|
||||
hr = ro_activate_instance(calendar_class_id, &calendar);
|
||||
if (FAILED(hr)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
ABI::Windows::Globalization::ITimeZoneOnCalendar* time_zone;
|
||||
hr = calendar->QueryInterface(IID_PPV_ARGS(&time_zone));
|
||||
if (FAILED(hr)) {
|
||||
calendar->Release();
|
||||
return result;
|
||||
}
|
||||
|
||||
HSTRING tz_hstr;
|
||||
hr = time_zone->GetTimeZone(&tz_hstr);
|
||||
if (SUCCEEDED(hr)) {
|
||||
UINT32 wlen;
|
||||
const PCWSTR tz_wstr = windows_get_string_raw_buffer(tz_hstr, &wlen);
|
||||
if (tz_wstr) {
|
||||
const int size =
|
||||
WideCharToMultiByte(CP_UTF8, 0, tz_wstr, static_cast<int>(wlen),
|
||||
nullptr, 0, nullptr, nullptr);
|
||||
result.resize(static_cast<size_t>(size));
|
||||
WideCharToMultiByte(CP_UTF8, 0, tz_wstr, static_cast<int>(wlen),
|
||||
&result[0], size, nullptr, nullptr);
|
||||
}
|
||||
windows_delete_string(tz_hstr);
|
||||
}
|
||||
time_zone->Release();
|
||||
calendar->Release();
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
std::string time_zone::name() const { return effective_impl().Name(); }
|
||||
|
||||
time_zone::absolute_lookup time_zone::lookup(
|
||||
const time_point<seconds>& tp) const {
|
||||
return effective_impl().BreakTime(tp);
|
||||
}
|
||||
|
||||
time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const {
|
||||
return effective_impl().MakeTime(cs);
|
||||
}
|
||||
|
||||
bool time_zone::next_transition(const time_point<seconds>& tp,
|
||||
civil_transition* trans) const {
|
||||
return effective_impl().NextTransition(tp, trans);
|
||||
}
|
||||
|
||||
bool time_zone::prev_transition(const time_point<seconds>& tp,
|
||||
civil_transition* trans) const {
|
||||
return effective_impl().PrevTransition(tp, trans);
|
||||
}
|
||||
|
||||
std::string time_zone::version() const { return effective_impl().Version(); }
|
||||
|
||||
std::string time_zone::description() const {
|
||||
return effective_impl().Description();
|
||||
}
|
||||
|
||||
const time_zone::Impl& time_zone::effective_impl() const {
|
||||
if (impl_ == nullptr) {
|
||||
// Dereferencing an implicit-UTC time_zone is expected to be
|
||||
// rare, so we don't mind paying a small synchronization cost.
|
||||
return *time_zone::Impl::UTC().impl_;
|
||||
}
|
||||
return *impl_;
|
||||
}
|
||||
|
||||
bool load_time_zone(const std::string& name, time_zone* tz) {
|
||||
return time_zone::Impl::LoadTimeZone(name, tz);
|
||||
}
|
||||
|
||||
time_zone utc_time_zone() {
|
||||
return time_zone::Impl::UTC(); // avoid name lookup
|
||||
}
|
||||
|
||||
time_zone fixed_time_zone(const seconds& offset) {
|
||||
time_zone tz;
|
||||
load_time_zone(FixedOffsetToName(offset), &tz);
|
||||
return tz;
|
||||
}
|
||||
|
||||
time_zone local_time_zone() {
|
||||
const char* zone = ":localtime";
|
||||
#if defined(__ANDROID__)
|
||||
char sysprop[PROP_VALUE_MAX];
|
||||
if (__system_property_get("persist.sys.timezone", sysprop) > 0) {
|
||||
zone = sysprop;
|
||||
}
|
||||
#endif
|
||||
#if defined(__APPLE__)
|
||||
std::vector<char> buffer;
|
||||
CFTimeZoneRef tz_default = CFTimeZoneCopyDefault();
|
||||
if (CFStringRef tz_name = CFTimeZoneGetName(tz_default)) {
|
||||
CFStringEncoding encoding = kCFStringEncodingUTF8;
|
||||
CFIndex length = CFStringGetLength(tz_name);
|
||||
CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, encoding) + 1;
|
||||
buffer.resize(static_cast<size_t>(max_size));
|
||||
if (CFStringGetCString(tz_name, &buffer[0], max_size, encoding)) {
|
||||
zone = &buffer[0];
|
||||
}
|
||||
}
|
||||
CFRelease(tz_default);
|
||||
#endif
|
||||
#if defined(__Fuchsia__)
|
||||
std::string primary_tz;
|
||||
[&]() {
|
||||
// Note: We can't use the synchronous FIDL API here because it doesn't
|
||||
// allow timeouts; if the FIDL call failed, local_time_zone() would never
|
||||
// return.
|
||||
|
||||
const zx::duration kTimeout = zx::msec(500);
|
||||
|
||||
// Don't attach to the thread because otherwise the thread's dispatcher
|
||||
// would be set to null when the loop is destroyed, causing any other FIDL
|
||||
// code running on the same thread to crash.
|
||||
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
|
||||
|
||||
fuchsia::intl::PropertyProviderHandle handle;
|
||||
zx_status_t status = fdio_service_connect_by_name(
|
||||
fuchsia::intl::PropertyProvider::Name_,
|
||||
handle.NewRequest().TakeChannel().release());
|
||||
if (status != ZX_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
fuchsia::intl::PropertyProviderPtr intl_provider;
|
||||
status = intl_provider.Bind(std::move(handle), loop.dispatcher());
|
||||
if (status != ZX_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
intl_provider->GetProfile(
|
||||
[&loop, &primary_tz](fuchsia::intl::Profile profile) {
|
||||
if (!profile.time_zones().empty()) {
|
||||
primary_tz = profile.time_zones()[0].id;
|
||||
}
|
||||
loop.Quit();
|
||||
});
|
||||
loop.Run(zx::deadline_after(kTimeout));
|
||||
}();
|
||||
|
||||
if (!primary_tz.empty()) {
|
||||
zone = primary_tz.c_str();
|
||||
}
|
||||
#endif
|
||||
#if defined(USE_WIN32_LOCAL_TIME_ZONE)
|
||||
// Use the WinRT Calendar class to get the local time zone. This feature is
|
||||
// available on Windows 10 and later. The library is dynamically linked to
|
||||
// maintain binary compatibility with Windows XP - Windows 7. On Windows 8,
|
||||
// The combase.dll API functions are available but the RoActivateInstance
|
||||
// call will fail for the Calendar class.
|
||||
std::string winrt_tz;
|
||||
const HMODULE combase =
|
||||
LoadLibraryEx(_T("combase.dll"), nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
if (combase) {
|
||||
const auto ro_initialize = reinterpret_cast<decltype(&::RoInitialize)>(
|
||||
GetProcAddress(combase, "RoInitialize"));
|
||||
const auto ro_uninitialize = reinterpret_cast<decltype(&::RoUninitialize)>(
|
||||
GetProcAddress(combase, "RoUninitialize"));
|
||||
if (ro_initialize && ro_uninitialize) {
|
||||
const HRESULT hr = ro_initialize(RO_INIT_MULTITHREADED);
|
||||
// RPC_E_CHANGED_MODE means that a previous RoInitialize call specified
|
||||
// a different concurrency model. The WinRT runtime is initialized and
|
||||
// should work for our purpose here, but we should *not* call
|
||||
// RoUninitialize because it's a failure.
|
||||
if (SUCCEEDED(hr) || hr == RPC_E_CHANGED_MODE) {
|
||||
winrt_tz = win32_local_time_zone(combase);
|
||||
if (SUCCEEDED(hr)) {
|
||||
ro_uninitialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
FreeLibrary(combase);
|
||||
}
|
||||
if (!winrt_tz.empty()) {
|
||||
zone = winrt_tz.c_str();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Allow ${TZ} to override to default zone.
|
||||
char* tz_env = nullptr;
|
||||
#if defined(_MSC_VER)
|
||||
_dupenv_s(&tz_env, nullptr, "TZ");
|
||||
#else
|
||||
tz_env = std::getenv("TZ");
|
||||
#endif
|
||||
if (tz_env) zone = tz_env;
|
||||
|
||||
// We only support the "[:]<zone-name>" form.
|
||||
if (*zone == ':') ++zone;
|
||||
|
||||
// Map "localtime" to a system-specific name, but
|
||||
// allow ${LOCALTIME} to override the default name.
|
||||
char* localtime_env = nullptr;
|
||||
if (strcmp(zone, "localtime") == 0) {
|
||||
#if defined(_MSC_VER)
|
||||
// System-specific default is just "localtime".
|
||||
_dupenv_s(&localtime_env, nullptr, "LOCALTIME");
|
||||
#else
|
||||
zone = "/etc/localtime"; // System-specific default.
|
||||
localtime_env = std::getenv("LOCALTIME");
|
||||
#endif
|
||||
if (localtime_env) zone = localtime_env;
|
||||
}
|
||||
|
||||
const std::string name = zone;
|
||||
#if defined(_MSC_VER)
|
||||
free(localtime_env);
|
||||
free(tz_env);
|
||||
#endif
|
||||
|
||||
time_zone tz;
|
||||
load_time_zone(name, &tz); // Falls back to UTC.
|
||||
// TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
|
||||
// arrange for %z to generate "-0000" when we don't know the local
|
||||
// offset because the load_time_zone() failed and we're using UTC.
|
||||
return tz;
|
||||
}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
159
Pods/abseil/absl/time/internal/cctz/src/time_zone_posix.cc
generated
Normal file
159
Pods/abseil/absl/time/internal/cctz/src/time_zone_posix.cc
generated
Normal file
@@ -0,0 +1,159 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "time_zone_posix.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
namespace {
|
||||
|
||||
const char kDigits[] = "0123456789";
|
||||
|
||||
const char* ParseInt(const char* p, int min, int max, int* vp) {
|
||||
int value = 0;
|
||||
const char* op = p;
|
||||
const int kMaxInt = std::numeric_limits<int>::max();
|
||||
for (; const char* dp = strchr(kDigits, *p); ++p) {
|
||||
int d = static_cast<int>(dp - kDigits);
|
||||
if (d >= 10) break; // '\0'
|
||||
if (value > kMaxInt / 10) return nullptr;
|
||||
value *= 10;
|
||||
if (value > kMaxInt - d) return nullptr;
|
||||
value += d;
|
||||
}
|
||||
if (p == op || value < min || value > max) return nullptr;
|
||||
*vp = value;
|
||||
return p;
|
||||
}
|
||||
|
||||
// abbr = <.*?> | [^-+,\d]{3,}
|
||||
const char* ParseAbbr(const char* p, std::string* abbr) {
|
||||
const char* op = p;
|
||||
if (*p == '<') { // special zoneinfo <...> form
|
||||
while (*++p != '>') {
|
||||
if (*p == '\0') return nullptr;
|
||||
}
|
||||
abbr->assign(op + 1, static_cast<std::size_t>(p - op) - 1);
|
||||
return ++p;
|
||||
}
|
||||
while (*p != '\0') {
|
||||
if (strchr("-+,", *p)) break;
|
||||
if (strchr(kDigits, *p)) break;
|
||||
++p;
|
||||
}
|
||||
if (p - op < 3) return nullptr;
|
||||
abbr->assign(op, static_cast<std::size_t>(p - op));
|
||||
return p;
|
||||
}
|
||||
|
||||
// offset = [+|-]hh[:mm[:ss]] (aggregated into single seconds value)
|
||||
const char* ParseOffset(const char* p, int min_hour, int max_hour, int sign,
|
||||
std::int_fast32_t* offset) {
|
||||
if (p == nullptr) return nullptr;
|
||||
if (*p == '+' || *p == '-') {
|
||||
if (*p++ == '-') sign = -sign;
|
||||
}
|
||||
int hours = 0;
|
||||
int minutes = 0;
|
||||
int seconds = 0;
|
||||
|
||||
p = ParseInt(p, min_hour, max_hour, &hours);
|
||||
if (p == nullptr) return nullptr;
|
||||
if (*p == ':') {
|
||||
p = ParseInt(p + 1, 0, 59, &minutes);
|
||||
if (p == nullptr) return nullptr;
|
||||
if (*p == ':') {
|
||||
p = ParseInt(p + 1, 0, 59, &seconds);
|
||||
if (p == nullptr) return nullptr;
|
||||
}
|
||||
}
|
||||
*offset = sign * ((((hours * 60) + minutes) * 60) + seconds);
|
||||
return p;
|
||||
}
|
||||
|
||||
// datetime = ( Jn | n | Mm.w.d ) [ / offset ]
|
||||
const char* ParseDateTime(const char* p, PosixTransition* res) {
|
||||
if (p != nullptr && *p == ',') {
|
||||
if (*++p == 'M') {
|
||||
int month = 0;
|
||||
if ((p = ParseInt(p + 1, 1, 12, &month)) != nullptr && *p == '.') {
|
||||
int week = 0;
|
||||
if ((p = ParseInt(p + 1, 1, 5, &week)) != nullptr && *p == '.') {
|
||||
int weekday = 0;
|
||||
if ((p = ParseInt(p + 1, 0, 6, &weekday)) != nullptr) {
|
||||
res->date.fmt = PosixTransition::M;
|
||||
res->date.m.month = static_cast<std::int_fast8_t>(month);
|
||||
res->date.m.week = static_cast<std::int_fast8_t>(week);
|
||||
res->date.m.weekday = static_cast<std::int_fast8_t>(weekday);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (*p == 'J') {
|
||||
int day = 0;
|
||||
if ((p = ParseInt(p + 1, 1, 365, &day)) != nullptr) {
|
||||
res->date.fmt = PosixTransition::J;
|
||||
res->date.j.day = static_cast<std::int_fast16_t>(day);
|
||||
}
|
||||
} else {
|
||||
int day = 0;
|
||||
if ((p = ParseInt(p, 0, 365, &day)) != nullptr) {
|
||||
res->date.fmt = PosixTransition::N;
|
||||
res->date.n.day = static_cast<std::int_fast16_t>(day);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (p != nullptr) {
|
||||
res->time.offset = 2 * 60 * 60; // default offset is 02:00:00
|
||||
if (*p == '/') p = ParseOffset(p + 1, -167, 167, 1, &res->time.offset);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// spec = std offset [ dst [ offset ] , datetime , datetime ]
|
||||
bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res) {
|
||||
const char* p = spec.c_str();
|
||||
if (*p == ':') return false;
|
||||
|
||||
p = ParseAbbr(p, &res->std_abbr);
|
||||
p = ParseOffset(p, 0, 24, -1, &res->std_offset);
|
||||
if (p == nullptr) return false;
|
||||
if (*p == '\0') return true;
|
||||
|
||||
p = ParseAbbr(p, &res->dst_abbr);
|
||||
if (p == nullptr) return false;
|
||||
res->dst_offset = res->std_offset + (60 * 60); // default
|
||||
if (*p != ',') p = ParseOffset(p, 0, 24, -1, &res->dst_offset);
|
||||
|
||||
p = ParseDateTime(p, &res->dst_start);
|
||||
p = ParseDateTime(p, &res->dst_end);
|
||||
|
||||
return p != nullptr && *p == '\0';
|
||||
}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
132
Pods/abseil/absl/time/internal/cctz/src/time_zone_posix.h
generated
Normal file
132
Pods/abseil/absl/time/internal/cctz/src/time_zone_posix.h
generated
Normal file
@@ -0,0 +1,132 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Parsing of a POSIX zone spec as described in the TZ part of section 8.3 in
|
||||
// http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html.
|
||||
//
|
||||
// The current POSIX spec for America/Los_Angeles is "PST8PDT,M3.2.0,M11.1.0",
|
||||
// which would be broken down as ...
|
||||
//
|
||||
// PosixTimeZone {
|
||||
// std_abbr = "PST"
|
||||
// std_offset = -28800
|
||||
// dst_abbr = "PDT"
|
||||
// dst_offset = -25200
|
||||
// dst_start = PosixTransition {
|
||||
// date {
|
||||
// m {
|
||||
// month = 3
|
||||
// week = 2
|
||||
// weekday = 0
|
||||
// }
|
||||
// }
|
||||
// time {
|
||||
// offset = 7200
|
||||
// }
|
||||
// }
|
||||
// dst_end = PosixTransition {
|
||||
// date {
|
||||
// m {
|
||||
// month = 11
|
||||
// week = 1
|
||||
// weekday = 0
|
||||
// }
|
||||
// }
|
||||
// time {
|
||||
// offset = 7200
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// The date/time of the transition. The date is specified as either:
|
||||
// (J) the Nth day of the year (1 <= N <= 365), excluding leap days, or
|
||||
// (N) the Nth day of the year (0 <= N <= 365), including leap days, or
|
||||
// (M) the Nth weekday of a month (e.g., the 2nd Sunday in March).
|
||||
// The time, specified as a day offset, identifies the particular moment
|
||||
// of the transition, and may be negative or >= 24h, and in which case
|
||||
// it would take us to another day, and perhaps week, or even month.
|
||||
struct PosixTransition {
|
||||
enum DateFormat { J, N, M };
|
||||
|
||||
struct Date {
|
||||
struct NonLeapDay {
|
||||
std::int_fast16_t day; // day of non-leap year [1:365]
|
||||
};
|
||||
struct Day {
|
||||
std::int_fast16_t day; // day of year [0:365]
|
||||
};
|
||||
struct MonthWeekWeekday {
|
||||
std::int_fast8_t month; // month of year [1:12]
|
||||
std::int_fast8_t week; // week of month [1:5] (5==last)
|
||||
std::int_fast8_t weekday; // 0==Sun, ..., 6=Sat
|
||||
};
|
||||
|
||||
DateFormat fmt;
|
||||
|
||||
union {
|
||||
NonLeapDay j;
|
||||
Day n;
|
||||
MonthWeekWeekday m;
|
||||
};
|
||||
};
|
||||
|
||||
struct Time {
|
||||
std::int_fast32_t offset; // seconds before/after 00:00:00
|
||||
};
|
||||
|
||||
Date date;
|
||||
Time time;
|
||||
};
|
||||
|
||||
// The entirety of a POSIX-string specified time-zone rule. The standard
|
||||
// abbreviation and offset are always given. If the time zone includes
|
||||
// daylight saving, then the daylight abbreviation is non-empty and the
|
||||
// remaining fields are also valid. Note that the start/end transitions
|
||||
// are not ordered---in the southern hemisphere the transition to end
|
||||
// daylight time occurs first in any particular year.
|
||||
struct PosixTimeZone {
|
||||
std::string std_abbr;
|
||||
std::int_fast32_t std_offset;
|
||||
|
||||
std::string dst_abbr;
|
||||
std::int_fast32_t dst_offset;
|
||||
PosixTransition dst_start;
|
||||
PosixTransition dst_end;
|
||||
};
|
||||
|
||||
// Breaks down a POSIX time-zone specification into its constituent pieces,
|
||||
// filling in any missing values (DST offset, or start/end transition times)
|
||||
// with the standard-defined defaults. Returns false if the specification
|
||||
// could not be parsed (although some fields of *res may have been altered).
|
||||
bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res);
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_
|
||||
118
Pods/abseil/absl/time/internal/cctz/src/tzfile.h
generated
Normal file
118
Pods/abseil/absl/time/internal/cctz/src/tzfile.h
generated
Normal file
@@ -0,0 +1,118 @@
|
||||
/* Layout and location of TZif files. */
|
||||
|
||||
#ifndef TZFILE_H
|
||||
|
||||
#define TZFILE_H
|
||||
|
||||
/*
|
||||
** This file is in the public domain, so clarified as of
|
||||
** 1996-06-05 by Arthur David Olson.
|
||||
*/
|
||||
|
||||
/*
|
||||
** This header is for use ONLY with the time conversion code.
|
||||
** There is no guarantee that it will remain unchanged,
|
||||
** or that it will remain at all.
|
||||
** Do NOT copy it to any system include directory.
|
||||
** Thank you!
|
||||
*/
|
||||
|
||||
/*
|
||||
** Information about time zone files.
|
||||
*/
|
||||
|
||||
#ifndef TZDEFRULES
|
||||
#define TZDEFRULES "posixrules"
|
||||
#endif /* !defined TZDEFRULES */
|
||||
|
||||
/* See Internet RFC 8536 for more details about the following format. */
|
||||
|
||||
/*
|
||||
** Each file begins with. . .
|
||||
*/
|
||||
|
||||
#define TZ_MAGIC "TZif"
|
||||
|
||||
struct tzhead {
|
||||
char tzh_magic[4]; /* TZ_MAGIC */
|
||||
char tzh_version[1]; /* '\0' or '2'-'4' as of 2021 */
|
||||
char tzh_reserved[15]; /* reserved; must be zero */
|
||||
char tzh_ttisutcnt[4]; /* coded number of trans. time flags */
|
||||
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
|
||||
char tzh_leapcnt[4]; /* coded number of leap seconds */
|
||||
char tzh_timecnt[4]; /* coded number of transition times */
|
||||
char tzh_typecnt[4]; /* coded number of local time types */
|
||||
char tzh_charcnt[4]; /* coded number of abbr. chars */
|
||||
};
|
||||
|
||||
/*
|
||||
** . . .followed by. . .
|
||||
**
|
||||
** tzh_timecnt (char [4])s coded transition times a la time(2)
|
||||
** tzh_timecnt (unsigned char)s types of local time starting at above
|
||||
** tzh_typecnt repetitions of
|
||||
** one (char [4]) coded UT offset in seconds
|
||||
** one (unsigned char) used to set tm_isdst
|
||||
** one (unsigned char) that's an abbreviation list index
|
||||
** tzh_charcnt (char)s '\0'-terminated zone abbreviations
|
||||
** tzh_leapcnt repetitions of
|
||||
** one (char [4]) coded leap second transition times
|
||||
** one (char [4]) total correction after above
|
||||
** tzh_ttisstdcnt (char)s indexed by type; if 1, transition
|
||||
** time is standard time, if 0,
|
||||
** transition time is local (wall clock)
|
||||
** time; if absent, transition times are
|
||||
** assumed to be local time
|
||||
** tzh_ttisutcnt (char)s indexed by type; if 1, transition
|
||||
** time is UT, if 0, transition time is
|
||||
** local time; if absent, transition
|
||||
** times are assumed to be local time.
|
||||
** When this is 1, the corresponding
|
||||
** std/wall indicator must also be 1.
|
||||
*/
|
||||
|
||||
/*
|
||||
** If tzh_version is '2' or greater, the above is followed by a second instance
|
||||
** of tzhead and a second instance of the data in which each coded transition
|
||||
** time uses 8 rather than 4 chars,
|
||||
** then a POSIX-TZ-environment-variable-style string for use in handling
|
||||
** instants after the last transition time stored in the file
|
||||
** (with nothing between the newlines if there is no POSIX representation for
|
||||
** such instants).
|
||||
**
|
||||
** If tz_version is '3' or greater, the above is extended as follows.
|
||||
** First, the POSIX TZ string's hour offset may range from -167
|
||||
** through 167 as compared to the POSIX-required 0 through 24.
|
||||
** Second, its DST start time may be January 1 at 00:00 and its stop
|
||||
** time December 31 at 24:00 plus the difference between DST and
|
||||
** standard time, indicating DST all year.
|
||||
*/
|
||||
|
||||
/*
|
||||
** In the current implementation, "tzset()" refuses to deal with files that
|
||||
** exceed any of the limits below.
|
||||
*/
|
||||
|
||||
#ifndef TZ_MAX_TIMES
|
||||
/* This must be at least 242 for Europe/London with 'zic -b fat'. */
|
||||
#define TZ_MAX_TIMES 2000
|
||||
#endif /* !defined TZ_MAX_TIMES */
|
||||
|
||||
#ifndef TZ_MAX_TYPES
|
||||
/* This must be at least 18 for Europe/Vilnius with 'zic -b fat'. */
|
||||
#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
|
||||
#endif /* !defined TZ_MAX_TYPES */
|
||||
|
||||
#ifndef TZ_MAX_CHARS
|
||||
/* This must be at least 40 for America/Anchorage. */
|
||||
#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
|
||||
/* (limited by what unsigned chars can hold) */
|
||||
#endif /* !defined TZ_MAX_CHARS */
|
||||
|
||||
#ifndef TZ_MAX_LEAPS
|
||||
/* This must be at least 27 for leap seconds from 1972 through mid-2023.
|
||||
There's a plan to discontinue leap seconds by 2035. */
|
||||
#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
|
||||
#endif /* !defined TZ_MAX_LEAPS */
|
||||
|
||||
#endif /* !defined TZFILE_H */
|
||||
116
Pods/abseil/absl/time/internal/cctz/src/zone_info_source.cc
generated
Normal file
116
Pods/abseil/absl/time/internal/cctz/src/zone_info_source.cc
generated
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/zone_info_source.h"
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// Defined out-of-line to avoid emitting a weak vtable in all TUs.
|
||||
ZoneInfoSource::~ZoneInfoSource() {}
|
||||
std::string ZoneInfoSource::Version() const { return std::string(); }
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz_extension {
|
||||
|
||||
namespace {
|
||||
|
||||
// A default for cctz_extension::zone_info_source_factory, which simply
|
||||
// defers to the fallback factory.
|
||||
std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> DefaultFactory(
|
||||
const std::string& name,
|
||||
const std::function<
|
||||
std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource>(
|
||||
const std::string& name)>& fallback_factory) {
|
||||
return fallback_factory(name);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// A "weak" definition for cctz_extension::zone_info_source_factory.
|
||||
// The user may override this with their own "strong" definition (see
|
||||
// zone_info_source.h).
|
||||
#if !defined(__has_attribute)
|
||||
#define __has_attribute(x) 0
|
||||
#endif
|
||||
// MinGW is GCC on Windows, so while it asserts __has_attribute(weak), the
|
||||
// Windows linker cannot handle that. Nor does the MinGW compiler know how to
|
||||
// pass "#pragma comment(linker, ...)" to the Windows linker.
|
||||
#if (__has_attribute(weak) || defined(__GNUC__)) && !defined(__MINGW32__) && \
|
||||
!defined(__CYGWIN__)
|
||||
ZoneInfoSourceFactory zone_info_source_factory __attribute__((weak)) =
|
||||
DefaultFactory;
|
||||
#elif defined(_MSC_VER) && !defined(__MINGW32__) && !defined(_LIBCPP_VERSION)
|
||||
extern ZoneInfoSourceFactory zone_info_source_factory;
|
||||
extern ZoneInfoSourceFactory default_factory;
|
||||
ZoneInfoSourceFactory default_factory = DefaultFactory;
|
||||
#if defined(_M_IX86) || defined(_M_ARM)
|
||||
#pragma comment( \
|
||||
linker, \
|
||||
"/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@@ZA=?default_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@@ZA")
|
||||
#elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM64)
|
||||
#pragma comment( \
|
||||
linker, \
|
||||
"/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@@ZEA=?default_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@@ZEA")
|
||||
#else
|
||||
#error Unsupported MSVC platform
|
||||
#endif // _M_<PLATFORM>
|
||||
#else
|
||||
// Make it a "strong" definition if we have no other choice.
|
||||
ZoneInfoSourceFactory zone_info_source_factory = DefaultFactory;
|
||||
#endif
|
||||
|
||||
} // namespace cctz_extension
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
Reference in New Issue
Block a user