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
36namespace absl {
37namespace time_internal {
38namespace cctz {
39
40#if defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21
41namespace {
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
46using property_get_func = int (*)(const char*, char*);
47
48property_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
61int __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
69std::string time_zone::name() const {
70 return effective_impl().Name();
71}
72
73time_zone::absolute_lookup time_zone::lookup(
74 const time_point<seconds>& tp) const {
75 return effective_impl().BreakTime(tp);
76}
77
78time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const {
79 return effective_impl().MakeTime(cs);
80}
81
82bool time_zone::next_transition(const time_point<seconds>& tp,
83 civil_transition* trans) const {
84 return effective_impl().NextTransition(tp, trans);
85}
86
87bool time_zone::prev_transition(const time_point<seconds>& tp,
88 civil_transition* trans) const {
89 return effective_impl().PrevTransition(tp, trans);
90}
91
92std::string time_zone::version() const {
93 return effective_impl().Version();
94}
95
96std::string time_zone::description() const {
97 return effective_impl().Description();
98}
99
100const 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
109bool load_time_zone(const std::string& name, time_zone* tz) {
110 return time_zone::Impl::LoadTimeZone(name, tz);
111}
112
113time_zone utc_time_zone() {
114 return time_zone::Impl::UTC(); // avoid name lookup
115}
116
117time_zone fixed_time_zone(const seconds& offset) {
118 time_zone tz;
119 load_time_zone(FixedOffsetToName(offset), &tz);
120 return tz;
121}
122
123time_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