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