time_zone_posix.cc (4824B)
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 "absl/time/internal/cctz/src/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 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,} 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) 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 ] 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 ] 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