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