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