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