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/fdio/directory.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     CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, encoding) + 1;
144     buffer.resize(static_cast<size_t>(max_size));
145     if (CFStringGetCString(tz_name, &buffer[0], max_size, encoding)) {
146       zone = &buffer[0];
147     }
148   }
149   CFRelease(tz_default);
150 #endif
151 #if defined(__Fuchsia__)
152   std::string primary_tz;
153   [&]() {
154     // Note: We can't use the synchronous FIDL API here because it doesn't
155     // allow timeouts; if the FIDL call failed, local_time_zone() would never
156     // return.
157 
158     const zx::duration kTimeout = zx::msec(500);
159 
160     // Don't attach to the thread because otherwise the thread's dispatcher
161     // would be set to null when the loop is destroyed, causing any other FIDL
162     // code running on the same thread to crash.
163     async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
164 
165     fuchsia::intl::PropertyProviderHandle handle;
166     zx_status_t status = fdio_service_connect_by_name(
167         fuchsia::intl::PropertyProvider::Name_,
168         handle.NewRequest().TakeChannel().release());
169     if (status != ZX_OK) {
170       return;
171     }
172 
173     fuchsia::intl::PropertyProviderPtr intl_provider;
174     status = intl_provider.Bind(std::move(handle), loop.dispatcher());
175     if (status != ZX_OK) {
176       return;
177     }
178 
179     intl_provider->GetProfile(
180         [&loop, &primary_tz](fuchsia::intl::Profile profile) {
181           if (!profile.time_zones().empty()) {
182             primary_tz = profile.time_zones()[0].id;
183           }
184           loop.Quit();
185         });
186     loop.Run(zx::deadline_after(kTimeout));
187   }();
188 
189   if (!primary_tz.empty()) {
190     zone = primary_tz.c_str();
191   }
192 #endif
193 
194   // Allow ${TZ} to override to default zone.
195   char* tz_env = nullptr;
196 #if defined(_MSC_VER)
197   _dupenv_s(&tz_env, nullptr, "TZ");
198 #else
199   tz_env = std::getenv("TZ");
200 #endif
201   if (tz_env) zone = tz_env;
202 
203   // We only support the "[:]<zone-name>" form.
204   if (*zone == ':') ++zone;
205 
206   // Map "localtime" to a system-specific name, but
207   // allow ${LOCALTIME} to override the default name.
208   char* localtime_env = nullptr;
209   if (strcmp(zone, "localtime") == 0) {
210 #if defined(_MSC_VER)
211     // System-specific default is just "localtime".
212     _dupenv_s(&localtime_env, nullptr, "LOCALTIME");
213 #else
214     zone = "/etc/localtime";  // System-specific default.
215     localtime_env = std::getenv("LOCALTIME");
216 #endif
217     if (localtime_env) zone = localtime_env;
218   }
219 
220   const std::string name = zone;
221 #if defined(_MSC_VER)
222   free(localtime_env);
223   free(tz_env);
224 #endif
225 
226   time_zone tz;
227   load_time_zone(name, &tz);  // Falls back to UTC.
228   // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
229   // arrange for %z to generate "-0000" when we don't know the local
230   // offset because the load_time_zone() failed and we're using UTC.
231   return tz;
232 }
233 
234 }  // namespace cctz
235 }  // namespace time_internal
236 ABSL_NAMESPACE_END
237 }  // namespace absl
238