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 namespace cctz {
24
25 namespace {
26
27 // The prefix used for the internal names of fixed-offset zones.
28 const char kFixedZonePrefix[] = "Fixed/UTC";
29
30 const char kDigits[] = "0123456789";
31
Format02d(char * p,int v)32 char* Format02d(char* p, int v) {
33 *p++ = kDigits[(v / 10) % 10];
34 *p++ = kDigits[v % 10];
35 return p;
36 }
37
Parse02d(const char * p)38 int Parse02d(const char* p) {
39 if (const char* ap = std::strchr(kDigits, *p)) {
40 int v = static_cast<int>(ap - kDigits);
41 if (const char* bp = std::strchr(kDigits, *++p)) {
42 return (v * 10) + static_cast<int>(bp - kDigits);
43 }
44 }
45 return -1;
46 }
47
48 } // namespace
49
FixedOffsetFromName(const std::string & name,seconds * offset)50 bool FixedOffsetFromName(const std::string& name, seconds* offset) {
51 if (name.compare(0, std::string::npos, "UTC", 3) == 0) {
52 *offset = seconds::zero();
53 return true;
54 }
55
56 const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
57 const char* const ep = kFixedZonePrefix + prefix_len;
58 if (name.size() != prefix_len + 9) // <prefix>+99:99:99
59 return false;
60 if (!std::equal(kFixedZonePrefix, ep, name.begin()))
61 return false;
62 const char* np = name.data() + prefix_len;
63 if (np[0] != '+' && np[0] != '-')
64 return false;
65 if (np[3] != ':' || np[6] != ':') // see note below about large offsets
66 return false;
67
68 int hours = Parse02d(np + 1);
69 if (hours == -1) return false;
70 int mins = Parse02d(np + 4);
71 if (mins == -1) return false;
72 int secs = Parse02d(np + 7);
73 if (secs == -1) return false;
74
75 secs += ((hours * 60) + mins) * 60;
76 if (secs > 24 * 60 * 60) return false; // outside supported offset range
77 *offset = seconds(secs * (np[0] == '-' ? -1 : 1)); // "-" means west
78 return true;
79 }
80
FixedOffsetToName(const seconds & offset)81 std::string FixedOffsetToName(const seconds& offset) {
82 if (offset == seconds::zero()) return "UTC";
83 if (offset < std::chrono::hours(-24) || offset > std::chrono::hours(24)) {
84 // We don't support fixed-offset zones more than 24 hours
85 // away from UTC to avoid complications in rendering such
86 // offsets and to (somewhat) limit the total number of zones.
87 return "UTC";
88 }
89 int offset_seconds = static_cast<int>(offset.count());
90 const char sign = (offset_seconds < 0 ? '-' : '+');
91 int offset_minutes = offset_seconds / 60;
92 offset_seconds %= 60;
93 if (sign == '-') {
94 if (offset_seconds > 0) {
95 offset_seconds -= 60;
96 offset_minutes += 1;
97 }
98 offset_seconds = -offset_seconds;
99 offset_minutes = -offset_minutes;
100 }
101 int offset_hours = offset_minutes / 60;
102 offset_minutes %= 60;
103 const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
104 char buf[prefix_len + sizeof("-24:00:00")];
105 char* ep = std::copy(kFixedZonePrefix, kFixedZonePrefix + prefix_len, buf);
106 *ep++ = sign;
107 ep = Format02d(ep, offset_hours);
108 *ep++ = ':';
109 ep = Format02d(ep, offset_minutes);
110 *ep++ = ':';
111 ep = Format02d(ep, offset_seconds);
112 *ep++ = '\0';
113 assert(ep == buf + sizeof(buf));
114 return buf;
115 }
116
FixedOffsetToAbbr(const seconds & offset)117 std::string FixedOffsetToAbbr(const seconds& offset) {
118 std::string abbr = FixedOffsetToName(offset);
119 const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
120 if (abbr.size() == prefix_len + 9) { // <prefix>+99:99:99
121 abbr.erase(0, prefix_len); // +99:99:99
122 abbr.erase(6, 1); // +99:9999
123 abbr.erase(3, 1); // +999999
124 if (abbr[5] == '0' && abbr[6] == '0') { // +999900
125 abbr.erase(5, 2); // +9999
126 if (abbr[3] == '0' && abbr[4] == '0') { // +9900
127 abbr.erase(3, 2); // +99
128 }
129 }
130 }
131 return abbr;
132 }
133
134 } // namespace cctz
135