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 #if defined(__ANDROID_API__) && __ANDROID_API__ >= 21
20 #include <dlfcn.h>
21 #endif
22 #endif
23 
24 #if defined(__APPLE__)
25 #include <CoreFoundation/CFTimeZone.h>
26 #include <vector>
27 #endif
28 
29 #include <cstdlib>
30 #include <cstring>
31 #include <string>
32 
33 #include "time_zone_fixed.h"
34 #include "time_zone_impl.h"
35 
36 namespace cctz {
37 
38 #if defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21
39 namespace {
40 // Android 'L' removes __system_property_get() from the NDK, however
41 // it is still a hidden symbol in libc so we use dlsym() to access it.
42 // See Chromium's base/sys_info_android.cc for a similar example.
43 
44 using property_get_func = int (*)(const char*, char*);
45 
LoadSystemPropertyGet()46 property_get_func LoadSystemPropertyGet() {
47   int flag = RTLD_LAZY | RTLD_GLOBAL;
48 #if defined(RTLD_NOLOAD)
49   flag |= RTLD_NOLOAD;  // libc.so should already be resident
50 #endif
51   if (void* handle = dlopen("libc.so", flag)) {
52     void* sym = dlsym(handle, "__system_property_get");
53     dlclose(handle);
54     return reinterpret_cast<property_get_func>(sym);
55   }
56   return nullptr;
57 }
58 
__system_property_get(const char * name,char * value)59 int __system_property_get(const char* name, char* value) {
60   static property_get_func system_property_get = LoadSystemPropertyGet();
61   return system_property_get ? system_property_get(name, value) : -1;
62 }
63 
64 }  // namespace
65 #endif
66 
name() const67 std::string time_zone::name() const {
68   return effective_impl().Name();
69 }
70 
lookup(const time_point<seconds> & tp) const71 time_zone::absolute_lookup time_zone::lookup(
72     const time_point<seconds>& tp) const {
73   return effective_impl().BreakTime(tp);
74 }
75 
lookup(const civil_second & cs) const76 time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const {
77   return effective_impl().MakeTime(cs);
78 }
79 
next_transition(const time_point<seconds> & tp,civil_transition * trans) const80 bool time_zone::next_transition(const time_point<seconds>& tp,
81                                 civil_transition* trans) const {
82   return effective_impl().NextTransition(tp, trans);
83 }
84 
prev_transition(const time_point<seconds> & tp,civil_transition * trans) const85 bool time_zone::prev_transition(const time_point<seconds>& tp,
86                                 civil_transition* trans) const {
87   return effective_impl().PrevTransition(tp, trans);
88 }
89 
version() const90 std::string time_zone::version() const {
91   return effective_impl().Version();
92 }
93 
description() const94 std::string time_zone::description() const {
95   return effective_impl().Description();
96 }
97 
effective_impl() const98 const time_zone::Impl& time_zone::effective_impl() const {
99   if (impl_ == nullptr) {
100     // Dereferencing an implicit-UTC time_zone is expected to be
101     // rare, so we don't mind paying a small synchronization cost.
102     return *time_zone::Impl::UTC().impl_;
103   }
104   return *impl_;
105 }
106 
load_time_zone(const std::string & name,time_zone * tz)107 bool load_time_zone(const std::string& name, time_zone* tz) {
108   return time_zone::Impl::LoadTimeZone(name, tz);
109 }
110 
utc_time_zone()111 time_zone utc_time_zone() {
112   return time_zone::Impl::UTC();  // avoid name lookup
113 }
114 
fixed_time_zone(const seconds & offset)115 time_zone fixed_time_zone(const seconds& offset) {
116   time_zone tz;
117   load_time_zone(FixedOffsetToName(offset), &tz);
118   return tz;
119 }
120 
local_time_zone()121 time_zone local_time_zone() {
122   const char* zone = ":localtime";
123 #if defined(__ANDROID__)
124   char sysprop[PROP_VALUE_MAX];
125   if (__system_property_get("persist.sys.timezone", sysprop) > 0) {
126     zone = sysprop;
127   }
128 #endif
129 #if defined(__APPLE__)
130   std::vector<char> buffer;
131   CFTimeZoneRef tz_default = CFTimeZoneCopyDefault();
132   if (CFStringRef tz_name = CFTimeZoneGetName(tz_default)) {
133     CFStringEncoding encoding = kCFStringEncodingUTF8;
134     CFIndex length = CFStringGetLength(tz_name);
135     buffer.resize(CFStringGetMaximumSizeForEncoding(length, encoding) + 1);
136     if (CFStringGetCString(tz_name, &buffer[0], buffer.size(), encoding)) {
137       zone = &buffer[0];
138     }
139   }
140   CFRelease(tz_default);
141 #endif
142 
143   // Allow ${TZ} to override to default zone.
144   char* tz_env = nullptr;
145 #if defined(_MSC_VER)
146   _dupenv_s(&tz_env, nullptr, "TZ");
147 #else
148   tz_env = std::getenv("TZ");
149 #endif
150   if (tz_env) zone = tz_env;
151 
152   // We only support the "[:]<zone-name>" form.
153   if (*zone == ':') ++zone;
154 
155   // Map "localtime" to a system-specific name, but
156   // allow ${LOCALTIME} to override the default name.
157   char* localtime_env = nullptr;
158   if (strcmp(zone, "localtime") == 0) {
159 #if defined(_MSC_VER)
160     // System-specific default is just "localtime".
161     _dupenv_s(&localtime_env, nullptr, "LOCALTIME");
162 #else
163     zone = "/etc/localtime";  // System-specific default.
164     localtime_env = std::getenv("LOCALTIME");
165 #endif
166     if (localtime_env) zone = localtime_env;
167   }
168 
169   const std::string name = zone;
170 #if defined(_MSC_VER)
171   free(localtime_env);
172   free(tz_env);
173 #endif
174 
175   time_zone tz;
176   load_time_zone(name, &tz);  // Falls back to UTC.
177   // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
178   // arrange for %z to generate "-0000" when we don't know the local
179   // offset because the load_time_zone() failed and we're using UTC.
180   return tz;
181 }
182 
183 }  // namespace cctz
184