1 // Copyright 2016 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "time_zone_fixed.h"
16
17 #include <algorithm>
18 #include <cassert>
19 #include <chrono>
20 #include <cstring>
21 #include <string>
22
23 #include "absl/base/config.h"
24
25 namespace absl {
26 ABSL_NAMESPACE_BEGIN
27 namespace time_internal {
28 namespace cctz {
29
30 namespace {
31
32 // The prefix used for the internal names of fixed-offset zones.
33 const char kFixedZonePrefix[] = "Fixed/UTC";
34
35 const char kDigits[] = "0123456789";
36
Format02d(char * p,int v)37 char* Format02d(char* p, int v) {
38 *p++ = kDigits[(v / 10) % 10];
39 *p++ = kDigits[v % 10];
40 return p;
41 }
42
Parse02d(const char * p)43 int Parse02d(const char* p) {
44 if (const char* ap = std::strchr(kDigits, *p)) {
45 int v = static_cast<int>(ap - kDigits);
46 if (const char* bp = std::strchr(kDigits, *++p)) {
47 return (v * 10) + static_cast<int>(bp - kDigits);
48 }
49 }
50 return -1;
51 }
52
53 } // namespace
54
FixedOffsetFromName(const std::string & name,seconds * offset)55 bool FixedOffsetFromName(const std::string& name, seconds* offset) {
56 if (name.compare(0, std::string::npos, "UTC", 3) == 0) {
57 *offset = seconds::zero();
58 return true;
59 }
60
61 const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
62 const char* const ep = kFixedZonePrefix + prefix_len;
63 if (name.size() != prefix_len + 9) // <prefix>+99:99:99
64 return false;
65 if (!std::equal(kFixedZonePrefix, ep, name.begin())) return false;
66 const char* np = name.data() + prefix_len;
67 if (np[0] != '+' && np[0] != '-') return false;
68 if (np[3] != ':' || np[6] != ':') // see note below about large offsets
69 return false;
70
71 int hours = Parse02d(np + 1);
72 if (hours == -1) return false;
73 int mins = Parse02d(np + 4);
74 if (mins == -1) return false;
75 int secs = Parse02d(np + 7);
76 if (secs == -1) return false;
77
78 secs += ((hours * 60) + mins) * 60;
79 if (secs > 24 * 60 * 60) return false; // outside supported offset range
80 *offset = seconds(secs * (np[0] == '-' ? -1 : 1)); // "-" means west
81 return true;
82 }
83
FixedOffsetToName(const seconds & offset)84 std::string FixedOffsetToName(const seconds& offset) {
85 if (offset == seconds::zero()) return "UTC";
86 if (offset < std::chrono::hours(-24) || offset > std::chrono::hours(24)) {
87 // We don't support fixed-offset zones more than 24 hours
88 // away from UTC to avoid complications in rendering such
89 // offsets and to (somewhat) limit the total number of zones.
90 return "UTC";
91 }
92 int offset_seconds = static_cast<int>(offset.count());
93 const char sign = (offset_seconds < 0 ? '-' : '+');
94 int offset_minutes = offset_seconds / 60;
95 offset_seconds %= 60;
96 if (sign == '-') {
97 if (offset_seconds > 0) {
98 offset_seconds -= 60;
99 offset_minutes += 1;
100 }
101 offset_seconds = -offset_seconds;
102 offset_minutes = -offset_minutes;
103 }
104 int offset_hours = offset_minutes / 60;
105 offset_minutes %= 60;
106 const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
107 char buf[prefix_len + sizeof("-24:00:00")];
108 char* ep = std::copy(kFixedZonePrefix, kFixedZonePrefix + prefix_len, buf);
109 *ep++ = sign;
110 ep = Format02d(ep, offset_hours);
111 *ep++ = ':';
112 ep = Format02d(ep, offset_minutes);
113 *ep++ = ':';
114 ep = Format02d(ep, offset_seconds);
115 *ep++ = '\0';
116 assert(ep == buf + sizeof(buf));
117 return buf;
118 }
119
FixedOffsetToAbbr(const seconds & offset)120 std::string FixedOffsetToAbbr(const seconds& offset) {
121 std::string abbr = FixedOffsetToName(offset);
122 const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
123 if (abbr.size() == prefix_len + 9) { // <prefix>+99:99:99
124 abbr.erase(0, prefix_len); // +99:99:99
125 abbr.erase(6, 1); // +99:9999
126 abbr.erase(3, 1); // +999999
127 if (abbr[5] == '0' && abbr[6] == '0') { // +999900
128 abbr.erase(5, 2); // +9999
129 if (abbr[3] == '0' && abbr[4] == '0') { // +9900
130 abbr.erase(3, 2); // +99
131 }
132 }
133 }
134 return abbr;
135 }
136
137 } // namespace cctz
138 } // namespace time_internal
139 ABSL_NAMESPACE_END
140 } // namespace absl
141