Coverage Report

Created: 2025-12-23 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cctz/src/time_zone_lookup.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 "cctz/time_zone.h"
16
17
#if defined(__ANDROID__)
18
#include <sys/system_properties.h>
19
#endif
20
21
#if defined(__APPLE__)
22
#include <CoreFoundation/CFTimeZone.h>
23
#include <vector>
24
#endif
25
26
#if defined(__Fuchsia__)
27
#include <fuchsia/intl/cpp/fidl.h>
28
#include <lib/async-loop/cpp/loop.h>
29
#include <lib/fdio/directory.h>
30
#include <zircon/types.h>
31
#endif
32
33
#include <array>
34
#include <cstdint>
35
#include <cstdlib>
36
#include <cstring>
37
#include <string>
38
39
#include "time_zone_fixed.h"
40
#include "time_zone_impl.h"
41
42
#if defined(_WIN32)
43
#include "time_zone_name_win.h"
44
#endif  // _WIN32
45
46
namespace cctz {
47
48
0
std::string time_zone::name() const {
49
0
  return effective_impl().Name();
50
0
}
51
52
time_zone::absolute_lookup time_zone::lookup(
53
8.96k
    const time_point<seconds>& tp) const {
54
8.96k
  return effective_impl().BreakTime(tp);
55
8.96k
}
56
57
13.3k
time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const {
58
13.3k
  return effective_impl().MakeTime(cs);
59
13.3k
}
60
61
bool time_zone::next_transition(const time_point<seconds>& tp,
62
0
                                civil_transition* trans) const {
63
0
  return effective_impl().NextTransition(tp, trans);
64
0
}
65
66
bool time_zone::prev_transition(const time_point<seconds>& tp,
67
0
                                civil_transition* trans) const {
68
0
  return effective_impl().PrevTransition(tp, trans);
69
0
}
70
71
0
std::string time_zone::version() const {
72
0
  return effective_impl().Version();
73
0
}
74
75
0
std::string time_zone::description() const {
76
0
  return effective_impl().Description();
77
0
}
78
79
22.3k
const time_zone::Impl& time_zone::effective_impl() const {
80
22.3k
  if (impl_ == nullptr) {
81
    // Dereferencing an implicit-UTC time_zone is expected to be
82
    // rare, so we don't mind paying a small synchronization cost.
83
0
    return *time_zone::Impl::UTC().impl_;
84
0
  }
85
22.3k
  return *impl_;
86
22.3k
}
87
88
8.83k
bool load_time_zone(const std::string& name, time_zone* tz) {
89
8.83k
  return time_zone::Impl::LoadTimeZone(name, tz);
90
8.83k
}
91
92
172
time_zone utc_time_zone() {
93
172
  return time_zone::Impl::UTC();  // avoid name lookup
94
172
}
95
96
0
time_zone fixed_time_zone(const seconds& offset) {
97
0
  time_zone tz;
98
0
  load_time_zone(FixedOffsetToName(offset), &tz);
99
0
  return tz;
100
0
}
101
102
0
time_zone local_time_zone() {
103
0
  const char* zone = ":localtime";
104
#if defined(__ANDROID__)
105
  char sysprop[PROP_VALUE_MAX];
106
  if (__system_property_get("persist.sys.timezone", sysprop) > 0) {
107
    zone = sysprop;
108
  }
109
#endif
110
#if defined(__APPLE__)
111
  std::vector<char> buffer;
112
  CFTimeZoneRef tz_default = CFTimeZoneCopyDefault();
113
  if (CFStringRef tz_name = CFTimeZoneGetName(tz_default)) {
114
    CFStringEncoding encoding = kCFStringEncodingUTF8;
115
    CFIndex length = CFStringGetLength(tz_name);
116
    CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, encoding) + 1;
117
    buffer.resize(static_cast<size_t>(max_size));
118
    if (CFStringGetCString(tz_name, &buffer[0], max_size, encoding)) {
119
      zone = &buffer[0];
120
    }
121
  }
122
  CFRelease(tz_default);
123
#endif
124
#if defined(__Fuchsia__)
125
  std::string primary_tz;
126
  [&]() {
127
    // Note: We can't use the synchronous FIDL API here because it doesn't
128
    // allow timeouts; if the FIDL call failed, local_time_zone() would never
129
    // return.
130
131
    const zx::duration kTimeout = zx::msec(500);
132
133
    // Don't attach to the thread because otherwise the thread's dispatcher
134
    // would be set to null when the loop is destroyed, causing any other FIDL
135
    // code running on the same thread to crash.
136
    async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
137
138
    fuchsia::intl::PropertyProviderHandle handle;
139
    zx_status_t status = fdio_service_connect_by_name(
140
        fuchsia::intl::PropertyProvider::Name_,
141
        handle.NewRequest().TakeChannel().release());
142
    if (status != ZX_OK) {
143
      return;
144
    }
145
146
    fuchsia::intl::PropertyProviderPtr intl_provider;
147
    status = intl_provider.Bind(std::move(handle), loop.dispatcher());
148
    if (status != ZX_OK) {
149
      return;
150
    }
151
152
    intl_provider->GetProfile(
153
        [&loop, &primary_tz](fuchsia::intl::Profile profile) {
154
          if (!profile.time_zones().empty()) {
155
            primary_tz = profile.time_zones()[0].id;
156
          }
157
          loop.Quit();
158
        });
159
    loop.Run(zx::deadline_after(kTimeout));
160
  }();
161
162
  if (!primary_tz.empty()) {
163
    zone = primary_tz.c_str();
164
  }
165
#endif
166
#if defined(_WIN32)
167
  std::string win32_tz = GetWindowsLocalTimeZone();
168
  if (!win32_tz.empty()) {
169
    zone = win32_tz.c_str();
170
  }
171
#endif
172
173
  // Allow ${TZ} to override to default zone.
174
0
  char* tz_env = nullptr;
175
#if defined(_MSC_VER)
176
  _dupenv_s(&tz_env, nullptr, "TZ");
177
#else
178
0
  tz_env = std::getenv("TZ");
179
0
#endif
180
0
  if (tz_env) zone = tz_env;
181
182
  // We only support the "[:]<zone-name>" form.
183
0
  if (*zone == ':') ++zone;
184
185
  // Map "localtime" to a system-specific name, but
186
  // allow ${LOCALTIME} to override the default name.
187
0
  char* localtime_env = nullptr;
188
0
  if (strcmp(zone, "localtime") == 0) {
189
#if defined(_MSC_VER)
190
    // System-specific default is just "localtime".
191
    _dupenv_s(&localtime_env, nullptr, "LOCALTIME");
192
#else
193
0
    zone = "/etc/localtime";  // System-specific default.
194
0
    localtime_env = std::getenv("LOCALTIME");
195
0
#endif
196
0
    if (localtime_env) zone = localtime_env;
197
0
  }
198
199
0
  const std::string name = zone;
200
#if defined(_MSC_VER)
201
  free(localtime_env);
202
  free(tz_env);
203
#endif
204
205
0
  time_zone tz;
206
0
  load_time_zone(name, &tz);  // Falls back to UTC.
207
  // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
208
  // arrange for %z to generate "-0000" when we don't know the local
209
  // offset because the load_time_zone() failed and we're using UTC.
210
0
  return tz;
211
0
}
212
213
}  // namespace cctz