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_posix.h"
16
17 #include <cstddef>
18 #include <cstring>
19 #include <limits>
20 #include <string>
21
22 #include "absl/base/config.h"
23
24 namespace absl {
25 ABSL_NAMESPACE_BEGIN
26 namespace time_internal {
27 namespace cctz {
28
29 namespace {
30
31 const char kDigits[] = "0123456789";
32
ParseInt(const char * p,int min,int max,int * vp)33 const char* ParseInt(const char* p, int min, int max, int* vp) {
34 int value = 0;
35 const char* op = p;
36 const int kMaxInt = std::numeric_limits<int>::max();
37 for (; const char* dp = strchr(kDigits, *p); ++p) {
38 int d = static_cast<int>(dp - kDigits);
39 if (d >= 10) break; // '\0'
40 if (value > kMaxInt / 10) return nullptr;
41 value *= 10;
42 if (value > kMaxInt - d) return nullptr;
43 value += d;
44 }
45 if (p == op || value < min || value > max) return nullptr;
46 *vp = value;
47 return p;
48 }
49
50 // abbr = <.*?> | [^-+,\d]{3,}
ParseAbbr(const char * p,std::string * abbr)51 const char* ParseAbbr(const char* p, std::string* abbr) {
52 const char* op = p;
53 if (*p == '<') { // special zoneinfo <...> form
54 while (*++p != '>') {
55 if (*p == '\0') return nullptr;
56 }
57 abbr->assign(op + 1, static_cast<std::size_t>(p - op) - 1);
58 return ++p;
59 }
60 while (*p != '\0') {
61 if (strchr("-+,", *p)) break;
62 if (strchr(kDigits, *p)) break;
63 ++p;
64 }
65 if (p - op < 3) return nullptr;
66 abbr->assign(op, static_cast<std::size_t>(p - op));
67 return p;
68 }
69
70 // offset = [+|-]hh[:mm[:ss]] (aggregated into single seconds value)
ParseOffset(const char * p,int min_hour,int max_hour,int sign,std::int_fast32_t * offset)71 const char* ParseOffset(const char* p, int min_hour, int max_hour, int sign,
72 std::int_fast32_t* offset) {
73 if (p == nullptr) return nullptr;
74 if (*p == '+' || *p == '-') {
75 if (*p++ == '-') sign = -sign;
76 }
77 int hours = 0;
78 int minutes = 0;
79 int seconds = 0;
80
81 p = ParseInt(p, min_hour, max_hour, &hours);
82 if (p == nullptr) return nullptr;
83 if (*p == ':') {
84 p = ParseInt(p + 1, 0, 59, &minutes);
85 if (p == nullptr) return nullptr;
86 if (*p == ':') {
87 p = ParseInt(p + 1, 0, 59, &seconds);
88 if (p == nullptr) return nullptr;
89 }
90 }
91 *offset = sign * ((((hours * 60) + minutes) * 60) + seconds);
92 return p;
93 }
94
95 // datetime = ( Jn | n | Mm.w.d ) [ / offset ]
ParseDateTime(const char * p,PosixTransition * res)96 const char* ParseDateTime(const char* p, PosixTransition* res) {
97 if (p != nullptr && *p == ',') {
98 if (*++p == 'M') {
99 int month = 0;
100 if ((p = ParseInt(p + 1, 1, 12, &month)) != nullptr && *p == '.') {
101 int week = 0;
102 if ((p = ParseInt(p + 1, 1, 5, &week)) != nullptr && *p == '.') {
103 int weekday = 0;
104 if ((p = ParseInt(p + 1, 0, 6, &weekday)) != nullptr) {
105 res->date.fmt = PosixTransition::M;
106 res->date.m.month = static_cast<std::int_fast8_t>(month);
107 res->date.m.week = static_cast<std::int_fast8_t>(week);
108 res->date.m.weekday = static_cast<std::int_fast8_t>(weekday);
109 }
110 }
111 }
112 } else if (*p == 'J') {
113 int day = 0;
114 if ((p = ParseInt(p + 1, 1, 365, &day)) != nullptr) {
115 res->date.fmt = PosixTransition::J;
116 res->date.j.day = static_cast<std::int_fast16_t>(day);
117 }
118 } else {
119 int day = 0;
120 if ((p = ParseInt(p, 0, 365, &day)) != nullptr) {
121 res->date.fmt = PosixTransition::N;
122 res->date.n.day = static_cast<std::int_fast16_t>(day);
123 }
124 }
125 }
126 if (p != nullptr) {
127 res->time.offset = 2 * 60 * 60; // default offset is 02:00:00
128 if (*p == '/') p = ParseOffset(p + 1, -167, 167, 1, &res->time.offset);
129 }
130 return p;
131 }
132
133 } // namespace
134
135 // spec = std offset [ dst [ offset ] , datetime , datetime ]
ParsePosixSpec(const std::string & spec,PosixTimeZone * res)136 bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res) {
137 const char* p = spec.c_str();
138 if (*p == ':') return false;
139
140 p = ParseAbbr(p, &res->std_abbr);
141 p = ParseOffset(p, 0, 24, -1, &res->std_offset);
142 if (p == nullptr) return false;
143 if (*p == '\0') return true;
144
145 p = ParseAbbr(p, &res->dst_abbr);
146 if (p == nullptr) return false;
147 res->dst_offset = res->std_offset + (60 * 60); // default
148 if (*p != ',') p = ParseOffset(p, 0, 24, -1, &res->dst_offset);
149
150 p = ParseDateTime(p, &res->dst_start);
151 p = ParseDateTime(p, &res->dst_end);
152
153 return p != nullptr && *p == '\0';
154 }
155
156 } // namespace cctz
157 } // namespace time_internal
158 ABSL_NAMESPACE_END
159 } // namespace absl
160