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