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/base/config.h"
16 #include "absl/time/internal/cctz/include/cctz/time_zone.h"
17
18 #if defined(__ANDROID__)
19 #include <sys/system_properties.h>
20 #if defined(__ANDROID_API__) && __ANDROID_API__ >= 21
21 #include <dlfcn.h>
22 #endif
23 #endif
24
25 #if defined(__APPLE__)
26 #include <CoreFoundation/CFTimeZone.h>
27
28 #include <vector>
29 #endif
30
31 #if defined(__Fuchsia__)
32 #include <fuchsia/intl/cpp/fidl.h>
33 #include <lib/async-loop/cpp/loop.h>
34 #include <lib/sys/cpp/component_context.h>
35 #include <zircon/types.h>
36 #endif
37
38 #include <cstdlib>
39 #include <cstring>
40 #include <string>
41
42 #include "time_zone_fixed.h"
43 #include "time_zone_impl.h"
44
45 namespace absl {
46 ABSL_NAMESPACE_BEGIN
47 namespace time_internal {
48 namespace cctz {
49
50 #if defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21
51 namespace {
52 // Android 'L' removes __system_property_get() from the NDK, however
53 // it is still a hidden symbol in libc so we use dlsym() to access it.
54 // See Chromium's base/sys_info_android.cc for a similar example.
55
56 using property_get_func = int (*)(const char*, char*);
57
LoadSystemPropertyGet()58 property_get_func LoadSystemPropertyGet() {
59 int flag = RTLD_LAZY | RTLD_GLOBAL;
60 #if defined(RTLD_NOLOAD)
61 flag |= RTLD_NOLOAD; // libc.so should already be resident
62 #endif
63 if (void* handle = dlopen("libc.so", flag)) {
64 void* sym = dlsym(handle, "__system_property_get");
65 dlclose(handle);
66 return reinterpret_cast<property_get_func>(sym);
67 }
68 return nullptr;
69 }
70
__system_property_get(const char * name,char * value)71 int __system_property_get(const char* name, char* value) {
72 static property_get_func system_property_get = LoadSystemPropertyGet();
73 return system_property_get ? system_property_get(name, value) : -1;
74 }
75
76 } // namespace
77 #endif
78
name() const79 std::string time_zone::name() const { return effective_impl().Name(); }
80
lookup(const time_point<seconds> & tp) const81 time_zone::absolute_lookup time_zone::lookup(
82 const time_point<seconds>& tp) const {
83 return effective_impl().BreakTime(tp);
84 }
85
lookup(const civil_second & cs) const86 time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const {
87 return effective_impl().MakeTime(cs);
88 }
89
next_transition(const time_point<seconds> & tp,civil_transition * trans) const90 bool time_zone::next_transition(const time_point<seconds>& tp,
91 civil_transition* trans) const {
92 return effective_impl().NextTransition(tp, trans);
93 }
94
prev_transition(const time_point<seconds> & tp,civil_transition * trans) const95 bool time_zone::prev_transition(const time_point<seconds>& tp,
96 civil_transition* trans) const {
97 return effective_impl().PrevTransition(tp, trans);
98 }
99
version() const100 std::string time_zone::version() const { return effective_impl().Version(); }
101
description() const102 std::string time_zone::description() const {
103 return effective_impl().Description();
104 }
105
effective_impl() const106 const time_zone::Impl& time_zone::effective_impl() const {
107 if (impl_ == nullptr) {
108 // Dereferencing an implicit-UTC time_zone is expected to be
109 // rare, so we don't mind paying a small synchronization cost.
110 return *time_zone::Impl::UTC().impl_;
111 }
112 return *impl_;
113 }
114
load_time_zone(const std::string & name,time_zone * tz)115 bool load_time_zone(const std::string& name, time_zone* tz) {
116 return time_zone::Impl::LoadTimeZone(name, tz);
117 }
118
utc_time_zone()119 time_zone utc_time_zone() {
120 return time_zone::Impl::UTC(); // avoid name lookup
121 }
122
fixed_time_zone(const seconds & offset)123 time_zone fixed_time_zone(const seconds& offset) {
124 time_zone tz;
125 load_time_zone(FixedOffsetToName(offset), &tz);
126 return tz;
127 }
128
local_time_zone()129 time_zone local_time_zone() {
130 const char* zone = ":localtime";
131 #if defined(__ANDROID__)
132 char sysprop[PROP_VALUE_MAX];
133 if (__system_property_get("persist.sys.timezone", sysprop) > 0) {
134 zone = sysprop;
135 }
136 #endif
137 #if defined(__APPLE__)
138 std::vector<char> buffer;
139 CFTimeZoneRef tz_default = CFTimeZoneCopyDefault();
140 if (CFStringRef tz_name = CFTimeZoneGetName(tz_default)) {
141 CFStringEncoding encoding = kCFStringEncodingUTF8;
142 CFIndex length = CFStringGetLength(tz_name);
143 buffer.resize(CFStringGetMaximumSizeForEncoding(length, encoding) + 1);
144 if (CFStringGetCString(tz_name, &buffer[0], buffer.size(), encoding)) {
145 zone = &buffer[0];
146 }
147 }
148 CFRelease(tz_default);
149 #endif
150 #if defined(__Fuchsia__)
151 std::string primary_tz;
152 [&]() {
153 // Note: We can't use the synchronous FIDL API here because it doesn't
154 // allow timeouts; if the FIDL call failed, local_time_zone() would never
155 // return.
156
157 const zx::duration kTimeout = zx::msec(500);
158
159 // Don't attach to the thread because otherwise the thread's dispatcher
160 // would be set to null when the loop is destroyed, causing any other FIDL
161 // code running on the same thread to crash.
162 async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
163 std::unique_ptr<sys::ComponentContext> context =
164 sys::ComponentContext::Create();
165
166 fuchsia::intl::PropertyProviderHandle handle;
167 zx_status_t status = context->svc()->Connect(handle.NewRequest());
168 if (status != ZX_OK) {
169 return;
170 }
171
172 fuchsia::intl::PropertyProviderPtr intl_provider;
173 status = intl_provider.Bind(std::move(handle), loop.dispatcher());
174 if (status != ZX_OK) {
175 return;
176 }
177
178 intl_provider->GetProfile(
179 [&loop, &primary_tz](fuchsia::intl::Profile profile) {
180 if (!profile.time_zones().empty()) {
181 primary_tz = profile.time_zones()[0].id;
182 }
183 loop.Quit();
184 });
185 loop.Run(zx::deadline_after(kTimeout));
186 }();
187
188 if (!primary_tz.empty()) {
189 zone = primary_tz.c_str();
190 }
191 #endif
192
193 // Allow ${TZ} to override to default zone.
194 char* tz_env = nullptr;
195 #if defined(_MSC_VER)
196 _dupenv_s(&tz_env, nullptr, "TZ");
197 #else
198 tz_env = std::getenv("TZ");
199 #endif
200 if (tz_env) zone = tz_env;
201
202 // We only support the "[:]<zone-name>" form.
203 if (*zone == ':') ++zone;
204
205 // Map "localtime" to a system-specific name, but
206 // allow ${LOCALTIME} to override the default name.
207 char* localtime_env = nullptr;
208 if (strcmp(zone, "localtime") == 0) {
209 #if defined(_MSC_VER)
210 // System-specific default is just "localtime".
211 _dupenv_s(&localtime_env, nullptr, "LOCALTIME");
212 #else
213 zone = "/etc/localtime"; // System-specific default.
214 localtime_env = std::getenv("LOCALTIME");
215 #endif
216 if (localtime_env) zone = localtime_env;
217 }
218
219 const std::string name = zone;
220 #if defined(_MSC_VER)
221 free(localtime_env);
222 free(tz_env);
223 #endif
224
225 time_zone tz;
226 load_time_zone(name, &tz); // Falls back to UTC.
227 // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
228 // arrange for %z to generate "-0000" when we don't know the local
229 // offset because the load_time_zone() failed and we're using UTC.
230 return tz;
231 }
232
233 } // namespace cctz
234 } // namespace time_internal
235 ABSL_NAMESPACE_END
236 } // namespace absl
237