• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #if defined(_WIN32)
39 #include <sdkddkver.h>
40 // Include only when the SDK is for Windows 10 (and later), and the binary is
41 // targeted for Windows XP and later.
42 // Note: The Windows SDK added windows.globalization.h file for Windows 10, but
43 // MinGW did not add it until NTDDI_WIN10_NI (SDK version 10.0.22621.0).
44 #if ((defined(_WIN32_WINNT_WIN10) && !defined(__MINGW32__)) ||        \
45      (defined(NTDDI_WIN10_NI) && NTDDI_VERSION >= NTDDI_WIN10_NI)) && \
46     (_WIN32_WINNT >= _WIN32_WINNT_WINXP)
47 #define USE_WIN32_LOCAL_TIME_ZONE
48 #include <roapi.h>
49 #include <tchar.h>
50 #include <wchar.h>
51 #include <windows.globalization.h>
52 #include <windows.h>
53 #endif
54 #endif
55 
56 #include <cstdlib>
57 #include <cstring>
58 #include <string>
59 
60 #include "time_zone_fixed.h"
61 #include "time_zone_impl.h"
62 
63 namespace absl {
64 ABSL_NAMESPACE_BEGIN
65 namespace time_internal {
66 namespace cctz {
67 
68 namespace {
69 #if defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21
70 // Android 'L' removes __system_property_get() from the NDK, however
71 // it is still a hidden symbol in libc so we use dlsym() to access it.
72 // See Chromium's base/sys_info_android.cc for a similar example.
73 
74 using property_get_func = int (*)(const char*, char*);
75 
LoadSystemPropertyGet()76 property_get_func LoadSystemPropertyGet() {
77   int flag = RTLD_LAZY | RTLD_GLOBAL;
78 #if defined(RTLD_NOLOAD)
79   flag |= RTLD_NOLOAD;  // libc.so should already be resident
80 #endif
81   if (void* handle = dlopen("libc.so", flag)) {
82     void* sym = dlsym(handle, "__system_property_get");
83     dlclose(handle);
84     return reinterpret_cast<property_get_func>(sym);
85   }
86   return nullptr;
87 }
88 
__system_property_get(const char * name,char * value)89 int __system_property_get(const char* name, char* value) {
90   static property_get_func system_property_get = LoadSystemPropertyGet();
91   return system_property_get ? system_property_get(name, value) : -1;
92 }
93 #endif
94 
95 #if defined(USE_WIN32_LOCAL_TIME_ZONE)
96 // Calls the WinRT Calendar.GetTimeZone method to obtain the IANA ID of the
97 // local time zone. Returns an empty vector in case of an error.
win32_local_time_zone(const HMODULE combase)98 std::string win32_local_time_zone(const HMODULE combase) {
99   std::string result;
100   const auto ro_activate_instance =
101       reinterpret_cast<decltype(&RoActivateInstance)>(
102           GetProcAddress(combase, "RoActivateInstance"));
103   if (!ro_activate_instance) {
104     return result;
105   }
106   const auto windows_create_string_reference =
107       reinterpret_cast<decltype(&WindowsCreateStringReference)>(
108           GetProcAddress(combase, "WindowsCreateStringReference"));
109   if (!windows_create_string_reference) {
110     return result;
111   }
112   const auto windows_delete_string =
113       reinterpret_cast<decltype(&WindowsDeleteString)>(
114           GetProcAddress(combase, "WindowsDeleteString"));
115   if (!windows_delete_string) {
116     return result;
117   }
118   const auto windows_get_string_raw_buffer =
119       reinterpret_cast<decltype(&WindowsGetStringRawBuffer)>(
120           GetProcAddress(combase, "WindowsGetStringRawBuffer"));
121   if (!windows_get_string_raw_buffer) {
122     return result;
123   }
124 
125   // The string returned by WindowsCreateStringReference doesn't need to be
126   // deleted.
127   HSTRING calendar_class_id;
128   HSTRING_HEADER calendar_class_id_header;
129   HRESULT hr = windows_create_string_reference(
130       RuntimeClass_Windows_Globalization_Calendar,
131       sizeof(RuntimeClass_Windows_Globalization_Calendar) / sizeof(wchar_t) - 1,
132       &calendar_class_id_header, &calendar_class_id);
133   if (FAILED(hr)) {
134     return result;
135   }
136 
137   IInspectable* calendar;
138   hr = ro_activate_instance(calendar_class_id, &calendar);
139   if (FAILED(hr)) {
140     return result;
141   }
142 
143   ABI::Windows::Globalization::ITimeZoneOnCalendar* time_zone;
144   hr = calendar->QueryInterface(IID_PPV_ARGS(&time_zone));
145   if (FAILED(hr)) {
146     calendar->Release();
147     return result;
148   }
149 
150   HSTRING tz_hstr;
151   hr = time_zone->GetTimeZone(&tz_hstr);
152   if (SUCCEEDED(hr)) {
153     UINT32 wlen;
154     const PCWSTR tz_wstr = windows_get_string_raw_buffer(tz_hstr, &wlen);
155     if (tz_wstr) {
156       const int size =
157           WideCharToMultiByte(CP_UTF8, 0, tz_wstr, static_cast<int>(wlen),
158                               nullptr, 0, nullptr, nullptr);
159       result.resize(static_cast<size_t>(size));
160       WideCharToMultiByte(CP_UTF8, 0, tz_wstr, static_cast<int>(wlen),
161                           &result[0], size, nullptr, nullptr);
162     }
163     windows_delete_string(tz_hstr);
164   }
165   time_zone->Release();
166   calendar->Release();
167   return result;
168 }
169 #endif
170 }  // namespace
171 
name() const172 std::string time_zone::name() const { return effective_impl().Name(); }
173 
lookup(const time_point<seconds> & tp) const174 time_zone::absolute_lookup time_zone::lookup(
175     const time_point<seconds>& tp) const {
176   return effective_impl().BreakTime(tp);
177 }
178 
lookup(const civil_second & cs) const179 time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const {
180   return effective_impl().MakeTime(cs);
181 }
182 
next_transition(const time_point<seconds> & tp,civil_transition * trans) const183 bool time_zone::next_transition(const time_point<seconds>& tp,
184                                 civil_transition* trans) const {
185   return effective_impl().NextTransition(tp, trans);
186 }
187 
prev_transition(const time_point<seconds> & tp,civil_transition * trans) const188 bool time_zone::prev_transition(const time_point<seconds>& tp,
189                                 civil_transition* trans) const {
190   return effective_impl().PrevTransition(tp, trans);
191 }
192 
version() const193 std::string time_zone::version() const { return effective_impl().Version(); }
194 
description() const195 std::string time_zone::description() const {
196   return effective_impl().Description();
197 }
198 
effective_impl() const199 const time_zone::Impl& time_zone::effective_impl() const {
200   if (impl_ == nullptr) {
201     // Dereferencing an implicit-UTC time_zone is expected to be
202     // rare, so we don't mind paying a small synchronization cost.
203     return *time_zone::Impl::UTC().impl_;
204   }
205   return *impl_;
206 }
207 
load_time_zone(const std::string & name,time_zone * tz)208 bool load_time_zone(const std::string& name, time_zone* tz) {
209   return time_zone::Impl::LoadTimeZone(name, tz);
210 }
211 
utc_time_zone()212 time_zone utc_time_zone() {
213   return time_zone::Impl::UTC();  // avoid name lookup
214 }
215 
fixed_time_zone(const seconds & offset)216 time_zone fixed_time_zone(const seconds& offset) {
217   time_zone tz;
218   load_time_zone(FixedOffsetToName(offset), &tz);
219   return tz;
220 }
221 
local_time_zone()222 time_zone local_time_zone() {
223   const char* zone = ":localtime";
224 #if defined(__ANDROID__)
225   char sysprop[PROP_VALUE_MAX];
226   if (__system_property_get("persist.sys.timezone", sysprop) > 0) {
227     zone = sysprop;
228   }
229 #endif
230 #if defined(__APPLE__)
231   std::vector<char> buffer;
232   CFTimeZoneRef tz_default = CFTimeZoneCopyDefault();
233   if (CFStringRef tz_name = CFTimeZoneGetName(tz_default)) {
234     CFStringEncoding encoding = kCFStringEncodingUTF8;
235     CFIndex length = CFStringGetLength(tz_name);
236     CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, encoding) + 1;
237     buffer.resize(static_cast<size_t>(max_size));
238     if (CFStringGetCString(tz_name, &buffer[0], max_size, encoding)) {
239       zone = &buffer[0];
240     }
241   }
242   CFRelease(tz_default);
243 #endif
244 #if defined(__Fuchsia__)
245   std::string primary_tz;
246   [&]() {
247     // Note: We can't use the synchronous FIDL API here because it doesn't
248     // allow timeouts; if the FIDL call failed, local_time_zone() would never
249     // return.
250 
251     const zx::duration kTimeout = zx::msec(500);
252 
253     // Don't attach to the thread because otherwise the thread's dispatcher
254     // would be set to null when the loop is destroyed, causing any other FIDL
255     // code running on the same thread to crash.
256     async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
257 
258     fuchsia::intl::PropertyProviderHandle handle;
259     zx_status_t status = fdio_service_connect_by_name(
260         fuchsia::intl::PropertyProvider::Name_,
261         handle.NewRequest().TakeChannel().release());
262     if (status != ZX_OK) {
263       return;
264     }
265 
266     fuchsia::intl::PropertyProviderPtr intl_provider;
267     status = intl_provider.Bind(std::move(handle), loop.dispatcher());
268     if (status != ZX_OK) {
269       return;
270     }
271 
272     intl_provider->GetProfile(
273         [&loop, &primary_tz](fuchsia::intl::Profile profile) {
274           if (!profile.time_zones().empty()) {
275             primary_tz = profile.time_zones()[0].id;
276           }
277           loop.Quit();
278         });
279     loop.Run(zx::deadline_after(kTimeout));
280   }();
281 
282   if (!primary_tz.empty()) {
283     zone = primary_tz.c_str();
284   }
285 #endif
286 #if defined(USE_WIN32_LOCAL_TIME_ZONE)
287   // Use the WinRT Calendar class to get the local time zone. This feature is
288   // available on Windows 10 and later. The library is dynamically linked to
289   // maintain binary compatibility with Windows XP - Windows 7. On Windows 8,
290   // The combase.dll API functions are available but the RoActivateInstance
291   // call will fail for the Calendar class.
292   std::string winrt_tz;
293   const HMODULE combase =
294       LoadLibraryEx(_T("combase.dll"), nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
295   if (combase) {
296     const auto ro_initialize = reinterpret_cast<decltype(&::RoInitialize)>(
297         GetProcAddress(combase, "RoInitialize"));
298     const auto ro_uninitialize = reinterpret_cast<decltype(&::RoUninitialize)>(
299         GetProcAddress(combase, "RoUninitialize"));
300     if (ro_initialize && ro_uninitialize) {
301       const HRESULT hr = ro_initialize(RO_INIT_MULTITHREADED);
302       // RPC_E_CHANGED_MODE means that a previous RoInitialize call specified
303       // a different concurrency model. The WinRT runtime is initialized and
304       // should work for our purpose here, but we should *not* call
305       // RoUninitialize because it's a failure.
306       if (SUCCEEDED(hr) || hr == RPC_E_CHANGED_MODE) {
307         winrt_tz = win32_local_time_zone(combase);
308         if (SUCCEEDED(hr)) {
309           ro_uninitialize();
310         }
311       }
312     }
313     FreeLibrary(combase);
314   }
315   if (!winrt_tz.empty()) {
316     zone = winrt_tz.c_str();
317   }
318 #endif
319 
320   // Allow ${TZ} to override to default zone.
321   char* tz_env = nullptr;
322 #if defined(_MSC_VER)
323   _dupenv_s(&tz_env, nullptr, "TZ");
324 #else
325   tz_env = std::getenv("TZ");
326 #endif
327   if (tz_env) zone = tz_env;
328 
329   // We only support the "[:]<zone-name>" form.
330   if (*zone == ':') ++zone;
331 
332   // Map "localtime" to a system-specific name, but
333   // allow ${LOCALTIME} to override the default name.
334   char* localtime_env = nullptr;
335   if (strcmp(zone, "localtime") == 0) {
336 #if defined(_MSC_VER)
337     // System-specific default is just "localtime".
338     _dupenv_s(&localtime_env, nullptr, "LOCALTIME");
339 #else
340     zone = "/etc/localtime";  // System-specific default.
341     localtime_env = std::getenv("LOCALTIME");
342 #endif
343     if (localtime_env) zone = localtime_env;
344   }
345 
346   const std::string name = zone;
347 #if defined(_MSC_VER)
348   free(localtime_env);
349   free(tz_env);
350 #endif
351 
352   time_zone tz;
353   load_time_zone(name, &tz);  // Falls back to UTC.
354   // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
355   // arrange for %z to generate "-0000" when we don't know the local
356   // offset because the load_time_zone() failed and we're using UTC.
357   return tz;
358 }
359 
360 }  // namespace cctz
361 }  // namespace time_internal
362 ABSL_NAMESPACE_END
363 }  // namespace absl
364