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