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