diff options
Diffstat (limited to 'absl/time/internal/cctz/src/time_zone_fixed.cc')
-rw-r--r-- | absl/time/internal/cctz/src/time_zone_fixed.cc | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/absl/time/internal/cctz/src/time_zone_fixed.cc b/absl/time/internal/cctz/src/time_zone_fixed.cc new file mode 100644 index 000000000000..8d3b1442524c --- /dev/null +++ b/absl/time/internal/cctz/src/time_zone_fixed.cc @@ -0,0 +1,133 @@ +// 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 +// +// http://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 <chrono> +#include <cstdio> +#include <cstring> +#include <string> + +namespace absl { +namespace time_internal { +namespace cctz { + +namespace { + +// The prefix used for the internal names of fixed-offset zones. +const char kFixedOffsetPrefix[] = "Fixed/"; + +int Parse02d(const char* p) { + static const char kDigits[] = "0123456789"; + 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, sys_seconds* offset) { + if (name.compare(0, std::string::npos, "UTC", 3) == 0) { + *offset = sys_seconds::zero(); + return true; + } + + const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1; + const char* const ep = kFixedOffsetPrefix + prefix_len; + if (name.size() != prefix_len + 12) // "<prefix>UTC+99:99:99" + return false; + if (!std::equal(kFixedOffsetPrefix, ep, name.begin())) + return false; + const char* np = name.data() + prefix_len; + if (*np++ != 'U' || *np++ != 'T' || *np++ != 'C') + return false; + 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 = sys_seconds(secs * (np[0] == '-' ? -1 : 1)); // "-" means west + return true; +} + +std::string FixedOffsetToName(const sys_seconds& offset) { + if (offset == sys_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 seconds = static_cast<int>(offset.count()); + const char sign = (seconds < 0 ? '-' : '+'); + int minutes = seconds / 60; + seconds %= 60; + if (sign == '-') { + if (seconds > 0) { + seconds -= 60; + minutes += 1; + } + seconds = -seconds; + minutes = -minutes; + } + int hours = minutes / 60; + minutes %= 60; + char buf[sizeof(kFixedOffsetPrefix) + sizeof("UTC-24:00:00")]; + snprintf(buf, sizeof(buf), "%sUTC%c%02d:%02d:%02d", + kFixedOffsetPrefix, sign, hours, minutes, seconds); + return buf; +} + +std::string FixedOffsetToAbbr(const sys_seconds& offset) { + std::string abbr = FixedOffsetToName(offset); + const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1; + const char* const ep = kFixedOffsetPrefix + prefix_len; + if (abbr.size() >= prefix_len) { + if (std::equal(kFixedOffsetPrefix, ep, abbr.begin())) { + abbr.erase(0, prefix_len); + if (abbr.size() == 12) { // UTC+99:99:99 + abbr.erase(9, 1); // UTC+99:9999 + abbr.erase(6, 1); // UTC+999999 + if (abbr[8] == '0' && abbr[9] == '0') { // UTC+999900 + abbr.erase(8, 2); // UTC+9999 + if (abbr[6] == '0' && abbr[7] == '0') { // UTC+9900 + abbr.erase(6, 2); // UTC+99 + if (abbr[4] == '0') { // UTC+09 + abbr.erase(4, 1); // UTC+9 + } + } + } + } + } + } + return abbr; +} + +} // namespace cctz +} // namespace time_internal +} // namespace absl |