Coverage Report

Created: 2025-11-09 06:33

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