|
ferencd@0
|
1 // The MIT License (MIT)
|
|
ferencd@0
|
2 //
|
|
ferencd@0
|
3 // Copyright (c) 2015, 2016, 2017 Howard Hinnant
|
|
ferencd@0
|
4 // Copyright (c) 2015 Ville Voutilainen
|
|
ferencd@0
|
5 // Copyright (c) 2016 Alexander Kormanovsky
|
|
ferencd@0
|
6 // Copyright (c) 2016, 2017 Jiangang Zhuang
|
|
ferencd@0
|
7 // Copyright (c) 2017 Nicolas Veloz Savino
|
|
ferencd@0
|
8 // Copyright (c) 2017 Florian Dang
|
|
ferencd@0
|
9 // Copyright (c) 2017 Aaron Bishop
|
|
ferencd@0
|
10 //
|
|
ferencd@0
|
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
ferencd@0
|
12 // of this software and associated documentation files (the "Software"), to deal
|
|
ferencd@0
|
13 // in the Software without restriction, including without limitation the rights
|
|
ferencd@0
|
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
ferencd@0
|
15 // copies of the Software, and to permit persons to whom the Software is
|
|
ferencd@0
|
16 // furnished to do so, subject to the following conditions:
|
|
ferencd@0
|
17 //
|
|
ferencd@0
|
18 // The above copyright notice and this permission notice shall be included in all
|
|
ferencd@0
|
19 // copies or substantial portions of the Software.
|
|
ferencd@0
|
20 //
|
|
ferencd@0
|
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
ferencd@0
|
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
ferencd@0
|
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
ferencd@0
|
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
ferencd@0
|
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
ferencd@0
|
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
ferencd@0
|
27 // SOFTWARE.
|
|
ferencd@0
|
28 //
|
|
ferencd@0
|
29 // Our apologies. When the previous paragraph was written, lowercase had not yet
|
|
ferencd@0
|
30 // been invented (that would involve another several millennia of evolution).
|
|
ferencd@0
|
31 // We did not mean to shout.
|
|
ferencd@0
|
32
|
|
ferencd@0
|
33 #ifdef _WIN32
|
|
ferencd@0
|
34 // windows.h will be included directly and indirectly (e.g. by curl).
|
|
ferencd@0
|
35 // We need to define these macros to prevent windows.h bringing in
|
|
ferencd@0
|
36 // more than we need and do it early so windows.h doesn't get included
|
|
ferencd@0
|
37 // without these macros having been defined.
|
|
ferencd@0
|
38 // min/max macros interfere with the C++ versions.
|
|
ferencd@0
|
39 # ifndef NOMINMAX
|
|
ferencd@0
|
40 # define NOMINMAX
|
|
ferencd@0
|
41 # endif
|
|
ferencd@0
|
42 // We don't need all that Windows has to offer.
|
|
ferencd@0
|
43 # ifndef WIN32_LEAN_AND_MEAN
|
|
ferencd@0
|
44 # define WIN32_LEAN_AND_MEAN
|
|
ferencd@0
|
45 # endif
|
|
ferencd@0
|
46
|
|
ferencd@0
|
47 // for wcstombs
|
|
ferencd@0
|
48 # ifndef _CRT_SECURE_NO_WARNINGS
|
|
ferencd@0
|
49 # define _CRT_SECURE_NO_WARNINGS
|
|
ferencd@0
|
50 # endif
|
|
ferencd@0
|
51
|
|
ferencd@0
|
52 // None of this happens with the MS SDK (at least VS14 which I tested), but:
|
|
ferencd@0
|
53 // Compiling with mingw, we get "error: 'KF_FLAG_DEFAULT' was not declared in this scope."
|
|
ferencd@0
|
54 // and error: 'SHGetKnownFolderPath' was not declared in this scope.".
|
|
ferencd@0
|
55 // It seems when using mingw NTDDI_VERSION is undefined and that
|
|
ferencd@0
|
56 // causes KNOWN_FOLDER_FLAG and the KF_ flags to not get defined.
|
|
ferencd@0
|
57 // So we must define NTDDI_VERSION to get those flags on mingw.
|
|
ferencd@0
|
58 // The docs say though here:
|
|
ferencd@0
|
59 // https://msdn.microsoft.com/en-nz/library/windows/desktop/aa383745(v=vs.85).aspx
|
|
ferencd@0
|
60 // that "If you define NTDDI_VERSION, you must also define _WIN32_WINNT."
|
|
ferencd@0
|
61 // So we declare we require Vista or greater.
|
|
ferencd@0
|
62 # ifdef __MINGW32__
|
|
ferencd@0
|
63
|
|
ferencd@0
|
64 # ifndef NTDDI_VERSION
|
|
ferencd@0
|
65 # define NTDDI_VERSION 0x06000000
|
|
ferencd@0
|
66 # define _WIN32_WINNT _WIN32_WINNT_VISTA
|
|
ferencd@0
|
67 # elif NTDDI_VERSION < 0x06000000
|
|
ferencd@0
|
68 # warning "If this fails to compile NTDDI_VERSION may be to low. See comments above."
|
|
ferencd@0
|
69 # endif
|
|
ferencd@0
|
70 // But once we define the values above we then get this linker error:
|
|
ferencd@0
|
71 // "tz.cpp:(.rdata$.refptr.FOLDERID_Downloads[.refptr.FOLDERID_Downloads]+0x0): "
|
|
ferencd@0
|
72 // "undefined reference to `FOLDERID_Downloads'"
|
|
ferencd@0
|
73 // which #include <initguid.h> cures see:
|
|
ferencd@0
|
74 // https://support.microsoft.com/en-us/kb/130869
|
|
ferencd@0
|
75 # include <initguid.h>
|
|
ferencd@0
|
76 // But with <initguid.h> included, the error moves on to:
|
|
ferencd@0
|
77 // error: 'FOLDERID_Downloads' was not declared in this scope
|
|
ferencd@0
|
78 // Which #include <knownfolders.h> cures.
|
|
ferencd@0
|
79 # include <knownfolders.h>
|
|
ferencd@0
|
80
|
|
ferencd@0
|
81 # endif // __MINGW32__
|
|
ferencd@0
|
82
|
|
ferencd@0
|
83 # include <windows.h>
|
|
ferencd@0
|
84 #endif // _WIN32
|
|
ferencd@0
|
85
|
|
ferencd@0
|
86 #include "date/tz_private.h"
|
|
ferencd@0
|
87
|
|
ferencd@0
|
88 #ifdef __APPLE__
|
|
ferencd@0
|
89 # include "date/ios.h"
|
|
ferencd@0
|
90 #else
|
|
ferencd@0
|
91 # define TARGET_OS_IPHONE 0
|
|
ferencd@0
|
92 #endif
|
|
ferencd@0
|
93
|
|
ferencd@0
|
94 #if USE_OS_TZDB
|
|
ferencd@0
|
95 # include <dirent.h>
|
|
ferencd@0
|
96 #endif
|
|
ferencd@0
|
97 #include <algorithm>
|
|
ferencd@0
|
98 #include <cctype>
|
|
ferencd@0
|
99 #include <cstdlib>
|
|
ferencd@0
|
100 #include <cstring>
|
|
ferencd@0
|
101 #include <cwchar>
|
|
ferencd@0
|
102 #include <exception>
|
|
ferencd@0
|
103 #include <fstream>
|
|
ferencd@0
|
104 #include <iostream>
|
|
ferencd@0
|
105 #include <iterator>
|
|
ferencd@0
|
106 #include <memory>
|
|
ferencd@0
|
107 #if USE_OS_TZDB
|
|
ferencd@0
|
108 # include <queue>
|
|
ferencd@0
|
109 #endif
|
|
ferencd@0
|
110 #include <sstream>
|
|
ferencd@0
|
111 #include <string>
|
|
ferencd@0
|
112 #include <tuple>
|
|
ferencd@0
|
113 #include <vector>
|
|
ferencd@0
|
114 #include <sys/stat.h>
|
|
ferencd@0
|
115
|
|
ferencd@0
|
116 // unistd.h is used on some platforms as part of the the means to get
|
|
ferencd@0
|
117 // the current time zone. On Win32 windows.h provides a means to do it.
|
|
ferencd@0
|
118 // gcc/mingw supports unistd.h on Win32 but MSVC does not.
|
|
ferencd@0
|
119
|
|
ferencd@0
|
120 #ifdef _WIN32
|
|
ferencd@0
|
121 # ifdef WINAPI_FAMILY
|
|
ferencd@0
|
122 # include <winapifamily.h>
|
|
ferencd@0
|
123 # if WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP
|
|
ferencd@0
|
124 # define WINRT
|
|
ferencd@0
|
125 # define INSTALL .
|
|
ferencd@0
|
126 # endif
|
|
ferencd@0
|
127 # endif
|
|
ferencd@0
|
128
|
|
ferencd@0
|
129 # include <io.h> // _unlink etc.
|
|
ferencd@0
|
130
|
|
ferencd@0
|
131 # if defined(__clang__)
|
|
ferencd@0
|
132 struct IUnknown; // fix for issue with static_cast<> in objbase.h
|
|
ferencd@0
|
133 // (see https://github.com/philsquared/Catch/issues/690)
|
|
ferencd@0
|
134 # endif
|
|
ferencd@0
|
135
|
|
ferencd@0
|
136 # include <shlobj.h> // CoTaskFree, ShGetKnownFolderPath etc.
|
|
ferencd@0
|
137 # if HAS_REMOTE_API
|
|
ferencd@0
|
138 # include <direct.h> // _mkdir
|
|
ferencd@0
|
139 # include <shellapi.h> // ShFileOperation etc.
|
|
ferencd@0
|
140 # endif // HAS_REMOTE_API
|
|
ferencd@0
|
141 #else // !_WIN32
|
|
ferencd@0
|
142 # include <unistd.h>
|
|
ferencd@0
|
143 # if !USE_OS_TZDB
|
|
ferencd@0
|
144 # include <wordexp.h>
|
|
ferencd@0
|
145 # endif
|
|
ferencd@0
|
146 # include <limits.h>
|
|
ferencd@0
|
147 # include <string.h>
|
|
ferencd@0
|
148 # if !USE_SHELL_API
|
|
ferencd@0
|
149 # include <sys/stat.h>
|
|
ferencd@0
|
150 # include <sys/fcntl.h>
|
|
ferencd@0
|
151 # include <dirent.h>
|
|
ferencd@0
|
152 # include <cstring>
|
|
ferencd@0
|
153 # include <sys/wait.h>
|
|
ferencd@0
|
154 # include <sys/types.h>
|
|
ferencd@0
|
155 # endif //!USE_SHELL_API
|
|
ferencd@0
|
156 #endif // !_WIN32
|
|
ferencd@0
|
157
|
|
ferencd@0
|
158
|
|
ferencd@0
|
159 #if HAS_REMOTE_API
|
|
ferencd@0
|
160 // Note curl includes windows.h so we must include curl AFTER definitions of things
|
|
ferencd@0
|
161 // that affect windows.h such as NOMINMAX.
|
|
ferencd@0
|
162 #if defined(_MSC_VER) && defined(SHORTENED_CURL_INCLUDE)
|
|
ferencd@0
|
163 // For rmt_curl nuget package
|
|
ferencd@0
|
164 # include <curl.h>
|
|
ferencd@0
|
165 #else
|
|
ferencd@0
|
166 # include <curl/curl.h>
|
|
ferencd@0
|
167 #endif
|
|
ferencd@0
|
168 #endif
|
|
ferencd@0
|
169
|
|
ferencd@0
|
170 #ifdef _WIN32
|
|
ferencd@0
|
171 static CONSTDATA char folder_delimiter = '\\';
|
|
ferencd@0
|
172 #else // !_WIN32
|
|
ferencd@0
|
173 static CONSTDATA char folder_delimiter = '/';
|
|
ferencd@0
|
174 #endif // !_WIN32
|
|
ferencd@0
|
175
|
|
ferencd@0
|
176 #if defined(__GNUC__) && __GNUC__ < 5
|
|
ferencd@0
|
177 // GCC 4.9 Bug 61489 Wrong warning with -Wmissing-field-initializers
|
|
ferencd@0
|
178 # pragma GCC diagnostic push
|
|
ferencd@0
|
179 # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
|
ferencd@0
|
180 #endif // defined(__GNUC__) && __GNUC__ < 5
|
|
ferencd@0
|
181
|
|
ferencd@0
|
182 #if !USE_OS_TZDB
|
|
ferencd@0
|
183
|
|
ferencd@0
|
184 # ifdef _WIN32
|
|
ferencd@0
|
185 # ifndef WINRT
|
|
ferencd@0
|
186
|
|
ferencd@0
|
187 namespace
|
|
ferencd@0
|
188 {
|
|
ferencd@0
|
189 struct task_mem_deleter
|
|
ferencd@0
|
190 {
|
|
ferencd@0
|
191 void operator()(wchar_t buf[])
|
|
ferencd@0
|
192 {
|
|
ferencd@0
|
193 if (buf != nullptr)
|
|
ferencd@0
|
194 CoTaskMemFree(buf);
|
|
ferencd@0
|
195 }
|
|
ferencd@0
|
196 };
|
|
ferencd@0
|
197 using co_task_mem_ptr = std::unique_ptr<wchar_t[], task_mem_deleter>;
|
|
ferencd@0
|
198 }
|
|
ferencd@0
|
199
|
|
ferencd@0
|
200 // We might need to know certain locations even if not using the remote API,
|
|
ferencd@0
|
201 // so keep these routines out of that block for now.
|
|
ferencd@0
|
202 static
|
|
ferencd@0
|
203 std::string
|
|
ferencd@0
|
204 get_known_folder(const GUID& folderid)
|
|
ferencd@0
|
205 {
|
|
ferencd@0
|
206 std::string folder;
|
|
ferencd@0
|
207 PWSTR pfolder = nullptr;
|
|
ferencd@0
|
208 HRESULT hr = SHGetKnownFolderPath(folderid, KF_FLAG_DEFAULT, nullptr, &pfolder);
|
|
ferencd@0
|
209 if (SUCCEEDED(hr))
|
|
ferencd@0
|
210 {
|
|
ferencd@0
|
211 co_task_mem_ptr folder_ptr(pfolder);
|
|
ferencd@0
|
212 const wchar_t* fptr = folder_ptr.get();
|
|
ferencd@0
|
213 auto state = std::mbstate_t();
|
|
ferencd@0
|
214 const auto required = std::wcsrtombs(nullptr, &fptr, 0, &state);
|
|
ferencd@0
|
215 if (required != 0 && required != std::size_t(-1))
|
|
ferencd@0
|
216 {
|
|
ferencd@0
|
217 folder.resize(required);
|
|
ferencd@0
|
218 std::wcsrtombs(&folder[0], &fptr, folder.size(), &state);
|
|
ferencd@0
|
219 }
|
|
ferencd@0
|
220 }
|
|
ferencd@0
|
221 return folder;
|
|
ferencd@0
|
222 }
|
|
ferencd@0
|
223
|
|
ferencd@0
|
224 # ifndef INSTALL
|
|
ferencd@0
|
225
|
|
ferencd@0
|
226 // Usually something like "c:\Users\username\Downloads".
|
|
ferencd@0
|
227 static
|
|
ferencd@0
|
228 std::string
|
|
ferencd@0
|
229 get_download_folder()
|
|
ferencd@0
|
230 {
|
|
ferencd@0
|
231 return get_known_folder(FOLDERID_Downloads);
|
|
ferencd@0
|
232 }
|
|
ferencd@0
|
233
|
|
ferencd@0
|
234 # endif // !INSTALL
|
|
ferencd@0
|
235
|
|
ferencd@0
|
236 # endif // WINRT
|
|
ferencd@0
|
237 # else // !_WIN32
|
|
ferencd@0
|
238
|
|
ferencd@0
|
239 # if !defined(INSTALL)
|
|
ferencd@0
|
240
|
|
ferencd@0
|
241 static
|
|
ferencd@0
|
242 std::string
|
|
ferencd@0
|
243 expand_path(std::string path)
|
|
ferencd@0
|
244 {
|
|
ferencd@0
|
245 # if TARGET_OS_IPHONE
|
|
ferencd@0
|
246 return date::iOSUtils::get_tzdata_path();
|
|
ferencd@0
|
247 # else // !TARGET_OS_IPHONE
|
|
ferencd@0
|
248 ::wordexp_t w{};
|
|
ferencd@0
|
249 std::unique_ptr<::wordexp_t, void(*)(::wordexp_t*)> hold{&w, ::wordfree};
|
|
ferencd@0
|
250 ::wordexp(path.c_str(), &w, 0);
|
|
ferencd@0
|
251 if (w.we_wordc != 1)
|
|
ferencd@0
|
252 throw std::runtime_error("Cannot expand path: " + path);
|
|
ferencd@0
|
253 path = w.we_wordv[0];
|
|
ferencd@0
|
254 return path;
|
|
ferencd@0
|
255 # endif // !TARGET_OS_IPHONE
|
|
ferencd@0
|
256 }
|
|
ferencd@0
|
257
|
|
ferencd@0
|
258 static
|
|
ferencd@0
|
259 std::string
|
|
ferencd@0
|
260 get_download_folder()
|
|
ferencd@0
|
261 {
|
|
ferencd@0
|
262 return expand_path("~/Downloads");
|
|
ferencd@0
|
263 }
|
|
ferencd@0
|
264
|
|
ferencd@0
|
265 # endif // !defined(INSTALL)
|
|
ferencd@0
|
266
|
|
ferencd@0
|
267 # endif // !_WIN32
|
|
ferencd@0
|
268
|
|
ferencd@0
|
269 #endif // !USE_OS_TZDB
|
|
ferencd@0
|
270
|
|
ferencd@0
|
271 namespace date
|
|
ferencd@0
|
272 {
|
|
ferencd@0
|
273 // +---------------------+
|
|
ferencd@0
|
274 // | Begin Configuration |
|
|
ferencd@0
|
275 // +---------------------+
|
|
ferencd@0
|
276
|
|
ferencd@0
|
277 using namespace detail;
|
|
ferencd@0
|
278
|
|
ferencd@0
|
279 #if !USE_OS_TZDB
|
|
ferencd@0
|
280
|
|
ferencd@0
|
281 static
|
|
ferencd@0
|
282 std::string&
|
|
ferencd@0
|
283 access_install()
|
|
ferencd@0
|
284 {
|
|
ferencd@0
|
285 static std::string install
|
|
ferencd@0
|
286 #ifndef INSTALL
|
|
ferencd@0
|
287
|
|
ferencd@0
|
288 = get_download_folder() + folder_delimiter + "tzdata";
|
|
ferencd@0
|
289
|
|
ferencd@0
|
290 #else // !INSTALL
|
|
ferencd@0
|
291
|
|
ferencd@0
|
292 # define STRINGIZEIMP(x) #x
|
|
ferencd@0
|
293 # define STRINGIZE(x) STRINGIZEIMP(x)
|
|
ferencd@0
|
294
|
|
ferencd@0
|
295 = STRINGIZE(INSTALL) + std::string(1, folder_delimiter) + "tzdata";
|
|
ferencd@0
|
296
|
|
ferencd@0
|
297 #undef STRINGIZEIMP
|
|
ferencd@0
|
298 #undef STRINGIZE
|
|
ferencd@0
|
299 #endif // !INSTALL
|
|
ferencd@0
|
300
|
|
ferencd@0
|
301 return install;
|
|
ferencd@0
|
302 }
|
|
ferencd@0
|
303
|
|
ferencd@0
|
304 void
|
|
ferencd@0
|
305 set_install(const std::string& s)
|
|
ferencd@0
|
306 {
|
|
ferencd@0
|
307 access_install() = s;
|
|
ferencd@0
|
308 }
|
|
ferencd@0
|
309
|
|
ferencd@0
|
310 static
|
|
ferencd@0
|
311 const std::string&
|
|
ferencd@0
|
312 get_install()
|
|
ferencd@0
|
313 {
|
|
ferencd@0
|
314 static const std::string& ref = access_install();
|
|
ferencd@0
|
315 return ref;
|
|
ferencd@0
|
316 }
|
|
ferencd@0
|
317
|
|
ferencd@0
|
318 #if HAS_REMOTE_API
|
|
ferencd@0
|
319 static
|
|
ferencd@0
|
320 std::string
|
|
ferencd@0
|
321 get_download_gz_file(const std::string& version)
|
|
ferencd@0
|
322 {
|
|
ferencd@0
|
323 auto file = get_install() + version + ".tar.gz";
|
|
ferencd@0
|
324 return file;
|
|
ferencd@0
|
325 }
|
|
ferencd@0
|
326 #endif // HAS_REMOTE_API
|
|
ferencd@0
|
327
|
|
ferencd@0
|
328 #endif // !USE_OS_TZDB
|
|
ferencd@0
|
329
|
|
ferencd@0
|
330 // These can be used to reduce the range of the database to save memory
|
|
ferencd@0
|
331 CONSTDATA auto min_year = date::year::min();
|
|
ferencd@0
|
332 CONSTDATA auto max_year = date::year::max();
|
|
ferencd@0
|
333
|
|
ferencd@0
|
334 CONSTDATA auto min_day = date::January/1;
|
|
ferencd@0
|
335 CONSTDATA auto max_day = date::December/31;
|
|
ferencd@0
|
336
|
|
ferencd@0
|
337 #if USE_OS_TZDB
|
|
ferencd@0
|
338
|
|
ferencd@0
|
339 CONSTCD14 const sys_seconds min_seconds = sys_days(min_year/min_day);
|
|
ferencd@0
|
340
|
|
ferencd@0
|
341 #endif // USE_OS_TZDB
|
|
ferencd@0
|
342
|
|
ferencd@0
|
343 #ifndef _WIN32
|
|
ferencd@0
|
344
|
|
ferencd@0
|
345 static
|
|
ferencd@0
|
346 std::string
|
|
ferencd@0
|
347 discover_tz_dir()
|
|
ferencd@0
|
348 {
|
|
ferencd@0
|
349 struct stat sb;
|
|
ferencd@0
|
350 using namespace std;
|
|
ferencd@0
|
351 # ifndef __APPLE__
|
|
ferencd@0
|
352 CONSTDATA auto tz_dir_default = "/usr/share/zoneinfo";
|
|
ferencd@0
|
353 CONSTDATA auto tz_dir_buildroot = "/usr/share/zoneinfo/uclibc";
|
|
ferencd@0
|
354
|
|
ferencd@0
|
355 // Check special path which is valid for buildroot with uclibc builds
|
|
ferencd@0
|
356 if(stat(tz_dir_buildroot, &sb) == 0 && S_ISDIR(sb.st_mode))
|
|
ferencd@0
|
357 return tz_dir_buildroot;
|
|
ferencd@0
|
358 else if(stat(tz_dir_default, &sb) == 0 && S_ISDIR(sb.st_mode))
|
|
ferencd@0
|
359 return tz_dir_default;
|
|
ferencd@0
|
360 else
|
|
ferencd@0
|
361 throw runtime_error("discover_tz_dir failed to find zoneinfo\n");
|
|
ferencd@0
|
362 # else // __APPLE__
|
|
ferencd@0
|
363 # if TARGET_OS_IPHONE
|
|
ferencd@0
|
364 return "/var/db/timezone/zoneinfo";
|
|
ferencd@0
|
365 # else
|
|
ferencd@0
|
366 CONSTDATA auto timezone = "/etc/localtime";
|
|
ferencd@0
|
367 if (!(lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0))
|
|
ferencd@0
|
368 throw runtime_error("discover_tz_dir failed\n");
|
|
ferencd@0
|
369 string result;
|
|
ferencd@0
|
370 char rp[PATH_MAX+1] = {};
|
|
ferencd@0
|
371 if (readlink(timezone, rp, sizeof(rp)-1) > 0)
|
|
ferencd@0
|
372 result = string(rp);
|
|
ferencd@0
|
373 else
|
|
ferencd@0
|
374 throw system_error(errno, system_category(), "readlink() failed");
|
|
ferencd@0
|
375 auto i = result.find("zoneinfo");
|
|
ferencd@0
|
376 if (i == string::npos)
|
|
ferencd@0
|
377 throw runtime_error("discover_tz_dir failed to find zoneinfo\n");
|
|
ferencd@0
|
378 i = result.find('/', i);
|
|
ferencd@0
|
379 if (i == string::npos)
|
|
ferencd@0
|
380 throw runtime_error("discover_tz_dir failed to find '/'\n");
|
|
ferencd@0
|
381 return result.substr(0, i);
|
|
ferencd@0
|
382 # endif
|
|
ferencd@0
|
383 # endif // __APPLE__
|
|
ferencd@0
|
384 }
|
|
ferencd@0
|
385
|
|
ferencd@0
|
386 static
|
|
ferencd@0
|
387 const std::string&
|
|
ferencd@0
|
388 get_tz_dir()
|
|
ferencd@0
|
389 {
|
|
ferencd@0
|
390 static const std::string tz_dir = discover_tz_dir();
|
|
ferencd@0
|
391 return tz_dir;
|
|
ferencd@0
|
392 }
|
|
ferencd@0
|
393
|
|
ferencd@0
|
394 #endif
|
|
ferencd@0
|
395
|
|
ferencd@0
|
396 // +-------------------+
|
|
ferencd@0
|
397 // | End Configuration |
|
|
ferencd@0
|
398 // +-------------------+
|
|
ferencd@0
|
399
|
|
ferencd@0
|
400 #ifndef _MSC_VER
|
|
ferencd@0
|
401 static_assert(min_year <= max_year, "Configuration error");
|
|
ferencd@0
|
402 #endif
|
|
ferencd@0
|
403
|
|
ferencd@0
|
404 static std::unique_ptr<tzdb> init_tzdb();
|
|
ferencd@0
|
405
|
|
ferencd@0
|
406 tzdb_list::~tzdb_list()
|
|
ferencd@0
|
407 {
|
|
ferencd@0
|
408 const tzdb* ptr = head_;
|
|
ferencd@0
|
409 head_ = nullptr;
|
|
ferencd@0
|
410 while (ptr != nullptr)
|
|
ferencd@0
|
411 {
|
|
ferencd@0
|
412 auto next = ptr->next;
|
|
ferencd@0
|
413 delete ptr;
|
|
ferencd@0
|
414 ptr = next;
|
|
ferencd@0
|
415 }
|
|
ferencd@0
|
416 }
|
|
ferencd@0
|
417
|
|
ferencd@0
|
418 tzdb_list::tzdb_list(tzdb_list&& x) noexcept
|
|
ferencd@0
|
419 : head_{x.head_.exchange(nullptr)}
|
|
ferencd@0
|
420 {
|
|
ferencd@0
|
421 }
|
|
ferencd@0
|
422
|
|
ferencd@0
|
423 void
|
|
ferencd@0
|
424 tzdb_list::push_front(tzdb* tzdb) noexcept
|
|
ferencd@0
|
425 {
|
|
ferencd@0
|
426 tzdb->next = head_;
|
|
ferencd@0
|
427 head_ = tzdb;
|
|
ferencd@0
|
428 }
|
|
ferencd@0
|
429
|
|
ferencd@0
|
430 tzdb_list::const_iterator
|
|
ferencd@0
|
431 tzdb_list::erase_after(const_iterator p) noexcept
|
|
ferencd@0
|
432 {
|
|
ferencd@0
|
433 auto t = p.p_->next;
|
|
ferencd@0
|
434 p.p_->next = p.p_->next->next;
|
|
ferencd@0
|
435 delete t;
|
|
ferencd@0
|
436 return ++p;
|
|
ferencd@0
|
437 }
|
|
ferencd@0
|
438
|
|
ferencd@0
|
439 struct tzdb_list::undocumented_helper
|
|
ferencd@0
|
440 {
|
|
ferencd@0
|
441 static void push_front(tzdb_list& db_list, tzdb* tzdb) noexcept
|
|
ferencd@0
|
442 {
|
|
ferencd@0
|
443 db_list.push_front(tzdb);
|
|
ferencd@0
|
444 }
|
|
ferencd@0
|
445 };
|
|
ferencd@0
|
446
|
|
ferencd@0
|
447 static
|
|
ferencd@0
|
448 tzdb_list
|
|
ferencd@0
|
449 create_tzdb()
|
|
ferencd@0
|
450 {
|
|
ferencd@0
|
451 tzdb_list tz_db;
|
|
ferencd@0
|
452 tzdb_list::undocumented_helper::push_front(tz_db, init_tzdb().release());
|
|
ferencd@0
|
453 return tz_db;
|
|
ferencd@0
|
454 }
|
|
ferencd@0
|
455
|
|
ferencd@0
|
456 tzdb_list&
|
|
ferencd@0
|
457 get_tzdb_list()
|
|
ferencd@0
|
458 {
|
|
ferencd@0
|
459 static tzdb_list tz_db = create_tzdb();
|
|
ferencd@0
|
460 return tz_db;
|
|
ferencd@0
|
461 }
|
|
ferencd@0
|
462
|
|
ferencd@0
|
463 #if !USE_OS_TZDB
|
|
ferencd@0
|
464
|
|
ferencd@0
|
465 #ifdef _WIN32
|
|
ferencd@0
|
466
|
|
ferencd@0
|
467 static
|
|
ferencd@0
|
468 void
|
|
ferencd@0
|
469 sort_zone_mappings(std::vector<date::detail::timezone_mapping>& mappings)
|
|
ferencd@0
|
470 {
|
|
ferencd@0
|
471 std::sort(mappings.begin(), mappings.end(),
|
|
ferencd@0
|
472 [](const date::detail::timezone_mapping& lhs,
|
|
ferencd@0
|
473 const date::detail::timezone_mapping& rhs)->bool
|
|
ferencd@0
|
474 {
|
|
ferencd@0
|
475 auto other_result = lhs.other.compare(rhs.other);
|
|
ferencd@0
|
476 if (other_result < 0)
|
|
ferencd@0
|
477 return true;
|
|
ferencd@0
|
478 else if (other_result == 0)
|
|
ferencd@0
|
479 {
|
|
ferencd@0
|
480 auto territory_result = lhs.territory.compare(rhs.territory);
|
|
ferencd@0
|
481 if (territory_result < 0)
|
|
ferencd@0
|
482 return true;
|
|
ferencd@0
|
483 else if (territory_result == 0)
|
|
ferencd@0
|
484 {
|
|
ferencd@0
|
485 if (lhs.type < rhs.type)
|
|
ferencd@0
|
486 return true;
|
|
ferencd@0
|
487 }
|
|
ferencd@0
|
488 }
|
|
ferencd@0
|
489 return false;
|
|
ferencd@0
|
490 });
|
|
ferencd@0
|
491 }
|
|
ferencd@0
|
492
|
|
ferencd@0
|
493 static
|
|
ferencd@0
|
494 bool
|
|
ferencd@0
|
495 native_to_standard_timezone_name(const std::string& native_tz_name,
|
|
ferencd@0
|
496 std::string& standard_tz_name)
|
|
ferencd@0
|
497 {
|
|
ferencd@0
|
498 // TOOD! Need be a case insensitive compare?
|
|
ferencd@0
|
499 if (native_tz_name == "UTC")
|
|
ferencd@0
|
500 {
|
|
ferencd@0
|
501 standard_tz_name = "Etc/UTC";
|
|
ferencd@0
|
502 return true;
|
|
ferencd@0
|
503 }
|
|
ferencd@0
|
504 standard_tz_name.clear();
|
|
ferencd@0
|
505 // TODO! we can improve on linear search.
|
|
ferencd@0
|
506 const auto& mappings = date::get_tzdb().mappings;
|
|
ferencd@0
|
507 for (const auto& tzm : mappings)
|
|
ferencd@0
|
508 {
|
|
ferencd@0
|
509 if (tzm.other == native_tz_name)
|
|
ferencd@0
|
510 {
|
|
ferencd@0
|
511 standard_tz_name = tzm.type;
|
|
ferencd@0
|
512 return true;
|
|
ferencd@0
|
513 }
|
|
ferencd@0
|
514 }
|
|
ferencd@0
|
515 return false;
|
|
ferencd@0
|
516 }
|
|
ferencd@0
|
517
|
|
ferencd@0
|
518 // Parse this XML file:
|
|
ferencd@0
|
519 // https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml
|
|
ferencd@0
|
520 // The parsing method is designed to be simple and quick. It is not overly
|
|
ferencd@0
|
521 // forgiving of change but it should diagnose basic format issues.
|
|
ferencd@0
|
522 // See timezone_mapping structure for more info.
|
|
ferencd@0
|
523 static
|
|
ferencd@0
|
524 std::vector<detail::timezone_mapping>
|
|
ferencd@0
|
525 load_timezone_mappings_from_xml_file(const std::string& input_path)
|
|
ferencd@0
|
526 {
|
|
ferencd@0
|
527 std::size_t line_num = 0;
|
|
ferencd@0
|
528 std::vector<detail::timezone_mapping> mappings;
|
|
ferencd@0
|
529 std::string line;
|
|
ferencd@0
|
530
|
|
ferencd@0
|
531 std::ifstream is(input_path);
|
|
ferencd@0
|
532 if (!is.is_open())
|
|
ferencd@0
|
533 {
|
|
ferencd@0
|
534 // We don't emit file exceptions because that's an implementation detail.
|
|
ferencd@0
|
535 std::string msg = "Error opening time zone mapping file \"";
|
|
ferencd@0
|
536 msg += input_path;
|
|
ferencd@0
|
537 msg += "\".";
|
|
ferencd@0
|
538 throw std::runtime_error(msg);
|
|
ferencd@0
|
539 }
|
|
ferencd@0
|
540
|
|
ferencd@0
|
541 auto error = [&input_path, &line_num](const char* info)
|
|
ferencd@0
|
542 {
|
|
ferencd@0
|
543 std::string msg = "Error loading time zone mapping file \"";
|
|
ferencd@0
|
544 msg += input_path;
|
|
ferencd@0
|
545 msg += "\" at line ";
|
|
ferencd@0
|
546 msg += std::to_string(line_num);
|
|
ferencd@0
|
547 msg += ": ";
|
|
ferencd@0
|
548 msg += info;
|
|
ferencd@0
|
549 throw std::runtime_error(msg);
|
|
ferencd@0
|
550 };
|
|
ferencd@0
|
551 // [optional space]a="b"
|
|
ferencd@0
|
552 auto read_attribute = [&line, &error]
|
|
ferencd@0
|
553 (const char* name, std::string& value, std::size_t startPos)
|
|
ferencd@0
|
554 ->std::size_t
|
|
ferencd@0
|
555 {
|
|
ferencd@0
|
556 value.clear();
|
|
ferencd@0
|
557 // Skip leading space before attribute name.
|
|
ferencd@0
|
558 std::size_t spos = line.find_first_not_of(' ', startPos);
|
|
ferencd@0
|
559 if (spos == std::string::npos)
|
|
ferencd@0
|
560 spos = startPos;
|
|
ferencd@0
|
561 // Assume everything up to next = is the attribute name
|
|
ferencd@0
|
562 // and that an = will always delimit that.
|
|
ferencd@0
|
563 std::size_t epos = line.find('=', spos);
|
|
ferencd@0
|
564 if (epos == std::string::npos)
|
|
ferencd@0
|
565 error("Expected \'=\' right after attribute name.");
|
|
ferencd@0
|
566 std::size_t name_len = epos - spos;
|
|
ferencd@0
|
567 // Expect the name we find matches the name we expect.
|
|
ferencd@0
|
568 if (line.compare(spos, name_len, name) != 0)
|
|
ferencd@0
|
569 {
|
|
ferencd@0
|
570 std::string msg;
|
|
ferencd@0
|
571 msg = "Expected attribute name \'";
|
|
ferencd@0
|
572 msg += name;
|
|
ferencd@0
|
573 msg += "\' around position ";
|
|
ferencd@0
|
574 msg += std::to_string(spos);
|
|
ferencd@0
|
575 msg += " but found something else.";
|
|
ferencd@0
|
576 error(msg.c_str());
|
|
ferencd@0
|
577 }
|
|
ferencd@0
|
578 ++epos; // Skip the '=' that is after the attribute name.
|
|
ferencd@0
|
579 spos = epos;
|
|
ferencd@0
|
580 if (spos < line.length() && line[spos] == '\"')
|
|
ferencd@0
|
581 ++spos; // Skip the quote that is before the attribute value.
|
|
ferencd@0
|
582 else
|
|
ferencd@0
|
583 {
|
|
ferencd@0
|
584 std::string msg = "Expected '\"' to begin value of attribute \'";
|
|
ferencd@0
|
585 msg += name;
|
|
ferencd@0
|
586 msg += "\'.";
|
|
ferencd@0
|
587 error(msg.c_str());
|
|
ferencd@0
|
588 }
|
|
ferencd@0
|
589 epos = line.find('\"', spos);
|
|
ferencd@0
|
590 if (epos == std::string::npos)
|
|
ferencd@0
|
591 {
|
|
ferencd@0
|
592 std::string msg = "Expected '\"' to end value of attribute \'";
|
|
ferencd@0
|
593 msg += name;
|
|
ferencd@0
|
594 msg += "\'.";
|
|
ferencd@0
|
595 error(msg.c_str());
|
|
ferencd@0
|
596 }
|
|
ferencd@0
|
597 // Extract everything in between the quotes. Note no escaping is done.
|
|
ferencd@0
|
598 std::size_t value_len = epos - spos;
|
|
ferencd@0
|
599 value.assign(line, spos, value_len);
|
|
ferencd@0
|
600 ++epos; // Skip the quote that is after the attribute value;
|
|
ferencd@0
|
601 return epos;
|
|
ferencd@0
|
602 };
|
|
ferencd@0
|
603
|
|
ferencd@0
|
604 // Quick but not overly forgiving XML mapping file processing.
|
|
ferencd@0
|
605 bool mapTimezonesOpenTagFound = false;
|
|
ferencd@0
|
606 bool mapTimezonesCloseTagFound = false;
|
|
ferencd@0
|
607 std::size_t mapZonePos = std::string::npos;
|
|
ferencd@0
|
608 std::size_t mapTimezonesPos = std::string::npos;
|
|
ferencd@0
|
609 CONSTDATA char mapTimeZonesOpeningTag[] = { "<mapTimezones " };
|
|
ferencd@0
|
610 CONSTDATA char mapZoneOpeningTag[] = { "<mapZone " };
|
|
ferencd@0
|
611 CONSTDATA std::size_t mapZoneOpeningTagLen = sizeof(mapZoneOpeningTag) /
|
|
ferencd@0
|
612 sizeof(mapZoneOpeningTag[0]) - 1;
|
|
ferencd@0
|
613 while (!mapTimezonesOpenTagFound)
|
|
ferencd@0
|
614 {
|
|
ferencd@0
|
615 std::getline(is, line);
|
|
ferencd@0
|
616 ++line_num;
|
|
ferencd@0
|
617 if (is.eof())
|
|
ferencd@0
|
618 {
|
|
ferencd@0
|
619 // If there is no mapTimezones tag is it an error?
|
|
ferencd@0
|
620 // Perhaps if there are no mapZone mappings it might be ok for
|
|
ferencd@0
|
621 // its parent mapTimezones element to be missing?
|
|
ferencd@0
|
622 // We treat this as an error though on the assumption that if there
|
|
ferencd@0
|
623 // really are no mappings we should still get a mapTimezones parent
|
|
ferencd@0
|
624 // element but no mapZone elements inside. Assuming we must
|
|
ferencd@0
|
625 // find something will hopefully at least catch more drastic formatting
|
|
ferencd@0
|
626 // changes or errors than if we don't do this and assume nothing found.
|
|
ferencd@0
|
627 error("Expected a mapTimezones opening tag.");
|
|
ferencd@0
|
628 }
|
|
ferencd@0
|
629 mapTimezonesPos = line.find(mapTimeZonesOpeningTag);
|
|
ferencd@0
|
630 mapTimezonesOpenTagFound = (mapTimezonesPos != std::string::npos);
|
|
ferencd@0
|
631 }
|
|
ferencd@0
|
632
|
|
ferencd@0
|
633 // NOTE: We could extract the version info that follows the opening
|
|
ferencd@0
|
634 // mapTimezones tag and compare that to the version of other data we have.
|
|
ferencd@0
|
635 // I would have expected them to be kept in synch but testing has shown
|
|
ferencd@0
|
636 // it typically does not match anyway. So what's the point?
|
|
ferencd@0
|
637 while (!mapTimezonesCloseTagFound)
|
|
ferencd@0
|
638 {
|
|
ferencd@0
|
639 std::ws(is);
|
|
ferencd@0
|
640 std::getline(is, line);
|
|
ferencd@0
|
641 ++line_num;
|
|
ferencd@0
|
642 if (is.eof())
|
|
ferencd@0
|
643 error("Expected a mapTimezones closing tag.");
|
|
ferencd@0
|
644 if (line.empty())
|
|
ferencd@0
|
645 continue;
|
|
ferencd@0
|
646 mapZonePos = line.find(mapZoneOpeningTag);
|
|
ferencd@0
|
647 if (mapZonePos != std::string::npos)
|
|
ferencd@0
|
648 {
|
|
ferencd@0
|
649 mapZonePos += mapZoneOpeningTagLen;
|
|
ferencd@0
|
650 detail::timezone_mapping zm{};
|
|
ferencd@0
|
651 std::size_t pos = read_attribute("other", zm.other, mapZonePos);
|
|
ferencd@0
|
652 pos = read_attribute("territory", zm.territory, pos);
|
|
ferencd@0
|
653 read_attribute("type", zm.type, pos);
|
|
ferencd@0
|
654 mappings.push_back(std::move(zm));
|
|
ferencd@0
|
655
|
|
ferencd@0
|
656 continue;
|
|
ferencd@0
|
657 }
|
|
ferencd@0
|
658 mapTimezonesPos = line.find("</mapTimezones>");
|
|
ferencd@0
|
659 mapTimezonesCloseTagFound = (mapTimezonesPos != std::string::npos);
|
|
ferencd@0
|
660 if (!mapTimezonesCloseTagFound)
|
|
ferencd@0
|
661 {
|
|
ferencd@0
|
662 std::size_t commentPos = line.find("<!--");
|
|
ferencd@0
|
663 if (commentPos == std::string::npos)
|
|
ferencd@0
|
664 error("Unexpected mapping record found. A xml mapZone or comment "
|
|
ferencd@0
|
665 "attribute or mapTimezones closing tag was expected.");
|
|
ferencd@0
|
666 }
|
|
ferencd@0
|
667 }
|
|
ferencd@0
|
668
|
|
ferencd@0
|
669 is.close();
|
|
ferencd@0
|
670 return mappings;
|
|
ferencd@0
|
671 }
|
|
ferencd@0
|
672
|
|
ferencd@0
|
673 #endif // _WIN32
|
|
ferencd@0
|
674
|
|
ferencd@0
|
675 // Parsing helpers
|
|
ferencd@0
|
676
|
|
ferencd@0
|
677 static
|
|
ferencd@0
|
678 std::string
|
|
ferencd@0
|
679 parse3(std::istream& in)
|
|
ferencd@0
|
680 {
|
|
ferencd@0
|
681 std::string r(3, ' ');
|
|
ferencd@0
|
682 ws(in);
|
|
ferencd@0
|
683 r[0] = static_cast<char>(in.get());
|
|
ferencd@0
|
684 r[1] = static_cast<char>(in.get());
|
|
ferencd@0
|
685 r[2] = static_cast<char>(in.get());
|
|
ferencd@0
|
686 return r;
|
|
ferencd@0
|
687 }
|
|
ferencd@0
|
688
|
|
ferencd@0
|
689 static
|
|
ferencd@0
|
690 unsigned
|
|
ferencd@0
|
691 parse_dow(std::istream& in)
|
|
ferencd@0
|
692 {
|
|
ferencd@0
|
693 CONSTDATA char*const dow_names[] =
|
|
ferencd@0
|
694 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
|
ferencd@0
|
695 auto s = parse3(in);
|
|
ferencd@0
|
696 auto dow = std::find(std::begin(dow_names), std::end(dow_names), s) - dow_names;
|
|
ferencd@0
|
697 if (dow >= std::end(dow_names) - std::begin(dow_names))
|
|
ferencd@0
|
698 throw std::runtime_error("oops: bad dow name: " + s);
|
|
ferencd@0
|
699 return static_cast<unsigned>(dow);
|
|
ferencd@0
|
700 }
|
|
ferencd@0
|
701
|
|
ferencd@0
|
702 static
|
|
ferencd@0
|
703 unsigned
|
|
ferencd@0
|
704 parse_month(std::istream& in)
|
|
ferencd@0
|
705 {
|
|
ferencd@0
|
706 CONSTDATA char*const month_names[] =
|
|
ferencd@0
|
707 {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
ferencd@0
|
708 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
|
ferencd@0
|
709 auto s = parse3(in);
|
|
ferencd@0
|
710 auto m = std::find(std::begin(month_names), std::end(month_names), s) - month_names;
|
|
ferencd@0
|
711 if (m >= std::end(month_names) - std::begin(month_names))
|
|
ferencd@0
|
712 throw std::runtime_error("oops: bad month name: " + s);
|
|
ferencd@0
|
713 return static_cast<unsigned>(++m);
|
|
ferencd@0
|
714 }
|
|
ferencd@0
|
715
|
|
ferencd@0
|
716 static
|
|
ferencd@0
|
717 std::chrono::seconds
|
|
ferencd@0
|
718 parse_unsigned_time(std::istream& in)
|
|
ferencd@0
|
719 {
|
|
ferencd@0
|
720 using namespace std::chrono;
|
|
ferencd@0
|
721 int x;
|
|
ferencd@0
|
722 in >> x;
|
|
ferencd@0
|
723 auto r = seconds{hours{x}};
|
|
ferencd@0
|
724 if (!in.eof() && in.peek() == ':')
|
|
ferencd@0
|
725 {
|
|
ferencd@0
|
726 in.get();
|
|
ferencd@0
|
727 in >> x;
|
|
ferencd@0
|
728 r += minutes{x};
|
|
ferencd@0
|
729 if (!in.eof() && in.peek() == ':')
|
|
ferencd@0
|
730 {
|
|
ferencd@0
|
731 in.get();
|
|
ferencd@0
|
732 in >> x;
|
|
ferencd@0
|
733 r += seconds{x};
|
|
ferencd@0
|
734 }
|
|
ferencd@0
|
735 }
|
|
ferencd@0
|
736 return r;
|
|
ferencd@0
|
737 }
|
|
ferencd@0
|
738
|
|
ferencd@0
|
739 static
|
|
ferencd@0
|
740 std::chrono::seconds
|
|
ferencd@0
|
741 parse_signed_time(std::istream& in)
|
|
ferencd@0
|
742 {
|
|
ferencd@0
|
743 ws(in);
|
|
ferencd@0
|
744 auto sign = 1;
|
|
ferencd@0
|
745 if (in.peek() == '-')
|
|
ferencd@0
|
746 {
|
|
ferencd@0
|
747 sign = -1;
|
|
ferencd@0
|
748 in.get();
|
|
ferencd@0
|
749 }
|
|
ferencd@0
|
750 else if (in.peek() == '+')
|
|
ferencd@0
|
751 in.get();
|
|
ferencd@0
|
752 return sign * parse_unsigned_time(in);
|
|
ferencd@0
|
753 }
|
|
ferencd@0
|
754
|
|
ferencd@0
|
755 // MonthDayTime
|
|
ferencd@0
|
756
|
|
ferencd@0
|
757 detail::MonthDayTime::MonthDayTime(local_seconds tp, tz timezone)
|
|
ferencd@0
|
758 : zone_(timezone)
|
|
ferencd@0
|
759 {
|
|
ferencd@0
|
760 using namespace date;
|
|
ferencd@0
|
761 const auto dp = date::floor<days>(tp);
|
|
ferencd@0
|
762 const auto hms = make_time(tp - dp);
|
|
ferencd@0
|
763 const auto ymd = year_month_day(dp);
|
|
ferencd@0
|
764 u = ymd.month() / ymd.day();
|
|
ferencd@0
|
765 h_ = hms.hours();
|
|
ferencd@0
|
766 m_ = hms.minutes();
|
|
ferencd@0
|
767 s_ = hms.seconds();
|
|
ferencd@0
|
768 }
|
|
ferencd@0
|
769
|
|
ferencd@0
|
770 detail::MonthDayTime::MonthDayTime(const date::month_day& md, tz timezone)
|
|
ferencd@0
|
771 : zone_(timezone)
|
|
ferencd@0
|
772 {
|
|
ferencd@0
|
773 u = md;
|
|
ferencd@0
|
774 }
|
|
ferencd@0
|
775
|
|
ferencd@0
|
776 date::day
|
|
ferencd@0
|
777 detail::MonthDayTime::day() const
|
|
ferencd@0
|
778 {
|
|
ferencd@0
|
779 switch (type_)
|
|
ferencd@0
|
780 {
|
|
ferencd@0
|
781 case month_day:
|
|
ferencd@0
|
782 return u.month_day_.day();
|
|
ferencd@0
|
783 case month_last_dow:
|
|
ferencd@0
|
784 return date::day{31};
|
|
ferencd@0
|
785 case lteq:
|
|
ferencd@0
|
786 case gteq:
|
|
ferencd@0
|
787 break;
|
|
ferencd@0
|
788 }
|
|
ferencd@0
|
789 return u.month_day_weekday_.month_day_.day();
|
|
ferencd@0
|
790 }
|
|
ferencd@0
|
791
|
|
ferencd@0
|
792 date::month
|
|
ferencd@0
|
793 detail::MonthDayTime::month() const
|
|
ferencd@0
|
794 {
|
|
ferencd@0
|
795 switch (type_)
|
|
ferencd@0
|
796 {
|
|
ferencd@0
|
797 case month_day:
|
|
ferencd@0
|
798 return u.month_day_.month();
|
|
ferencd@0
|
799 case month_last_dow:
|
|
ferencd@0
|
800 return u.month_weekday_last_.month();
|
|
ferencd@0
|
801 case lteq:
|
|
ferencd@0
|
802 case gteq:
|
|
ferencd@0
|
803 break;
|
|
ferencd@0
|
804 }
|
|
ferencd@0
|
805 return u.month_day_weekday_.month_day_.month();
|
|
ferencd@0
|
806 }
|
|
ferencd@0
|
807
|
|
ferencd@0
|
808 int
|
|
ferencd@0
|
809 detail::MonthDayTime::compare(date::year y, const MonthDayTime& x, date::year yx,
|
|
ferencd@0
|
810 std::chrono::seconds offset, std::chrono::minutes prev_save) const
|
|
ferencd@0
|
811 {
|
|
ferencd@0
|
812 if (zone_ != x.zone_)
|
|
ferencd@0
|
813 {
|
|
ferencd@0
|
814 auto dp0 = to_sys_days(y);
|
|
ferencd@0
|
815 auto dp1 = x.to_sys_days(yx);
|
|
ferencd@0
|
816 if (std::abs((dp0-dp1).count()) > 1)
|
|
ferencd@0
|
817 return dp0 < dp1 ? -1 : 1;
|
|
ferencd@0
|
818 if (zone_ == tz::local)
|
|
ferencd@0
|
819 {
|
|
ferencd@0
|
820 auto tp0 = to_time_point(y) - prev_save;
|
|
ferencd@0
|
821 if (x.zone_ == tz::utc)
|
|
ferencd@0
|
822 tp0 -= offset;
|
|
ferencd@0
|
823 auto tp1 = x.to_time_point(yx);
|
|
ferencd@0
|
824 return tp0 < tp1 ? -1 : tp0 == tp1 ? 0 : 1;
|
|
ferencd@0
|
825 }
|
|
ferencd@0
|
826 else if (zone_ == tz::standard)
|
|
ferencd@0
|
827 {
|
|
ferencd@0
|
828 auto tp0 = to_time_point(y);
|
|
ferencd@0
|
829 auto tp1 = x.to_time_point(yx);
|
|
ferencd@0
|
830 if (x.zone_ == tz::local)
|
|
ferencd@0
|
831 tp1 -= prev_save;
|
|
ferencd@0
|
832 else
|
|
ferencd@0
|
833 tp0 -= offset;
|
|
ferencd@0
|
834 return tp0 < tp1 ? -1 : tp0 == tp1 ? 0 : 1;
|
|
ferencd@0
|
835 }
|
|
ferencd@0
|
836 // zone_ == tz::utc
|
|
ferencd@0
|
837 auto tp0 = to_time_point(y);
|
|
ferencd@0
|
838 auto tp1 = x.to_time_point(yx);
|
|
ferencd@0
|
839 if (x.zone_ == tz::local)
|
|
ferencd@0
|
840 tp1 -= offset + prev_save;
|
|
ferencd@0
|
841 else
|
|
ferencd@0
|
842 tp1 -= offset;
|
|
ferencd@0
|
843 return tp0 < tp1 ? -1 : tp0 == tp1 ? 0 : 1;
|
|
ferencd@0
|
844 }
|
|
ferencd@0
|
845 auto const t0 = to_time_point(y);
|
|
ferencd@0
|
846 auto const t1 = x.to_time_point(yx);
|
|
ferencd@0
|
847 return t0 < t1 ? -1 : t0 == t1 ? 0 : 1;
|
|
ferencd@0
|
848 }
|
|
ferencd@0
|
849
|
|
ferencd@0
|
850 sys_seconds
|
|
ferencd@0
|
851 detail::MonthDayTime::to_sys(date::year y, std::chrono::seconds offset,
|
|
ferencd@0
|
852 std::chrono::seconds save) const
|
|
ferencd@0
|
853 {
|
|
ferencd@0
|
854 using namespace date;
|
|
ferencd@0
|
855 using namespace std::chrono;
|
|
ferencd@0
|
856 auto until_utc = to_time_point(y);
|
|
ferencd@0
|
857 if (zone_ == tz::standard)
|
|
ferencd@0
|
858 until_utc -= offset;
|
|
ferencd@0
|
859 else if (zone_ == tz::local)
|
|
ferencd@0
|
860 until_utc -= offset + save;
|
|
ferencd@0
|
861 return until_utc;
|
|
ferencd@0
|
862 }
|
|
ferencd@0
|
863
|
|
ferencd@0
|
864 detail::MonthDayTime::U&
|
|
ferencd@0
|
865 detail::MonthDayTime::U::operator=(const date::month_day& x)
|
|
ferencd@0
|
866 {
|
|
ferencd@0
|
867 month_day_ = x;
|
|
ferencd@0
|
868 return *this;
|
|
ferencd@0
|
869 }
|
|
ferencd@0
|
870
|
|
ferencd@0
|
871 detail::MonthDayTime::U&
|
|
ferencd@0
|
872 detail::MonthDayTime::U::operator=(const date::month_weekday_last& x)
|
|
ferencd@0
|
873 {
|
|
ferencd@0
|
874 month_weekday_last_ = x;
|
|
ferencd@0
|
875 return *this;
|
|
ferencd@0
|
876 }
|
|
ferencd@0
|
877
|
|
ferencd@0
|
878 detail::MonthDayTime::U&
|
|
ferencd@0
|
879 detail::MonthDayTime::U::operator=(const pair& x)
|
|
ferencd@0
|
880 {
|
|
ferencd@0
|
881 month_day_weekday_ = x;
|
|
ferencd@0
|
882 return *this;
|
|
ferencd@0
|
883 }
|
|
ferencd@0
|
884
|
|
ferencd@0
|
885 date::sys_days
|
|
ferencd@0
|
886 detail::MonthDayTime::to_sys_days(date::year y) const
|
|
ferencd@0
|
887 {
|
|
ferencd@0
|
888 using namespace std::chrono;
|
|
ferencd@0
|
889 using namespace date;
|
|
ferencd@0
|
890 switch (type_)
|
|
ferencd@0
|
891 {
|
|
ferencd@0
|
892 case month_day:
|
|
ferencd@0
|
893 return sys_days(y/u.month_day_);
|
|
ferencd@0
|
894 case month_last_dow:
|
|
ferencd@0
|
895 return sys_days(y/u.month_weekday_last_);
|
|
ferencd@0
|
896 case lteq:
|
|
ferencd@0
|
897 {
|
|
ferencd@0
|
898 auto const x = y/u.month_day_weekday_.month_day_;
|
|
ferencd@0
|
899 auto const wd1 = weekday(static_cast<sys_days>(x));
|
|
ferencd@0
|
900 auto const wd0 = u.month_day_weekday_.weekday_;
|
|
ferencd@0
|
901 return sys_days(x) - (wd1-wd0);
|
|
ferencd@0
|
902 }
|
|
ferencd@0
|
903 case gteq:
|
|
ferencd@0
|
904 break;
|
|
ferencd@0
|
905 }
|
|
ferencd@0
|
906 auto const x = y/u.month_day_weekday_.month_day_;
|
|
ferencd@0
|
907 auto const wd1 = u.month_day_weekday_.weekday_;
|
|
ferencd@0
|
908 auto const wd0 = weekday(static_cast<sys_days>(x));
|
|
ferencd@0
|
909 return sys_days(x) + (wd1-wd0);
|
|
ferencd@0
|
910 }
|
|
ferencd@0
|
911
|
|
ferencd@0
|
912 sys_seconds
|
|
ferencd@0
|
913 detail::MonthDayTime::to_time_point(date::year y) const
|
|
ferencd@0
|
914 {
|
|
ferencd@0
|
915 // Add seconds first to promote to largest rep early to prevent overflow
|
|
ferencd@0
|
916 return to_sys_days(y) + s_ + h_ + m_;
|
|
ferencd@0
|
917 }
|
|
ferencd@0
|
918
|
|
ferencd@0
|
919 void
|
|
ferencd@0
|
920 detail::MonthDayTime::canonicalize(date::year y)
|
|
ferencd@0
|
921 {
|
|
ferencd@0
|
922 using namespace std::chrono;
|
|
ferencd@0
|
923 using namespace date;
|
|
ferencd@0
|
924 switch (type_)
|
|
ferencd@0
|
925 {
|
|
ferencd@0
|
926 case month_day:
|
|
ferencd@0
|
927 return;
|
|
ferencd@0
|
928 case month_last_dow:
|
|
ferencd@0
|
929 {
|
|
ferencd@0
|
930 auto const ymd = year_month_day(sys_days(y/u.month_weekday_last_));
|
|
ferencd@0
|
931 u.month_day_ = ymd.month()/ymd.day();
|
|
ferencd@0
|
932 type_ = month_day;
|
|
ferencd@0
|
933 return;
|
|
ferencd@0
|
934 }
|
|
ferencd@0
|
935 case lteq:
|
|
ferencd@0
|
936 {
|
|
ferencd@0
|
937 auto const x = y/u.month_day_weekday_.month_day_;
|
|
ferencd@0
|
938 auto const wd1 = weekday(static_cast<sys_days>(x));
|
|
ferencd@0
|
939 auto const wd0 = u.month_day_weekday_.weekday_;
|
|
ferencd@0
|
940 auto const ymd = year_month_day(sys_days(x) - (wd1-wd0));
|
|
ferencd@0
|
941 u.month_day_ = ymd.month()/ymd.day();
|
|
ferencd@0
|
942 type_ = month_day;
|
|
ferencd@0
|
943 return;
|
|
ferencd@0
|
944 }
|
|
ferencd@0
|
945 case gteq:
|
|
ferencd@0
|
946 {
|
|
ferencd@0
|
947 auto const x = y/u.month_day_weekday_.month_day_;
|
|
ferencd@0
|
948 auto const wd1 = u.month_day_weekday_.weekday_;
|
|
ferencd@0
|
949 auto const wd0 = weekday(static_cast<sys_days>(x));
|
|
ferencd@0
|
950 auto const ymd = year_month_day(sys_days(x) + (wd1-wd0));
|
|
ferencd@0
|
951 u.month_day_ = ymd.month()/ymd.day();
|
|
ferencd@0
|
952 type_ = month_day;
|
|
ferencd@0
|
953 return;
|
|
ferencd@0
|
954 }
|
|
ferencd@0
|
955 }
|
|
ferencd@0
|
956 }
|
|
ferencd@0
|
957
|
|
ferencd@0
|
958 std::istream&
|
|
ferencd@0
|
959 detail::operator>>(std::istream& is, MonthDayTime& x)
|
|
ferencd@0
|
960 {
|
|
ferencd@0
|
961 using namespace date;
|
|
ferencd@0
|
962 using namespace std::chrono;
|
|
ferencd@0
|
963 assert(((std::ios::failbit | std::ios::badbit) & is.exceptions()) ==
|
|
ferencd@0
|
964 (std::ios::failbit | std::ios::badbit));
|
|
ferencd@0
|
965 x = MonthDayTime{};
|
|
ferencd@0
|
966 if (!is.eof() && ws(is) && !is.eof() && is.peek() != '#')
|
|
ferencd@0
|
967 {
|
|
ferencd@0
|
968 auto m = parse_month(is);
|
|
ferencd@0
|
969 if (!is.eof() && ws(is) && !is.eof() && is.peek() != '#')
|
|
ferencd@0
|
970 {
|
|
ferencd@0
|
971 if (is.peek() == 'l')
|
|
ferencd@0
|
972 {
|
|
ferencd@0
|
973 for (int i = 0; i < 4; ++i)
|
|
ferencd@0
|
974 is.get();
|
|
ferencd@0
|
975 auto dow = parse_dow(is);
|
|
ferencd@0
|
976 x.type_ = MonthDayTime::month_last_dow;
|
|
ferencd@0
|
977 x.u = date::month(m)/weekday(dow)[last];
|
|
ferencd@0
|
978 }
|
|
ferencd@0
|
979 else if (std::isalpha(is.peek()))
|
|
ferencd@0
|
980 {
|
|
ferencd@0
|
981 auto dow = parse_dow(is);
|
|
ferencd@0
|
982 char c{};
|
|
ferencd@0
|
983 is >> c;
|
|
ferencd@0
|
984 if (c == '<' || c == '>')
|
|
ferencd@0
|
985 {
|
|
ferencd@0
|
986 char c2{};
|
|
ferencd@0
|
987 is >> c2;
|
|
ferencd@0
|
988 if (c2 != '=')
|
|
ferencd@0
|
989 throw std::runtime_error(std::string("bad operator: ") + c + c2);
|
|
ferencd@0
|
990 int d;
|
|
ferencd@0
|
991 is >> d;
|
|
ferencd@0
|
992 if (d < 1 || d > 31)
|
|
ferencd@0
|
993 throw std::runtime_error(std::string("bad operator: ") + c + c2
|
|
ferencd@0
|
994 + std::to_string(d));
|
|
ferencd@0
|
995 x.type_ = c == '<' ? MonthDayTime::lteq : MonthDayTime::gteq;
|
|
ferencd@0
|
996 x.u = MonthDayTime::pair{ date::month(m) / d, date::weekday(dow) };
|
|
ferencd@0
|
997 }
|
|
ferencd@0
|
998 else
|
|
ferencd@0
|
999 throw std::runtime_error(std::string("bad operator: ") + c);
|
|
ferencd@0
|
1000 }
|
|
ferencd@0
|
1001 else // if (std::isdigit(is.peek())
|
|
ferencd@0
|
1002 {
|
|
ferencd@0
|
1003 int d;
|
|
ferencd@0
|
1004 is >> d;
|
|
ferencd@0
|
1005 if (d < 1 || d > 31)
|
|
ferencd@0
|
1006 throw std::runtime_error(std::string("day of month: ")
|
|
ferencd@0
|
1007 + std::to_string(d));
|
|
ferencd@0
|
1008 x.type_ = MonthDayTime::month_day;
|
|
ferencd@0
|
1009 x.u = date::month(m)/d;
|
|
ferencd@0
|
1010 }
|
|
ferencd@0
|
1011 if (!is.eof() && ws(is) && !is.eof() && is.peek() != '#')
|
|
ferencd@0
|
1012 {
|
|
ferencd@0
|
1013 int t;
|
|
ferencd@0
|
1014 is >> t;
|
|
ferencd@0
|
1015 x.h_ = hours{t};
|
|
ferencd@0
|
1016 if (!is.eof() && is.peek() == ':')
|
|
ferencd@0
|
1017 {
|
|
ferencd@0
|
1018 is.get();
|
|
ferencd@0
|
1019 is >> t;
|
|
ferencd@0
|
1020 x.m_ = minutes{t};
|
|
ferencd@0
|
1021 if (!is.eof() && is.peek() == ':')
|
|
ferencd@0
|
1022 {
|
|
ferencd@0
|
1023 is.get();
|
|
ferencd@0
|
1024 is >> t;
|
|
ferencd@0
|
1025 x.s_ = seconds{t};
|
|
ferencd@0
|
1026 }
|
|
ferencd@0
|
1027 }
|
|
ferencd@0
|
1028 if (!is.eof() && std::isalpha(is.peek()))
|
|
ferencd@0
|
1029 {
|
|
ferencd@0
|
1030 char c;
|
|
ferencd@0
|
1031 is >> c;
|
|
ferencd@0
|
1032 switch (c)
|
|
ferencd@0
|
1033 {
|
|
ferencd@0
|
1034 case 's':
|
|
ferencd@0
|
1035 x.zone_ = tz::standard;
|
|
ferencd@0
|
1036 break;
|
|
ferencd@0
|
1037 case 'u':
|
|
ferencd@0
|
1038 x.zone_ = tz::utc;
|
|
ferencd@0
|
1039 break;
|
|
ferencd@0
|
1040 }
|
|
ferencd@0
|
1041 }
|
|
ferencd@0
|
1042 }
|
|
ferencd@0
|
1043 }
|
|
ferencd@0
|
1044 else
|
|
ferencd@0
|
1045 {
|
|
ferencd@0
|
1046 x.u = month{m}/1;
|
|
ferencd@0
|
1047 }
|
|
ferencd@0
|
1048 }
|
|
ferencd@0
|
1049 return is;
|
|
ferencd@0
|
1050 }
|
|
ferencd@0
|
1051
|
|
ferencd@0
|
1052 std::ostream&
|
|
ferencd@0
|
1053 detail::operator<<(std::ostream& os, const MonthDayTime& x)
|
|
ferencd@0
|
1054 {
|
|
ferencd@0
|
1055 switch (x.type_)
|
|
ferencd@0
|
1056 {
|
|
ferencd@0
|
1057 case MonthDayTime::month_day:
|
|
ferencd@0
|
1058 os << x.u.month_day_ << " ";
|
|
ferencd@0
|
1059 break;
|
|
ferencd@0
|
1060 case MonthDayTime::month_last_dow:
|
|
ferencd@0
|
1061 os << x.u.month_weekday_last_ << " ";
|
|
ferencd@0
|
1062 break;
|
|
ferencd@0
|
1063 case MonthDayTime::lteq:
|
|
ferencd@0
|
1064 os << x.u.month_day_weekday_.weekday_ << " on or before "
|
|
ferencd@0
|
1065 << x.u.month_day_weekday_.month_day_ << " ";
|
|
ferencd@0
|
1066 break;
|
|
ferencd@0
|
1067 case MonthDayTime::gteq:
|
|
ferencd@0
|
1068 if ((static_cast<unsigned>(x.day()) - 1) % 7 == 0)
|
|
ferencd@0
|
1069 {
|
|
ferencd@0
|
1070 os << (x.u.month_day_weekday_.month_day_.month() /
|
|
ferencd@0
|
1071 x.u.month_day_weekday_.weekday_[
|
|
ferencd@0
|
1072 (static_cast<unsigned>(x.day()) - 1)/7+1]) << " ";
|
|
ferencd@0
|
1073 }
|
|
ferencd@0
|
1074 else
|
|
ferencd@0
|
1075 {
|
|
ferencd@0
|
1076 os << x.u.month_day_weekday_.weekday_ << " on or after "
|
|
ferencd@0
|
1077 << x.u.month_day_weekday_.month_day_ << " ";
|
|
ferencd@0
|
1078 }
|
|
ferencd@0
|
1079 break;
|
|
ferencd@0
|
1080 }
|
|
ferencd@0
|
1081 os << date::make_time(x.s_ + x.h_ + x.m_);
|
|
ferencd@0
|
1082 if (x.zone_ == tz::utc)
|
|
ferencd@0
|
1083 os << "UTC ";
|
|
ferencd@0
|
1084 else if (x.zone_ == tz::standard)
|
|
ferencd@0
|
1085 os << "STD ";
|
|
ferencd@0
|
1086 else
|
|
ferencd@0
|
1087 os << " ";
|
|
ferencd@0
|
1088 return os;
|
|
ferencd@0
|
1089 }
|
|
ferencd@0
|
1090
|
|
ferencd@0
|
1091 // Rule
|
|
ferencd@0
|
1092
|
|
ferencd@0
|
1093 detail::Rule::Rule(const std::string& s)
|
|
ferencd@0
|
1094 {
|
|
ferencd@0
|
1095 try
|
|
ferencd@0
|
1096 {
|
|
ferencd@0
|
1097 using namespace date;
|
|
ferencd@0
|
1098 using namespace std::chrono;
|
|
ferencd@0
|
1099 std::istringstream in(s);
|
|
ferencd@0
|
1100 in.exceptions(std::ios::failbit | std::ios::badbit);
|
|
ferencd@0
|
1101 std::string word;
|
|
ferencd@0
|
1102 in >> word >> name_;
|
|
ferencd@0
|
1103 int x;
|
|
ferencd@0
|
1104 ws(in);
|
|
ferencd@0
|
1105 if (std::isalpha(in.peek()))
|
|
ferencd@0
|
1106 {
|
|
ferencd@0
|
1107 in >> word;
|
|
ferencd@0
|
1108 if (word == "min")
|
|
ferencd@0
|
1109 {
|
|
ferencd@0
|
1110 starting_year_ = year::min();
|
|
ferencd@0
|
1111 }
|
|
ferencd@0
|
1112 else
|
|
ferencd@0
|
1113 throw std::runtime_error("Didn't find expected word: " + word);
|
|
ferencd@0
|
1114 }
|
|
ferencd@0
|
1115 else
|
|
ferencd@0
|
1116 {
|
|
ferencd@0
|
1117 in >> x;
|
|
ferencd@0
|
1118 starting_year_ = year{x};
|
|
ferencd@0
|
1119 }
|
|
ferencd@0
|
1120 std::ws(in);
|
|
ferencd@0
|
1121 if (std::isalpha(in.peek()))
|
|
ferencd@0
|
1122 {
|
|
ferencd@0
|
1123 in >> word;
|
|
ferencd@0
|
1124 if (word == "only")
|
|
ferencd@0
|
1125 {
|
|
ferencd@0
|
1126 ending_year_ = starting_year_;
|
|
ferencd@0
|
1127 }
|
|
ferencd@0
|
1128 else if (word == "max")
|
|
ferencd@0
|
1129 {
|
|
ferencd@0
|
1130 ending_year_ = year::max();
|
|
ferencd@0
|
1131 }
|
|
ferencd@0
|
1132 else
|
|
ferencd@0
|
1133 throw std::runtime_error("Didn't find expected word: " + word);
|
|
ferencd@0
|
1134 }
|
|
ferencd@0
|
1135 else
|
|
ferencd@0
|
1136 {
|
|
ferencd@0
|
1137 in >> x;
|
|
ferencd@0
|
1138 ending_year_ = year{x};
|
|
ferencd@0
|
1139 }
|
|
ferencd@0
|
1140 in >> word; // TYPE (always "-")
|
|
ferencd@0
|
1141 assert(word == "-");
|
|
ferencd@0
|
1142 in >> starting_at_;
|
|
ferencd@0
|
1143 save_ = duration_cast<minutes>(parse_signed_time(in));
|
|
ferencd@0
|
1144 in >> abbrev_;
|
|
ferencd@0
|
1145 if (abbrev_ == "-")
|
|
ferencd@0
|
1146 abbrev_.clear();
|
|
ferencd@0
|
1147 assert(hours{-1} <= save_ && save_ <= hours{2});
|
|
ferencd@0
|
1148 }
|
|
ferencd@0
|
1149 catch (...)
|
|
ferencd@0
|
1150 {
|
|
ferencd@0
|
1151 std::cerr << s << '\n';
|
|
ferencd@0
|
1152 std::cerr << *this << '\n';
|
|
ferencd@0
|
1153 throw;
|
|
ferencd@0
|
1154 }
|
|
ferencd@0
|
1155 }
|
|
ferencd@0
|
1156
|
|
ferencd@0
|
1157 detail::Rule::Rule(const Rule& r, date::year starting_year, date::year ending_year)
|
|
ferencd@0
|
1158 : name_(r.name_)
|
|
ferencd@0
|
1159 , starting_year_(starting_year)
|
|
ferencd@0
|
1160 , ending_year_(ending_year)
|
|
ferencd@0
|
1161 , starting_at_(r.starting_at_)
|
|
ferencd@0
|
1162 , save_(r.save_)
|
|
ferencd@0
|
1163 , abbrev_(r.abbrev_)
|
|
ferencd@0
|
1164 {
|
|
ferencd@0
|
1165 }
|
|
ferencd@0
|
1166
|
|
ferencd@0
|
1167 bool
|
|
ferencd@0
|
1168 detail::operator==(const Rule& x, const Rule& y)
|
|
ferencd@0
|
1169 {
|
|
ferencd@0
|
1170 if (std::tie(x.name_, x.save_, x.starting_year_, x.ending_year_) ==
|
|
ferencd@0
|
1171 std::tie(y.name_, y.save_, y.starting_year_, y.ending_year_))
|
|
ferencd@0
|
1172 return x.month() == y.month() && x.day() == y.day();
|
|
ferencd@0
|
1173 return false;
|
|
ferencd@0
|
1174 }
|
|
ferencd@0
|
1175
|
|
ferencd@0
|
1176 bool
|
|
ferencd@0
|
1177 detail::operator<(const Rule& x, const Rule& y)
|
|
ferencd@0
|
1178 {
|
|
ferencd@0
|
1179 using namespace std::chrono;
|
|
ferencd@0
|
1180 auto const xm = x.month();
|
|
ferencd@0
|
1181 auto const ym = y.month();
|
|
ferencd@0
|
1182 if (std::tie(x.name_, x.starting_year_, xm, x.ending_year_) <
|
|
ferencd@0
|
1183 std::tie(y.name_, y.starting_year_, ym, y.ending_year_))
|
|
ferencd@0
|
1184 return true;
|
|
ferencd@0
|
1185 if (std::tie(x.name_, x.starting_year_, xm, x.ending_year_) >
|
|
ferencd@0
|
1186 std::tie(y.name_, y.starting_year_, ym, y.ending_year_))
|
|
ferencd@0
|
1187 return false;
|
|
ferencd@0
|
1188 return x.day() < y.day();
|
|
ferencd@0
|
1189 }
|
|
ferencd@0
|
1190
|
|
ferencd@0
|
1191 bool
|
|
ferencd@0
|
1192 detail::operator==(const Rule& x, const date::year& y)
|
|
ferencd@0
|
1193 {
|
|
ferencd@0
|
1194 return x.starting_year_ <= y && y <= x.ending_year_;
|
|
ferencd@0
|
1195 }
|
|
ferencd@0
|
1196
|
|
ferencd@0
|
1197 bool
|
|
ferencd@0
|
1198 detail::operator<(const Rule& x, const date::year& y)
|
|
ferencd@0
|
1199 {
|
|
ferencd@0
|
1200 return x.ending_year_ < y;
|
|
ferencd@0
|
1201 }
|
|
ferencd@0
|
1202
|
|
ferencd@0
|
1203 bool
|
|
ferencd@0
|
1204 detail::operator==(const date::year& x, const Rule& y)
|
|
ferencd@0
|
1205 {
|
|
ferencd@0
|
1206 return y.starting_year_ <= x && x <= y.ending_year_;
|
|
ferencd@0
|
1207 }
|
|
ferencd@0
|
1208
|
|
ferencd@0
|
1209 bool
|
|
ferencd@0
|
1210 detail::operator<(const date::year& x, const Rule& y)
|
|
ferencd@0
|
1211 {
|
|
ferencd@0
|
1212 return x < y.starting_year_;
|
|
ferencd@0
|
1213 }
|
|
ferencd@0
|
1214
|
|
ferencd@0
|
1215 bool
|
|
ferencd@0
|
1216 detail::operator==(const Rule& x, const std::string& y)
|
|
ferencd@0
|
1217 {
|
|
ferencd@0
|
1218 return x.name() == y;
|
|
ferencd@0
|
1219 }
|
|
ferencd@0
|
1220
|
|
ferencd@0
|
1221 bool
|
|
ferencd@0
|
1222 detail::operator<(const Rule& x, const std::string& y)
|
|
ferencd@0
|
1223 {
|
|
ferencd@0
|
1224 return x.name() < y;
|
|
ferencd@0
|
1225 }
|
|
ferencd@0
|
1226
|
|
ferencd@0
|
1227 bool
|
|
ferencd@0
|
1228 detail::operator==(const std::string& x, const Rule& y)
|
|
ferencd@0
|
1229 {
|
|
ferencd@0
|
1230 return y.name() == x;
|
|
ferencd@0
|
1231 }
|
|
ferencd@0
|
1232
|
|
ferencd@0
|
1233 bool
|
|
ferencd@0
|
1234 detail::operator<(const std::string& x, const Rule& y)
|
|
ferencd@0
|
1235 {
|
|
ferencd@0
|
1236 return x < y.name();
|
|
ferencd@0
|
1237 }
|
|
ferencd@0
|
1238
|
|
ferencd@0
|
1239 std::ostream&
|
|
ferencd@0
|
1240 detail::operator<<(std::ostream& os, const Rule& r)
|
|
ferencd@0
|
1241 {
|
|
ferencd@0
|
1242 using namespace date;
|
|
ferencd@0
|
1243 using namespace std::chrono;
|
|
ferencd@0
|
1244 detail::save_ostream<char> _(os);
|
|
ferencd@0
|
1245 os.fill(' ');
|
|
ferencd@0
|
1246 os.flags(std::ios::dec | std::ios::left);
|
|
ferencd@0
|
1247 os.width(15);
|
|
ferencd@0
|
1248 os << r.name_;
|
|
ferencd@0
|
1249 os << r.starting_year_ << " " << r.ending_year_ << " ";
|
|
ferencd@0
|
1250 os << r.starting_at_;
|
|
ferencd@0
|
1251 if (r.save_ >= minutes{0})
|
|
ferencd@0
|
1252 os << ' ';
|
|
ferencd@0
|
1253 os << date::make_time(r.save_) << " ";
|
|
ferencd@0
|
1254 os << r.abbrev_;
|
|
ferencd@0
|
1255 return os;
|
|
ferencd@0
|
1256 }
|
|
ferencd@0
|
1257
|
|
ferencd@0
|
1258 date::day
|
|
ferencd@0
|
1259 detail::Rule::day() const
|
|
ferencd@0
|
1260 {
|
|
ferencd@0
|
1261 return starting_at_.day();
|
|
ferencd@0
|
1262 }
|
|
ferencd@0
|
1263
|
|
ferencd@0
|
1264 date::month
|
|
ferencd@0
|
1265 detail::Rule::month() const
|
|
ferencd@0
|
1266 {
|
|
ferencd@0
|
1267 return starting_at_.month();
|
|
ferencd@0
|
1268 }
|
|
ferencd@0
|
1269
|
|
ferencd@0
|
1270 struct find_rule_by_name
|
|
ferencd@0
|
1271 {
|
|
ferencd@0
|
1272 bool operator()(const Rule& x, const std::string& nm) const
|
|
ferencd@0
|
1273 {
|
|
ferencd@0
|
1274 return x.name() < nm;
|
|
ferencd@0
|
1275 }
|
|
ferencd@0
|
1276
|
|
ferencd@0
|
1277 bool operator()(const std::string& nm, const Rule& x) const
|
|
ferencd@0
|
1278 {
|
|
ferencd@0
|
1279 return nm < x.name();
|
|
ferencd@0
|
1280 }
|
|
ferencd@0
|
1281 };
|
|
ferencd@0
|
1282
|
|
ferencd@0
|
1283 bool
|
|
ferencd@0
|
1284 detail::Rule::overlaps(const Rule& x, const Rule& y)
|
|
ferencd@0
|
1285 {
|
|
ferencd@0
|
1286 // assume x.starting_year_ <= y.starting_year_;
|
|
ferencd@0
|
1287 if (!(x.starting_year_ <= y.starting_year_))
|
|
ferencd@0
|
1288 {
|
|
ferencd@0
|
1289 std::cerr << x << '\n';
|
|
ferencd@0
|
1290 std::cerr << y << '\n';
|
|
ferencd@0
|
1291 assert(x.starting_year_ <= y.starting_year_);
|
|
ferencd@0
|
1292 }
|
|
ferencd@0
|
1293 if (y.starting_year_ > x.ending_year_)
|
|
ferencd@0
|
1294 return false;
|
|
ferencd@0
|
1295 return !(x.starting_year_ == y.starting_year_ && x.ending_year_ == y.ending_year_);
|
|
ferencd@0
|
1296 }
|
|
ferencd@0
|
1297
|
|
ferencd@0
|
1298 void
|
|
ferencd@0
|
1299 detail::Rule::split(std::vector<Rule>& rules, std::size_t i, std::size_t k, std::size_t& e)
|
|
ferencd@0
|
1300 {
|
|
ferencd@0
|
1301 using namespace date;
|
|
ferencd@0
|
1302 using difference_type = std::vector<Rule>::iterator::difference_type;
|
|
ferencd@0
|
1303 // rules[i].starting_year_ <= rules[k].starting_year_ &&
|
|
ferencd@0
|
1304 // rules[i].ending_year_ >= rules[k].starting_year_ &&
|
|
ferencd@0
|
1305 // (rules[i].starting_year_ != rules[k].starting_year_ ||
|
|
ferencd@0
|
1306 // rules[i].ending_year_ != rules[k].ending_year_)
|
|
ferencd@0
|
1307 assert(rules[i].starting_year_ <= rules[k].starting_year_ &&
|
|
ferencd@0
|
1308 rules[i].ending_year_ >= rules[k].starting_year_ &&
|
|
ferencd@0
|
1309 (rules[i].starting_year_ != rules[k].starting_year_ ||
|
|
ferencd@0
|
1310 rules[i].ending_year_ != rules[k].ending_year_));
|
|
ferencd@0
|
1311 if (rules[i].starting_year_ == rules[k].starting_year_)
|
|
ferencd@0
|
1312 {
|
|
ferencd@0
|
1313 if (rules[k].ending_year_ < rules[i].ending_year_)
|
|
ferencd@0
|
1314 {
|
|
ferencd@0
|
1315 rules.insert(rules.begin() + static_cast<difference_type>(k+1),
|
|
ferencd@0
|
1316 Rule(rules[i], rules[k].ending_year_ + years{1},
|
|
ferencd@0
|
1317 std::move(rules[i].ending_year_)));
|
|
ferencd@0
|
1318 ++e;
|
|
ferencd@0
|
1319 rules[i].ending_year_ = rules[k].ending_year_;
|
|
ferencd@0
|
1320 }
|
|
ferencd@0
|
1321 else // rules[k].ending_year_ > rules[i].ending_year_
|
|
ferencd@0
|
1322 {
|
|
ferencd@0
|
1323 rules.insert(rules.begin() + static_cast<difference_type>(k+1),
|
|
ferencd@0
|
1324 Rule(rules[k], rules[i].ending_year_ + years{1},
|
|
ferencd@0
|
1325 std::move(rules[k].ending_year_)));
|
|
ferencd@0
|
1326 ++e;
|
|
ferencd@0
|
1327 rules[k].ending_year_ = rules[i].ending_year_;
|
|
ferencd@0
|
1328 }
|
|
ferencd@0
|
1329 }
|
|
ferencd@0
|
1330 else // rules[i].starting_year_ < rules[k].starting_year_
|
|
ferencd@0
|
1331 {
|
|
ferencd@0
|
1332 if (rules[k].ending_year_ < rules[i].ending_year_)
|
|
ferencd@0
|
1333 {
|
|
ferencd@0
|
1334 rules.insert(rules.begin() + static_cast<difference_type>(k),
|
|
ferencd@0
|
1335 Rule(rules[i], rules[k].starting_year_, rules[k].ending_year_));
|
|
ferencd@0
|
1336 ++k;
|
|
ferencd@0
|
1337 rules.insert(rules.begin() + static_cast<difference_type>(k+1),
|
|
ferencd@0
|
1338 Rule(rules[i], rules[k].ending_year_ + years{1},
|
|
ferencd@0
|
1339 std::move(rules[i].ending_year_)));
|
|
ferencd@0
|
1340 rules[i].ending_year_ = rules[k].starting_year_ - years{1};
|
|
ferencd@0
|
1341 e += 2;
|
|
ferencd@0
|
1342 }
|
|
ferencd@0
|
1343 else if (rules[k].ending_year_ > rules[i].ending_year_)
|
|
ferencd@0
|
1344 {
|
|
ferencd@0
|
1345 rules.insert(rules.begin() + static_cast<difference_type>(k),
|
|
ferencd@0
|
1346 Rule(rules[i], rules[k].starting_year_, rules[i].ending_year_));
|
|
ferencd@0
|
1347 ++k;
|
|
ferencd@0
|
1348 rules.insert(rules.begin() + static_cast<difference_type>(k+1),
|
|
ferencd@0
|
1349 Rule(rules[k], rules[i].ending_year_ + years{1},
|
|
ferencd@0
|
1350 std::move(rules[k].ending_year_)));
|
|
ferencd@0
|
1351 e += 2;
|
|
ferencd@0
|
1352 rules[k].ending_year_ = std::move(rules[i].ending_year_);
|
|
ferencd@0
|
1353 rules[i].ending_year_ = rules[k].starting_year_ - years{1};
|
|
ferencd@0
|
1354 }
|
|
ferencd@0
|
1355 else // rules[k].ending_year_ == rules[i].ending_year_
|
|
ferencd@0
|
1356 {
|
|
ferencd@0
|
1357 rules.insert(rules.begin() + static_cast<difference_type>(k),
|
|
ferencd@0
|
1358 Rule(rules[i], rules[k].starting_year_,
|
|
ferencd@0
|
1359 std::move(rules[i].ending_year_)));
|
|
ferencd@0
|
1360 ++k;
|
|
ferencd@0
|
1361 ++e;
|
|
ferencd@0
|
1362 rules[i].ending_year_ = rules[k].starting_year_ - years{1};
|
|
ferencd@0
|
1363 }
|
|
ferencd@0
|
1364 }
|
|
ferencd@0
|
1365 }
|
|
ferencd@0
|
1366
|
|
ferencd@0
|
1367 void
|
|
ferencd@0
|
1368 detail::Rule::split_overlaps(std::vector<Rule>& rules, std::size_t i, std::size_t& e)
|
|
ferencd@0
|
1369 {
|
|
ferencd@0
|
1370 using difference_type = std::vector<Rule>::iterator::difference_type;
|
|
ferencd@0
|
1371 auto j = i;
|
|
ferencd@0
|
1372 for (; i + 1 < e; ++i)
|
|
ferencd@0
|
1373 {
|
|
ferencd@0
|
1374 for (auto k = i + 1; k < e; ++k)
|
|
ferencd@0
|
1375 {
|
|
ferencd@0
|
1376 if (overlaps(rules[i], rules[k]))
|
|
ferencd@0
|
1377 {
|
|
ferencd@0
|
1378 split(rules, i, k, e);
|
|
ferencd@0
|
1379 std::sort(rules.begin() + static_cast<difference_type>(i),
|
|
ferencd@0
|
1380 rules.begin() + static_cast<difference_type>(e));
|
|
ferencd@0
|
1381 }
|
|
ferencd@0
|
1382 }
|
|
ferencd@0
|
1383 }
|
|
ferencd@0
|
1384 for (; j < e; ++j)
|
|
ferencd@0
|
1385 {
|
|
ferencd@0
|
1386 if (rules[j].starting_year() == rules[j].ending_year())
|
|
ferencd@0
|
1387 rules[j].starting_at_.canonicalize(rules[j].starting_year());
|
|
ferencd@0
|
1388 }
|
|
ferencd@0
|
1389 }
|
|
ferencd@0
|
1390
|
|
ferencd@0
|
1391 void
|
|
ferencd@0
|
1392 detail::Rule::split_overlaps(std::vector<Rule>& rules)
|
|
ferencd@0
|
1393 {
|
|
ferencd@0
|
1394 using difference_type = std::vector<Rule>::iterator::difference_type;
|
|
ferencd@0
|
1395 for (std::size_t i = 0; i < rules.size();)
|
|
ferencd@0
|
1396 {
|
|
ferencd@0
|
1397 auto e = static_cast<std::size_t>(std::upper_bound(
|
|
ferencd@0
|
1398 rules.cbegin()+static_cast<difference_type>(i), rules.cend(), rules[i].name(),
|
|
ferencd@0
|
1399 [](const std::string& nm, const Rule& x)
|
|
ferencd@0
|
1400 {
|
|
ferencd@0
|
1401 return nm < x.name();
|
|
ferencd@0
|
1402 }) - rules.cbegin());
|
|
ferencd@0
|
1403 split_overlaps(rules, i, e);
|
|
ferencd@0
|
1404 auto first_rule = rules.begin() + static_cast<difference_type>(i);
|
|
ferencd@0
|
1405 auto last_rule = rules.begin() + static_cast<difference_type>(e);
|
|
ferencd@0
|
1406 auto t = std::lower_bound(first_rule, last_rule, min_year);
|
|
ferencd@0
|
1407 if (t > first_rule+1)
|
|
ferencd@0
|
1408 {
|
|
ferencd@0
|
1409 if (t == last_rule || t->starting_year() >= min_year)
|
|
ferencd@0
|
1410 --t;
|
|
ferencd@0
|
1411 auto d = static_cast<std::size_t>(t - first_rule);
|
|
ferencd@0
|
1412 rules.erase(first_rule, t);
|
|
ferencd@0
|
1413 e -= d;
|
|
ferencd@0
|
1414 }
|
|
ferencd@0
|
1415 first_rule = rules.begin() + static_cast<difference_type>(i);
|
|
ferencd@0
|
1416 last_rule = rules.begin() + static_cast<difference_type>(e);
|
|
ferencd@0
|
1417 t = std::upper_bound(first_rule, last_rule, max_year);
|
|
ferencd@0
|
1418 if (t != last_rule)
|
|
ferencd@0
|
1419 {
|
|
ferencd@0
|
1420 auto d = static_cast<std::size_t>(last_rule - t);
|
|
ferencd@0
|
1421 rules.erase(t, last_rule);
|
|
ferencd@0
|
1422 e -= d;
|
|
ferencd@0
|
1423 }
|
|
ferencd@0
|
1424 i = e;
|
|
ferencd@0
|
1425 }
|
|
ferencd@0
|
1426 rules.shrink_to_fit();
|
|
ferencd@0
|
1427 }
|
|
ferencd@0
|
1428
|
|
ferencd@0
|
1429 // Find the rule that comes chronologically before Rule r. For multi-year rules,
|
|
ferencd@0
|
1430 // y specifies which rules in r. For single year rules, y is assumed to be equal
|
|
ferencd@0
|
1431 // to the year specified by r.
|
|
ferencd@0
|
1432 // Returns a pointer to the chronologically previous rule, and the year within
|
|
ferencd@0
|
1433 // that rule. If there is no previous rule, returns nullptr and year::min().
|
|
ferencd@0
|
1434 // Preconditions:
|
|
ferencd@0
|
1435 // r->starting_year() <= y && y <= r->ending_year()
|
|
ferencd@0
|
1436 static
|
|
ferencd@0
|
1437 std::pair<const Rule*, date::year>
|
|
ferencd@0
|
1438 find_previous_rule(const Rule* r, date::year y)
|
|
ferencd@0
|
1439 {
|
|
ferencd@0
|
1440 using namespace date;
|
|
ferencd@0
|
1441 auto const& rules = get_tzdb().rules;
|
|
ferencd@0
|
1442 if (y == r->starting_year())
|
|
ferencd@0
|
1443 {
|
|
ferencd@0
|
1444 if (r == &rules.front() || r->name() != r[-1].name())
|
|
ferencd@0
|
1445 std::terminate(); // never called with first rule
|
|
ferencd@0
|
1446 --r;
|
|
ferencd@0
|
1447 if (y == r->starting_year())
|
|
ferencd@0
|
1448 return {r, y};
|
|
ferencd@0
|
1449 return {r, r->ending_year()};
|
|
ferencd@0
|
1450 }
|
|
ferencd@0
|
1451 if (r == &rules.front() || r->name() != r[-1].name() ||
|
|
ferencd@0
|
1452 r[-1].starting_year() < r->starting_year())
|
|
ferencd@0
|
1453 {
|
|
ferencd@0
|
1454 while (r < &rules.back() && r->name() == r[1].name() &&
|
|
ferencd@0
|
1455 r->starting_year() == r[1].starting_year())
|
|
ferencd@0
|
1456 ++r;
|
|
ferencd@0
|
1457 return {r, --y};
|
|
ferencd@0
|
1458 }
|
|
ferencd@0
|
1459 --r;
|
|
ferencd@0
|
1460 return {r, y};
|
|
ferencd@0
|
1461 }
|
|
ferencd@0
|
1462
|
|
ferencd@0
|
1463 // Find the rule that comes chronologically after Rule r. For multi-year rules,
|
|
ferencd@0
|
1464 // y specifies which rules in r. For single year rules, y is assumed to be equal
|
|
ferencd@0
|
1465 // to the year specified by r.
|
|
ferencd@0
|
1466 // Returns a pointer to the chronologically next rule, and the year within
|
|
ferencd@0
|
1467 // that rule. If there is no next rule, return a pointer to a defaulted rule
|
|
ferencd@0
|
1468 // and y+1.
|
|
ferencd@0
|
1469 // Preconditions:
|
|
ferencd@0
|
1470 // first <= r && r < last && r->starting_year() <= y && y <= r->ending_year()
|
|
ferencd@0
|
1471 // [first, last) all have the same name
|
|
ferencd@0
|
1472 static
|
|
ferencd@0
|
1473 std::pair<const Rule*, date::year>
|
|
ferencd@0
|
1474 find_next_rule(const Rule* first_rule, const Rule* last_rule, const Rule* r, date::year y)
|
|
ferencd@0
|
1475 {
|
|
ferencd@0
|
1476 using namespace date;
|
|
ferencd@0
|
1477 if (y == r->ending_year())
|
|
ferencd@0
|
1478 {
|
|
ferencd@0
|
1479 if (r == last_rule-1)
|
|
ferencd@0
|
1480 return {nullptr, year::max()};
|
|
ferencd@0
|
1481 ++r;
|
|
ferencd@0
|
1482 if (y == r->ending_year())
|
|
ferencd@0
|
1483 return {r, y};
|
|
ferencd@0
|
1484 return {r, r->starting_year()};
|
|
ferencd@0
|
1485 }
|
|
ferencd@0
|
1486 if (r == last_rule-1 || r->ending_year() < r[1].ending_year())
|
|
ferencd@0
|
1487 {
|
|
ferencd@0
|
1488 while (r > first_rule && r->starting_year() == r[-1].starting_year())
|
|
ferencd@0
|
1489 --r;
|
|
ferencd@0
|
1490 return {r, ++y};
|
|
ferencd@0
|
1491 }
|
|
ferencd@0
|
1492 ++r;
|
|
ferencd@0
|
1493 return {r, y};
|
|
ferencd@0
|
1494 }
|
|
ferencd@0
|
1495
|
|
ferencd@0
|
1496 // Find the rule that comes chronologically after Rule r. For multi-year rules,
|
|
ferencd@0
|
1497 // y specifies which rules in r. For single year rules, y is assumed to be equal
|
|
ferencd@0
|
1498 // to the year specified by r.
|
|
ferencd@0
|
1499 // Returns a pointer to the chronologically next rule, and the year within
|
|
ferencd@0
|
1500 // that rule. If there is no next rule, return nullptr and year::max().
|
|
ferencd@0
|
1501 // Preconditions:
|
|
ferencd@0
|
1502 // r->starting_year() <= y && y <= r->ending_year()
|
|
ferencd@0
|
1503 static
|
|
ferencd@0
|
1504 std::pair<const Rule*, date::year>
|
|
ferencd@0
|
1505 find_next_rule(const Rule* r, date::year y)
|
|
ferencd@0
|
1506 {
|
|
ferencd@0
|
1507 using namespace date;
|
|
ferencd@0
|
1508 auto const& rules = get_tzdb().rules;
|
|
ferencd@0
|
1509 if (y == r->ending_year())
|
|
ferencd@0
|
1510 {
|
|
ferencd@0
|
1511 if (r == &rules.back() || r->name() != r[1].name())
|
|
ferencd@0
|
1512 return {nullptr, year::max()};
|
|
ferencd@0
|
1513 ++r;
|
|
ferencd@0
|
1514 if (y == r->ending_year())
|
|
ferencd@0
|
1515 return {r, y};
|
|
ferencd@0
|
1516 return {r, r->starting_year()};
|
|
ferencd@0
|
1517 }
|
|
ferencd@0
|
1518 if (r == &rules.back() || r->name() != r[1].name() ||
|
|
ferencd@0
|
1519 r->ending_year() < r[1].ending_year())
|
|
ferencd@0
|
1520 {
|
|
ferencd@0
|
1521 while (r > &rules.front() && r->name() == r[-1].name() &&
|
|
ferencd@0
|
1522 r->starting_year() == r[-1].starting_year())
|
|
ferencd@0
|
1523 --r;
|
|
ferencd@0
|
1524 return {r, ++y};
|
|
ferencd@0
|
1525 }
|
|
ferencd@0
|
1526 ++r;
|
|
ferencd@0
|
1527 return {r, y};
|
|
ferencd@0
|
1528 }
|
|
ferencd@0
|
1529
|
|
ferencd@0
|
1530 static
|
|
ferencd@0
|
1531 const Rule*
|
|
ferencd@0
|
1532 find_first_std_rule(const std::pair<const Rule*, const Rule*>& eqr)
|
|
ferencd@0
|
1533 {
|
|
ferencd@0
|
1534 auto r = eqr.first;
|
|
ferencd@0
|
1535 auto ry = r->starting_year();
|
|
ferencd@0
|
1536 while (r->save() != std::chrono::minutes{0})
|
|
ferencd@0
|
1537 {
|
|
ferencd@0
|
1538 std::tie(r, ry) = find_next_rule(eqr.first, eqr.second, r, ry);
|
|
ferencd@0
|
1539 if (r == nullptr)
|
|
ferencd@0
|
1540 throw std::runtime_error("Could not find standard offset in rule "
|
|
ferencd@0
|
1541 + eqr.first->name());
|
|
ferencd@0
|
1542 }
|
|
ferencd@0
|
1543 return r;
|
|
ferencd@0
|
1544 }
|
|
ferencd@0
|
1545
|
|
ferencd@0
|
1546 static
|
|
ferencd@0
|
1547 std::pair<const Rule*, date::year>
|
|
ferencd@0
|
1548 find_rule_for_zone(const std::pair<const Rule*, const Rule*>& eqr,
|
|
ferencd@0
|
1549 const date::year& y, const std::chrono::seconds& offset,
|
|
ferencd@0
|
1550 const MonthDayTime& mdt)
|
|
ferencd@0
|
1551 {
|
|
ferencd@0
|
1552 assert(eqr.first != nullptr);
|
|
ferencd@0
|
1553 assert(eqr.second != nullptr);
|
|
ferencd@0
|
1554
|
|
ferencd@0
|
1555 using namespace std::chrono;
|
|
ferencd@0
|
1556 using namespace date;
|
|
ferencd@0
|
1557 auto r = eqr.first;
|
|
ferencd@0
|
1558 auto ry = r->starting_year();
|
|
ferencd@0
|
1559 auto prev_save = minutes{0};
|
|
ferencd@0
|
1560 auto prev_year = year::min();
|
|
ferencd@0
|
1561 const Rule* prev_rule = nullptr;
|
|
ferencd@0
|
1562 while (r != nullptr)
|
|
ferencd@0
|
1563 {
|
|
ferencd@0
|
1564 if (mdt.compare(y, r->mdt(), ry, offset, prev_save) <= 0)
|
|
ferencd@0
|
1565 break;
|
|
ferencd@0
|
1566 prev_rule = r;
|
|
ferencd@0
|
1567 prev_year = ry;
|
|
ferencd@0
|
1568 prev_save = prev_rule->save();
|
|
ferencd@0
|
1569 std::tie(r, ry) = find_next_rule(eqr.first, eqr.second, r, ry);
|
|
ferencd@0
|
1570 }
|
|
ferencd@0
|
1571 return {prev_rule, prev_year};
|
|
ferencd@0
|
1572 }
|
|
ferencd@0
|
1573
|
|
ferencd@0
|
1574 static
|
|
ferencd@0
|
1575 std::pair<const Rule*, date::year>
|
|
ferencd@0
|
1576 find_rule_for_zone(const std::pair<const Rule*, const Rule*>& eqr,
|
|
ferencd@0
|
1577 const sys_seconds& tp_utc,
|
|
ferencd@0
|
1578 const local_seconds& tp_std,
|
|
ferencd@0
|
1579 const local_seconds& tp_loc)
|
|
ferencd@0
|
1580 {
|
|
ferencd@0
|
1581 using namespace std::chrono;
|
|
ferencd@0
|
1582 using namespace date;
|
|
ferencd@0
|
1583 auto r = eqr.first;
|
|
ferencd@0
|
1584 auto ry = r->starting_year();
|
|
ferencd@0
|
1585 auto prev_save = minutes{0};
|
|
ferencd@0
|
1586 auto prev_year = year::min();
|
|
ferencd@0
|
1587 const Rule* prev_rule = nullptr;
|
|
ferencd@0
|
1588 while (r != nullptr)
|
|
ferencd@0
|
1589 {
|
|
ferencd@0
|
1590 bool found = false;
|
|
ferencd@0
|
1591 switch (r->mdt().zone())
|
|
ferencd@0
|
1592 {
|
|
ferencd@0
|
1593 case tz::utc:
|
|
ferencd@0
|
1594 found = tp_utc < r->mdt().to_time_point(ry);
|
|
ferencd@0
|
1595 break;
|
|
ferencd@0
|
1596 case tz::standard:
|
|
ferencd@0
|
1597 found = sys_seconds{tp_std.time_since_epoch()} < r->mdt().to_time_point(ry);
|
|
ferencd@0
|
1598 break;
|
|
ferencd@0
|
1599 case tz::local:
|
|
ferencd@0
|
1600 found = sys_seconds{tp_loc.time_since_epoch()} < r->mdt().to_time_point(ry);
|
|
ferencd@0
|
1601 break;
|
|
ferencd@0
|
1602 }
|
|
ferencd@0
|
1603 if (found)
|
|
ferencd@0
|
1604 break;
|
|
ferencd@0
|
1605 prev_rule = r;
|
|
ferencd@0
|
1606 prev_year = ry;
|
|
ferencd@0
|
1607 prev_save = prev_rule->save();
|
|
ferencd@0
|
1608 std::tie(r, ry) = find_next_rule(eqr.first, eqr.second, r, ry);
|
|
ferencd@0
|
1609 }
|
|
ferencd@0
|
1610 return {prev_rule, prev_year};
|
|
ferencd@0
|
1611 }
|
|
ferencd@0
|
1612
|
|
ferencd@0
|
1613 static
|
|
ferencd@0
|
1614 sys_info
|
|
ferencd@0
|
1615 find_rule(const std::pair<const Rule*, date::year>& first_rule,
|
|
ferencd@0
|
1616 const std::pair<const Rule*, date::year>& last_rule,
|
|
ferencd@0
|
1617 const date::year& y, const std::chrono::seconds& offset,
|
|
ferencd@0
|
1618 const MonthDayTime& mdt, const std::chrono::minutes& initial_save,
|
|
ferencd@0
|
1619 const std::string& initial_abbrev)
|
|
ferencd@0
|
1620 {
|
|
ferencd@0
|
1621 using namespace std::chrono;
|
|
ferencd@0
|
1622 using namespace date;
|
|
ferencd@0
|
1623 auto r = first_rule.first;
|
|
ferencd@0
|
1624 auto ry = first_rule.second;
|
|
ferencd@0
|
1625 sys_info x{sys_days(year::min()/min_day), sys_days(year::max()/max_day),
|
|
ferencd@0
|
1626 seconds{0}, initial_save, initial_abbrev};
|
|
ferencd@0
|
1627 while (r != nullptr)
|
|
ferencd@0
|
1628 {
|
|
ferencd@0
|
1629 auto tr = r->mdt().to_sys(ry, offset, x.save);
|
|
ferencd@0
|
1630 auto tx = mdt.to_sys(y, offset, x.save);
|
|
ferencd@0
|
1631 // Find last rule where tx >= tr
|
|
ferencd@0
|
1632 if (tx <= tr || (r == last_rule.first && ry == last_rule.second))
|
|
ferencd@0
|
1633 {
|
|
ferencd@0
|
1634 if (tx < tr && r == first_rule.first && ry == first_rule.second)
|
|
ferencd@0
|
1635 {
|
|
ferencd@0
|
1636 x.end = r->mdt().to_sys(ry, offset, x.save);
|
|
ferencd@0
|
1637 break;
|
|
ferencd@0
|
1638 }
|
|
ferencd@0
|
1639 if (tx < tr)
|
|
ferencd@0
|
1640 {
|
|
ferencd@0
|
1641 std::tie(r, ry) = find_previous_rule(r, ry); // can't return nullptr for r
|
|
ferencd@0
|
1642 assert(r != nullptr);
|
|
ferencd@0
|
1643 }
|
|
ferencd@0
|
1644 // r != nullptr && tx >= tr (if tr were to be recomputed)
|
|
ferencd@0
|
1645 auto prev_save = initial_save;
|
|
ferencd@0
|
1646 if (!(r == first_rule.first && ry == first_rule.second))
|
|
ferencd@0
|
1647 prev_save = find_previous_rule(r, ry).first->save();
|
|
ferencd@0
|
1648 x.begin = r->mdt().to_sys(ry, offset, prev_save);
|
|
ferencd@0
|
1649 x.save = r->save();
|
|
ferencd@0
|
1650 x.abbrev = r->abbrev();
|
|
ferencd@0
|
1651 if (!(r == last_rule.first && ry == last_rule.second))
|
|
ferencd@0
|
1652 {
|
|
ferencd@0
|
1653 std::tie(r, ry) = find_next_rule(r, ry); // can't return nullptr for r
|
|
ferencd@0
|
1654 assert(r != nullptr);
|
|
ferencd@0
|
1655 x.end = r->mdt().to_sys(ry, offset, x.save);
|
|
ferencd@0
|
1656 }
|
|
ferencd@0
|
1657 else
|
|
ferencd@0
|
1658 x.end = sys_days(year::max()/max_day);
|
|
ferencd@0
|
1659 break;
|
|
ferencd@0
|
1660 }
|
|
ferencd@0
|
1661 x.save = r->save();
|
|
ferencd@0
|
1662 std::tie(r, ry) = find_next_rule(r, ry); // Can't return nullptr for r
|
|
ferencd@0
|
1663 assert(r != nullptr);
|
|
ferencd@0
|
1664 }
|
|
ferencd@0
|
1665 return x;
|
|
ferencd@0
|
1666 }
|
|
ferencd@0
|
1667
|
|
ferencd@0
|
1668 // zonelet
|
|
ferencd@0
|
1669
|
|
ferencd@0
|
1670 detail::zonelet::~zonelet()
|
|
ferencd@0
|
1671 {
|
|
ferencd@0
|
1672 #if !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
|
ferencd@0
|
1673 using minutes = std::chrono::minutes;
|
|
ferencd@0
|
1674 using string = std::string;
|
|
ferencd@0
|
1675 if (tag_ == has_save)
|
|
ferencd@0
|
1676 u.save_.~minutes();
|
|
ferencd@0
|
1677 else
|
|
ferencd@0
|
1678 u.rule_.~string();
|
|
ferencd@0
|
1679 #endif
|
|
ferencd@0
|
1680 }
|
|
ferencd@0
|
1681
|
|
ferencd@0
|
1682 detail::zonelet::zonelet()
|
|
ferencd@0
|
1683 {
|
|
ferencd@0
|
1684 #if !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
|
ferencd@0
|
1685 ::new(&u.rule_) std::string();
|
|
ferencd@0
|
1686 #endif
|
|
ferencd@0
|
1687 }
|
|
ferencd@0
|
1688
|
|
ferencd@0
|
1689 detail::zonelet::zonelet(const zonelet& i)
|
|
ferencd@0
|
1690 : gmtoff_(i.gmtoff_)
|
|
ferencd@0
|
1691 , tag_(i.tag_)
|
|
ferencd@0
|
1692 , format_(i.format_)
|
|
ferencd@0
|
1693 , until_year_(i.until_year_)
|
|
ferencd@0
|
1694 , until_date_(i.until_date_)
|
|
ferencd@0
|
1695 , until_utc_(i.until_utc_)
|
|
ferencd@0
|
1696 , until_std_(i.until_std_)
|
|
ferencd@0
|
1697 , until_loc_(i.until_loc_)
|
|
ferencd@0
|
1698 , initial_save_(i.initial_save_)
|
|
ferencd@0
|
1699 , initial_abbrev_(i.initial_abbrev_)
|
|
ferencd@0
|
1700 , first_rule_(i.first_rule_)
|
|
ferencd@0
|
1701 , last_rule_(i.last_rule_)
|
|
ferencd@0
|
1702 {
|
|
ferencd@0
|
1703 #if !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
|
ferencd@0
|
1704 if (tag_ == has_save)
|
|
ferencd@0
|
1705 ::new(&u.save_) std::chrono::minutes(i.u.save_);
|
|
ferencd@0
|
1706 else
|
|
ferencd@0
|
1707 ::new(&u.rule_) std::string(i.u.rule_);
|
|
ferencd@0
|
1708 #else
|
|
ferencd@0
|
1709 if (tag_ == has_save)
|
|
ferencd@0
|
1710 u.save_ = i.u.save_;
|
|
ferencd@0
|
1711 else
|
|
ferencd@0
|
1712 u.rule_ = i.u.rule_;
|
|
ferencd@0
|
1713 #endif
|
|
ferencd@0
|
1714 }
|
|
ferencd@0
|
1715
|
|
ferencd@0
|
1716 #endif // !USE_OS_TZDB
|
|
ferencd@0
|
1717
|
|
ferencd@0
|
1718 // time_zone
|
|
ferencd@0
|
1719
|
|
ferencd@0
|
1720 #if USE_OS_TZDB
|
|
ferencd@0
|
1721
|
|
ferencd@0
|
1722 time_zone::time_zone(const std::string& s, detail::undocumented)
|
|
ferencd@0
|
1723 : name_(s)
|
|
ferencd@0
|
1724 , adjusted_(new std::once_flag{})
|
|
ferencd@0
|
1725 {
|
|
ferencd@0
|
1726 }
|
|
ferencd@0
|
1727
|
|
ferencd@0
|
1728 enum class endian
|
|
ferencd@0
|
1729 {
|
|
ferencd@0
|
1730 native = __BYTE_ORDER__,
|
|
ferencd@0
|
1731 little = __ORDER_LITTLE_ENDIAN__,
|
|
ferencd@0
|
1732 big = __ORDER_BIG_ENDIAN__
|
|
ferencd@0
|
1733 };
|
|
ferencd@0
|
1734
|
|
ferencd@0
|
1735 static
|
|
ferencd@0
|
1736 inline
|
|
ferencd@0
|
1737 std::uint32_t
|
|
ferencd@0
|
1738 reverse_bytes(std::uint32_t i)
|
|
ferencd@0
|
1739 {
|
|
ferencd@0
|
1740 return
|
|
ferencd@0
|
1741 (i & 0xff000000u) >> 24 |
|
|
ferencd@0
|
1742 (i & 0x00ff0000u) >> 8 |
|
|
ferencd@0
|
1743 (i & 0x0000ff00u) << 8 |
|
|
ferencd@0
|
1744 (i & 0x000000ffu) << 24;
|
|
ferencd@0
|
1745 }
|
|
ferencd@0
|
1746
|
|
ferencd@0
|
1747 static
|
|
ferencd@0
|
1748 inline
|
|
ferencd@0
|
1749 std::uint64_t
|
|
ferencd@0
|
1750 reverse_bytes(std::uint64_t i)
|
|
ferencd@0
|
1751 {
|
|
ferencd@0
|
1752 return
|
|
ferencd@0
|
1753 (i & 0xff00000000000000ull) >> 56 |
|
|
ferencd@0
|
1754 (i & 0x00ff000000000000ull) >> 40 |
|
|
ferencd@0
|
1755 (i & 0x0000ff0000000000ull) >> 24 |
|
|
ferencd@0
|
1756 (i & 0x000000ff00000000ull) >> 8 |
|
|
ferencd@0
|
1757 (i & 0x00000000ff000000ull) << 8 |
|
|
ferencd@0
|
1758 (i & 0x0000000000ff0000ull) << 24 |
|
|
ferencd@0
|
1759 (i & 0x000000000000ff00ull) << 40 |
|
|
ferencd@0
|
1760 (i & 0x00000000000000ffull) << 56;
|
|
ferencd@0
|
1761 }
|
|
ferencd@0
|
1762
|
|
ferencd@0
|
1763 template <class T>
|
|
ferencd@0
|
1764 static
|
|
ferencd@0
|
1765 inline
|
|
ferencd@0
|
1766 void
|
|
ferencd@0
|
1767 maybe_reverse_bytes(T&, std::false_type)
|
|
ferencd@0
|
1768 {
|
|
ferencd@0
|
1769 }
|
|
ferencd@0
|
1770
|
|
ferencd@0
|
1771 static
|
|
ferencd@0
|
1772 inline
|
|
ferencd@0
|
1773 void
|
|
ferencd@0
|
1774 maybe_reverse_bytes(std::int32_t& t, std::true_type)
|
|
ferencd@0
|
1775 {
|
|
ferencd@0
|
1776 t = static_cast<std::int32_t>(reverse_bytes(static_cast<std::uint32_t>(t)));
|
|
ferencd@0
|
1777 }
|
|
ferencd@0
|
1778
|
|
ferencd@0
|
1779 static
|
|
ferencd@0
|
1780 inline
|
|
ferencd@0
|
1781 void
|
|
ferencd@0
|
1782 maybe_reverse_bytes(std::int64_t& t, std::true_type)
|
|
ferencd@0
|
1783 {
|
|
ferencd@0
|
1784 t = static_cast<std::int64_t>(reverse_bytes(static_cast<std::uint64_t>(t)));
|
|
ferencd@0
|
1785 }
|
|
ferencd@0
|
1786
|
|
ferencd@0
|
1787 template <class T>
|
|
ferencd@0
|
1788 static
|
|
ferencd@0
|
1789 inline
|
|
ferencd@0
|
1790 void
|
|
ferencd@0
|
1791 maybe_reverse_bytes(T& t)
|
|
ferencd@0
|
1792 {
|
|
ferencd@0
|
1793 maybe_reverse_bytes(t, std::integral_constant<bool,
|
|
ferencd@0
|
1794 endian::native == endian::little>{});
|
|
ferencd@0
|
1795 }
|
|
ferencd@0
|
1796
|
|
ferencd@0
|
1797 static
|
|
ferencd@0
|
1798 void
|
|
ferencd@0
|
1799 load_header(std::istream& inf)
|
|
ferencd@0
|
1800 {
|
|
ferencd@0
|
1801 // Read TZif
|
|
ferencd@0
|
1802 auto t = inf.get();
|
|
ferencd@0
|
1803 auto z = inf.get();
|
|
ferencd@0
|
1804 auto i = inf.get();
|
|
ferencd@0
|
1805 auto f = inf.get();
|
|
ferencd@0
|
1806 #ifndef NDEBUG
|
|
ferencd@0
|
1807 assert(t == 'T');
|
|
ferencd@0
|
1808 assert(z == 'Z');
|
|
ferencd@0
|
1809 assert(i == 'i');
|
|
ferencd@0
|
1810 assert(f == 'f');
|
|
ferencd@0
|
1811 #else
|
|
ferencd@0
|
1812 (void)t;
|
|
ferencd@0
|
1813 (void)z;
|
|
ferencd@0
|
1814 (void)i;
|
|
ferencd@0
|
1815 (void)f;
|
|
ferencd@0
|
1816 #endif
|
|
ferencd@0
|
1817 }
|
|
ferencd@0
|
1818
|
|
ferencd@0
|
1819 static
|
|
ferencd@0
|
1820 unsigned char
|
|
ferencd@0
|
1821 load_version(std::istream& inf)
|
|
ferencd@0
|
1822 {
|
|
ferencd@0
|
1823 // Read version
|
|
ferencd@0
|
1824 auto v = inf.get();
|
|
ferencd@0
|
1825 assert(v != EOF);
|
|
ferencd@0
|
1826 return static_cast<unsigned char>(v);
|
|
ferencd@0
|
1827 }
|
|
ferencd@0
|
1828
|
|
ferencd@0
|
1829 static
|
|
ferencd@0
|
1830 void
|
|
ferencd@0
|
1831 skip_reserve(std::istream& inf)
|
|
ferencd@0
|
1832 {
|
|
ferencd@0
|
1833 inf.ignore(15);
|
|
ferencd@0
|
1834 }
|
|
ferencd@0
|
1835
|
|
ferencd@0
|
1836 static
|
|
ferencd@0
|
1837 void
|
|
ferencd@0
|
1838 load_counts(std::istream& inf,
|
|
ferencd@0
|
1839 std::int32_t& tzh_ttisgmtcnt, std::int32_t& tzh_ttisstdcnt,
|
|
ferencd@0
|
1840 std::int32_t& tzh_leapcnt, std::int32_t& tzh_timecnt,
|
|
ferencd@0
|
1841 std::int32_t& tzh_typecnt, std::int32_t& tzh_charcnt)
|
|
ferencd@0
|
1842 {
|
|
ferencd@0
|
1843 // Read counts;
|
|
ferencd@0
|
1844 inf.read(reinterpret_cast<char*>(&tzh_ttisgmtcnt), 4);
|
|
ferencd@0
|
1845 maybe_reverse_bytes(tzh_ttisgmtcnt);
|
|
ferencd@0
|
1846 inf.read(reinterpret_cast<char*>(&tzh_ttisstdcnt), 4);
|
|
ferencd@0
|
1847 maybe_reverse_bytes(tzh_ttisstdcnt);
|
|
ferencd@0
|
1848 inf.read(reinterpret_cast<char*>(&tzh_leapcnt), 4);
|
|
ferencd@0
|
1849 maybe_reverse_bytes(tzh_leapcnt);
|
|
ferencd@0
|
1850 inf.read(reinterpret_cast<char*>(&tzh_timecnt), 4);
|
|
ferencd@0
|
1851 maybe_reverse_bytes(tzh_timecnt);
|
|
ferencd@0
|
1852 inf.read(reinterpret_cast<char*>(&tzh_typecnt), 4);
|
|
ferencd@0
|
1853 maybe_reverse_bytes(tzh_typecnt);
|
|
ferencd@0
|
1854 inf.read(reinterpret_cast<char*>(&tzh_charcnt), 4);
|
|
ferencd@0
|
1855 maybe_reverse_bytes(tzh_charcnt);
|
|
ferencd@0
|
1856 }
|
|
ferencd@0
|
1857
|
|
ferencd@0
|
1858 template <class TimeType>
|
|
ferencd@0
|
1859 static
|
|
ferencd@0
|
1860 std::vector<detail::transition>
|
|
ferencd@0
|
1861 load_transitions(std::istream& inf, std::int32_t tzh_timecnt)
|
|
ferencd@0
|
1862 {
|
|
ferencd@0
|
1863 // Read transitions
|
|
ferencd@0
|
1864 using namespace std::chrono;
|
|
ferencd@0
|
1865 std::vector<detail::transition> transitions;
|
|
ferencd@0
|
1866 transitions.reserve(static_cast<unsigned>(tzh_timecnt));
|
|
ferencd@0
|
1867 for (std::int32_t i = 0; i < tzh_timecnt; ++i)
|
|
ferencd@0
|
1868 {
|
|
ferencd@0
|
1869 TimeType t;
|
|
ferencd@0
|
1870 inf.read(reinterpret_cast<char*>(&t), sizeof(t));
|
|
ferencd@0
|
1871 maybe_reverse_bytes(t);
|
|
ferencd@0
|
1872 transitions.emplace_back(sys_seconds{seconds{t}});
|
|
ferencd@0
|
1873 if (transitions.back().timepoint < min_seconds)
|
|
ferencd@0
|
1874 transitions.back().timepoint = min_seconds;
|
|
ferencd@0
|
1875 }
|
|
ferencd@0
|
1876 return transitions;
|
|
ferencd@0
|
1877 }
|
|
ferencd@0
|
1878
|
|
ferencd@0
|
1879 static
|
|
ferencd@0
|
1880 std::vector<std::uint8_t>
|
|
ferencd@0
|
1881 load_indices(std::istream& inf, std::int32_t tzh_timecnt)
|
|
ferencd@0
|
1882 {
|
|
ferencd@0
|
1883 // Read indices
|
|
ferencd@0
|
1884 std::vector<std::uint8_t> indices;
|
|
ferencd@0
|
1885 indices.reserve(static_cast<unsigned>(tzh_timecnt));
|
|
ferencd@0
|
1886 for (std::int32_t i = 0; i < tzh_timecnt; ++i)
|
|
ferencd@0
|
1887 {
|
|
ferencd@0
|
1888 std::uint8_t t;
|
|
ferencd@0
|
1889 inf.read(reinterpret_cast<char*>(&t), sizeof(t));
|
|
ferencd@0
|
1890 indices.emplace_back(t);
|
|
ferencd@0
|
1891 }
|
|
ferencd@0
|
1892 return indices;
|
|
ferencd@0
|
1893 }
|
|
ferencd@0
|
1894
|
|
ferencd@0
|
1895 static
|
|
ferencd@0
|
1896 std::vector<ttinfo>
|
|
ferencd@0
|
1897 load_ttinfo(std::istream& inf, std::int32_t tzh_typecnt)
|
|
ferencd@0
|
1898 {
|
|
ferencd@0
|
1899 // Read ttinfo
|
|
ferencd@0
|
1900 std::vector<ttinfo> ttinfos;
|
|
ferencd@0
|
1901 ttinfos.reserve(static_cast<unsigned>(tzh_typecnt));
|
|
ferencd@0
|
1902 for (std::int32_t i = 0; i < tzh_typecnt; ++i)
|
|
ferencd@0
|
1903 {
|
|
ferencd@0
|
1904 ttinfo t;
|
|
ferencd@0
|
1905 inf.read(reinterpret_cast<char*>(&t), 6);
|
|
ferencd@0
|
1906 maybe_reverse_bytes(t.tt_gmtoff);
|
|
ferencd@0
|
1907 ttinfos.emplace_back(t);
|
|
ferencd@0
|
1908 }
|
|
ferencd@0
|
1909 return ttinfos;
|
|
ferencd@0
|
1910 }
|
|
ferencd@0
|
1911
|
|
ferencd@0
|
1912 static
|
|
ferencd@0
|
1913 std::string
|
|
ferencd@0
|
1914 load_abbreviations(std::istream& inf, std::int32_t tzh_charcnt)
|
|
ferencd@0
|
1915 {
|
|
ferencd@0
|
1916 // Read abbreviations
|
|
ferencd@0
|
1917 std::string abbrev;
|
|
ferencd@0
|
1918 abbrev.resize(static_cast<unsigned>(tzh_charcnt), '\0');
|
|
ferencd@0
|
1919 inf.read(&abbrev[0], tzh_charcnt);
|
|
ferencd@0
|
1920 return abbrev;
|
|
ferencd@0
|
1921 }
|
|
ferencd@0
|
1922
|
|
ferencd@0
|
1923 #if !MISSING_LEAP_SECONDS
|
|
ferencd@0
|
1924
|
|
ferencd@0
|
1925 template <class TimeType>
|
|
ferencd@0
|
1926 static
|
|
ferencd@0
|
1927 std::vector<leap>
|
|
ferencd@0
|
1928 load_leaps(std::istream& inf, std::int32_t tzh_leapcnt)
|
|
ferencd@0
|
1929 {
|
|
ferencd@0
|
1930 // Read tzh_leapcnt pairs
|
|
ferencd@0
|
1931 using namespace std::chrono;
|
|
ferencd@0
|
1932 std::vector<leap> leap_seconds;
|
|
ferencd@0
|
1933 leap_seconds.reserve(static_cast<std::size_t>(tzh_leapcnt));
|
|
ferencd@0
|
1934 for (std::int32_t i = 0; i < tzh_leapcnt; ++i)
|
|
ferencd@0
|
1935 {
|
|
ferencd@0
|
1936 TimeType t0;
|
|
ferencd@0
|
1937 std::int32_t t1;
|
|
ferencd@0
|
1938 inf.read(reinterpret_cast<char*>(&t0), sizeof(t0));
|
|
ferencd@0
|
1939 inf.read(reinterpret_cast<char*>(&t1), sizeof(t1));
|
|
ferencd@0
|
1940 maybe_reverse_bytes(t0);
|
|
ferencd@0
|
1941 maybe_reverse_bytes(t1);
|
|
ferencd@0
|
1942 leap_seconds.emplace_back(sys_seconds{seconds{t0 - (t1-1)}},
|
|
ferencd@0
|
1943 detail::undocumented{});
|
|
ferencd@0
|
1944 }
|
|
ferencd@0
|
1945 return leap_seconds;
|
|
ferencd@0
|
1946 }
|
|
ferencd@0
|
1947
|
|
ferencd@0
|
1948 template <class TimeType>
|
|
ferencd@0
|
1949 static
|
|
ferencd@0
|
1950 std::vector<leap>
|
|
ferencd@0
|
1951 load_leap_data(std::istream& inf,
|
|
ferencd@0
|
1952 std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt,
|
|
ferencd@0
|
1953 std::int32_t tzh_typecnt, std::int32_t tzh_charcnt)
|
|
ferencd@0
|
1954 {
|
|
ferencd@0
|
1955 inf.ignore(tzh_timecnt*static_cast<std::int32_t>(sizeof(TimeType)) + tzh_timecnt +
|
|
ferencd@0
|
1956 tzh_typecnt*6 + tzh_charcnt);
|
|
ferencd@0
|
1957 return load_leaps<TimeType>(inf, tzh_leapcnt);
|
|
ferencd@0
|
1958 }
|
|
ferencd@0
|
1959
|
|
ferencd@0
|
1960 static
|
|
ferencd@0
|
1961 std::vector<leap>
|
|
ferencd@0
|
1962 load_just_leaps(std::istream& inf)
|
|
ferencd@0
|
1963 {
|
|
ferencd@0
|
1964 // Read tzh_leapcnt pairs
|
|
ferencd@0
|
1965 using namespace std::chrono;
|
|
ferencd@0
|
1966 load_header(inf);
|
|
ferencd@0
|
1967 auto v = load_version(inf);
|
|
ferencd@0
|
1968 std::int32_t tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
|
|
ferencd@0
|
1969 tzh_timecnt, tzh_typecnt, tzh_charcnt;
|
|
ferencd@0
|
1970 skip_reserve(inf);
|
|
ferencd@0
|
1971 load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
|
|
ferencd@0
|
1972 tzh_timecnt, tzh_typecnt, tzh_charcnt);
|
|
ferencd@0
|
1973 if (v == 0)
|
|
ferencd@0
|
1974 return load_leap_data<int32_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt,
|
|
ferencd@0
|
1975 tzh_charcnt);
|
|
ferencd@0
|
1976 #if !defined(NDEBUG)
|
|
ferencd@0
|
1977 inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt +
|
|
ferencd@0
|
1978 tzh_ttisstdcnt + tzh_ttisgmtcnt);
|
|
ferencd@0
|
1979 load_header(inf);
|
|
ferencd@0
|
1980 auto v2 = load_version(inf);
|
|
ferencd@0
|
1981 assert(v == v2);
|
|
ferencd@0
|
1982 skip_reserve(inf);
|
|
ferencd@0
|
1983 #else // defined(NDEBUG)
|
|
ferencd@0
|
1984 inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt +
|
|
ferencd@0
|
1985 tzh_ttisstdcnt + tzh_ttisgmtcnt + (4+1+15));
|
|
ferencd@0
|
1986 #endif // defined(NDEBUG)
|
|
ferencd@0
|
1987 load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
|
|
ferencd@0
|
1988 tzh_timecnt, tzh_typecnt, tzh_charcnt);
|
|
ferencd@0
|
1989 return load_leap_data<int64_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt,
|
|
ferencd@0
|
1990 tzh_charcnt);
|
|
ferencd@0
|
1991 }
|
|
ferencd@0
|
1992
|
|
ferencd@0
|
1993 #endif // !MISSING_LEAP_SECONDS
|
|
ferencd@0
|
1994
|
|
ferencd@0
|
1995 template <class TimeType>
|
|
ferencd@0
|
1996 void
|
|
ferencd@0
|
1997 time_zone::load_data(std::istream& inf,
|
|
ferencd@0
|
1998 std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt,
|
|
ferencd@0
|
1999 std::int32_t tzh_typecnt, std::int32_t tzh_charcnt)
|
|
ferencd@0
|
2000 {
|
|
ferencd@0
|
2001 using namespace std::chrono;
|
|
ferencd@0
|
2002 transitions_ = load_transitions<TimeType>(inf, tzh_timecnt);
|
|
ferencd@0
|
2003 auto indices = load_indices(inf, tzh_timecnt);
|
|
ferencd@0
|
2004 auto infos = load_ttinfo(inf, tzh_typecnt);
|
|
ferencd@0
|
2005 auto abbrev = load_abbreviations(inf, tzh_charcnt);
|
|
ferencd@0
|
2006 #if !MISSING_LEAP_SECONDS
|
|
ferencd@0
|
2007 auto& leap_seconds = get_tzdb_list().front().leaps;
|
|
ferencd@0
|
2008 if (leap_seconds.empty() && tzh_leapcnt > 0)
|
|
ferencd@0
|
2009 leap_seconds = load_leaps<TimeType>(inf, tzh_leapcnt);
|
|
ferencd@0
|
2010 #endif
|
|
ferencd@0
|
2011 ttinfos_.reserve(infos.size());
|
|
ferencd@0
|
2012 for (auto& info : infos)
|
|
ferencd@0
|
2013 {
|
|
ferencd@0
|
2014 ttinfos_.push_back({seconds{info.tt_gmtoff},
|
|
ferencd@0
|
2015 abbrev.c_str() + info.tt_abbrind,
|
|
ferencd@0
|
2016 info.tt_isdst != 0});
|
|
ferencd@0
|
2017 }
|
|
ferencd@0
|
2018 auto i = 0u;
|
|
ferencd@0
|
2019 if (transitions_.empty() || transitions_.front().timepoint != min_seconds)
|
|
ferencd@0
|
2020 {
|
|
ferencd@0
|
2021 transitions_.emplace(transitions_.begin(), min_seconds);
|
|
ferencd@0
|
2022 auto tf = std::find_if(ttinfos_.begin(), ttinfos_.end(),
|
|
ferencd@0
|
2023 [](const expanded_ttinfo& ti)
|
|
ferencd@0
|
2024 {return ti.is_dst == 0;});
|
|
ferencd@0
|
2025 if (tf == ttinfos_.end())
|
|
ferencd@0
|
2026 tf = ttinfos_.begin();
|
|
ferencd@0
|
2027 transitions_[i].info = &*tf;
|
|
ferencd@0
|
2028 ++i;
|
|
ferencd@0
|
2029 }
|
|
ferencd@0
|
2030 for (auto j = 0u; i < transitions_.size(); ++i, ++j)
|
|
ferencd@0
|
2031 transitions_[i].info = ttinfos_.data() + indices[j];
|
|
ferencd@0
|
2032 }
|
|
ferencd@0
|
2033
|
|
ferencd@0
|
2034 void
|
|
ferencd@0
|
2035 time_zone::init_impl()
|
|
ferencd@0
|
2036 {
|
|
ferencd@0
|
2037 using namespace std;
|
|
ferencd@0
|
2038 using namespace std::chrono;
|
|
ferencd@0
|
2039 auto name = get_tz_dir() + ('/' + name_);
|
|
ferencd@0
|
2040 std::ifstream inf(name);
|
|
ferencd@0
|
2041 if (!inf.is_open())
|
|
ferencd@0
|
2042 throw std::runtime_error{"Unable to open " + name};
|
|
ferencd@0
|
2043 inf.exceptions(std::ios::failbit | std::ios::badbit);
|
|
ferencd@0
|
2044 load_header(inf);
|
|
ferencd@0
|
2045 auto v = load_version(inf);
|
|
ferencd@0
|
2046 std::int32_t tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
|
|
ferencd@0
|
2047 tzh_timecnt, tzh_typecnt, tzh_charcnt;
|
|
ferencd@0
|
2048 skip_reserve(inf);
|
|
ferencd@0
|
2049 load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
|
|
ferencd@0
|
2050 tzh_timecnt, tzh_typecnt, tzh_charcnt);
|
|
ferencd@0
|
2051 if (v == 0)
|
|
ferencd@0
|
2052 {
|
|
ferencd@0
|
2053 load_data<int32_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt, tzh_charcnt);
|
|
ferencd@0
|
2054 }
|
|
ferencd@0
|
2055 else
|
|
ferencd@0
|
2056 {
|
|
ferencd@0
|
2057 #if !defined(NDEBUG)
|
|
ferencd@0
|
2058 inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt +
|
|
ferencd@0
|
2059 tzh_ttisstdcnt + tzh_ttisgmtcnt);
|
|
ferencd@0
|
2060 load_header(inf);
|
|
ferencd@0
|
2061 auto v2 = load_version(inf);
|
|
ferencd@0
|
2062 assert(v == v2);
|
|
ferencd@0
|
2063 skip_reserve(inf);
|
|
ferencd@0
|
2064 #else // defined(NDEBUG)
|
|
ferencd@0
|
2065 inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt +
|
|
ferencd@0
|
2066 tzh_ttisstdcnt + tzh_ttisgmtcnt + (4+1+15));
|
|
ferencd@0
|
2067 #endif // defined(NDEBUG)
|
|
ferencd@0
|
2068 load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
|
|
ferencd@0
|
2069 tzh_timecnt, tzh_typecnt, tzh_charcnt);
|
|
ferencd@0
|
2070 load_data<int64_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt, tzh_charcnt);
|
|
ferencd@0
|
2071 }
|
|
ferencd@0
|
2072 #if !MISSING_LEAP_SECONDS
|
|
ferencd@0
|
2073 if (tzh_leapcnt > 0)
|
|
ferencd@0
|
2074 {
|
|
ferencd@0
|
2075 auto& leap_seconds = get_tzdb_list().front().leaps;
|
|
ferencd@0
|
2076 auto itr = leap_seconds.begin();
|
|
ferencd@0
|
2077 auto l = itr->date();
|
|
ferencd@0
|
2078 seconds leap_count{0};
|
|
ferencd@0
|
2079 for (auto t = std::upper_bound(transitions_.begin(), transitions_.end(), l,
|
|
ferencd@0
|
2080 [](const sys_seconds& x, const transition& ct)
|
|
ferencd@0
|
2081 {
|
|
ferencd@0
|
2082 return x < ct.timepoint;
|
|
ferencd@0
|
2083 });
|
|
ferencd@0
|
2084 t != transitions_.end(); ++t)
|
|
ferencd@0
|
2085 {
|
|
ferencd@0
|
2086 while (t->timepoint >= l)
|
|
ferencd@0
|
2087 {
|
|
ferencd@0
|
2088 ++leap_count;
|
|
ferencd@0
|
2089 if (++itr == leap_seconds.end())
|
|
ferencd@0
|
2090 l = sys_days(max_year/max_day);
|
|
ferencd@0
|
2091 else
|
|
ferencd@0
|
2092 l = itr->date() + leap_count;
|
|
ferencd@0
|
2093 }
|
|
ferencd@0
|
2094 t->timepoint -= leap_count;
|
|
ferencd@0
|
2095 }
|
|
ferencd@0
|
2096 }
|
|
ferencd@0
|
2097 #endif // !MISSING_LEAP_SECONDS
|
|
ferencd@0
|
2098 auto b = transitions_.begin();
|
|
ferencd@0
|
2099 auto i = transitions_.end();
|
|
ferencd@0
|
2100 if (i != b)
|
|
ferencd@0
|
2101 {
|
|
ferencd@0
|
2102 for (--i; i != b; --i)
|
|
ferencd@0
|
2103 {
|
|
ferencd@0
|
2104 if (i->info->offset == i[-1].info->offset &&
|
|
ferencd@0
|
2105 i->info->abbrev == i[-1].info->abbrev &&
|
|
ferencd@0
|
2106 i->info->is_dst == i[-1].info->is_dst)
|
|
ferencd@0
|
2107 i = transitions_.erase(i);
|
|
ferencd@0
|
2108 }
|
|
ferencd@0
|
2109 }
|
|
ferencd@0
|
2110 }
|
|
ferencd@0
|
2111
|
|
ferencd@0
|
2112 void
|
|
ferencd@0
|
2113 time_zone::init() const
|
|
ferencd@0
|
2114 {
|
|
ferencd@0
|
2115 std::call_once(*adjusted_, [this]() {const_cast<time_zone*>(this)->init_impl();});
|
|
ferencd@0
|
2116 }
|
|
ferencd@0
|
2117
|
|
ferencd@0
|
2118 sys_info
|
|
ferencd@0
|
2119 time_zone::load_sys_info(std::vector<detail::transition>::const_iterator i) const
|
|
ferencd@0
|
2120 {
|
|
ferencd@0
|
2121 using namespace std::chrono;
|
|
ferencd@0
|
2122 assert(!transitions_.empty());
|
|
ferencd@0
|
2123 assert(i != transitions_.begin());
|
|
ferencd@0
|
2124 sys_info r;
|
|
ferencd@0
|
2125 r.begin = i[-1].timepoint;
|
|
ferencd@0
|
2126 r.end = i != transitions_.end() ? i->timepoint :
|
|
ferencd@0
|
2127 sys_seconds(sys_days(year::max()/max_day));
|
|
ferencd@0
|
2128 r.offset = i[-1].info->offset;
|
|
ferencd@0
|
2129 r.save = i[-1].info->is_dst ? minutes{1} : minutes{0};
|
|
ferencd@0
|
2130 r.abbrev = i[-1].info->abbrev;
|
|
ferencd@0
|
2131 return r;
|
|
ferencd@0
|
2132 }
|
|
ferencd@0
|
2133
|
|
ferencd@0
|
2134 sys_info
|
|
ferencd@0
|
2135 time_zone::get_info_impl(sys_seconds tp) const
|
|
ferencd@0
|
2136 {
|
|
ferencd@0
|
2137 using namespace std;
|
|
ferencd@0
|
2138 init();
|
|
ferencd@0
|
2139 return load_sys_info(upper_bound(transitions_.begin(), transitions_.end(), tp,
|
|
ferencd@0
|
2140 [](const sys_seconds& x, const transition& t)
|
|
ferencd@0
|
2141 {
|
|
ferencd@0
|
2142 return x < t.timepoint;
|
|
ferencd@0
|
2143 }));
|
|
ferencd@0
|
2144 }
|
|
ferencd@0
|
2145
|
|
ferencd@0
|
2146 local_info
|
|
ferencd@0
|
2147 time_zone::get_info_impl(local_seconds tp) const
|
|
ferencd@0
|
2148 {
|
|
ferencd@0
|
2149 using namespace std::chrono;
|
|
ferencd@0
|
2150 init();
|
|
ferencd@0
|
2151 local_info i;
|
|
ferencd@0
|
2152 i.result = local_info::unique;
|
|
ferencd@0
|
2153 auto tr = upper_bound(transitions_.begin(), transitions_.end(), tp,
|
|
ferencd@0
|
2154 [](const local_seconds& x, const transition& t)
|
|
ferencd@0
|
2155 {
|
|
ferencd@0
|
2156 return sys_seconds{x.time_since_epoch()} -
|
|
ferencd@0
|
2157 t.info->offset < t.timepoint;
|
|
ferencd@0
|
2158 });
|
|
ferencd@0
|
2159 i.first = load_sys_info(tr);
|
|
ferencd@0
|
2160 auto tps = sys_seconds{(tp - i.first.offset).time_since_epoch()};
|
|
ferencd@0
|
2161 if (tps < i.first.begin + days{1} && tr != transitions_.begin())
|
|
ferencd@0
|
2162 {
|
|
ferencd@0
|
2163 i.second = load_sys_info(--tr);
|
|
ferencd@0
|
2164 tps = sys_seconds{(tp - i.second.offset).time_since_epoch()};
|
|
ferencd@0
|
2165 if (tps < i.second.end)
|
|
ferencd@0
|
2166 {
|
|
ferencd@0
|
2167 i.result = local_info::ambiguous;
|
|
ferencd@0
|
2168 std::swap(i.first, i.second);
|
|
ferencd@0
|
2169 }
|
|
ferencd@0
|
2170 else
|
|
ferencd@0
|
2171 {
|
|
ferencd@0
|
2172 i.second = {};
|
|
ferencd@0
|
2173 }
|
|
ferencd@0
|
2174 }
|
|
ferencd@0
|
2175 else if (tps >= i.first.end && tr != transitions_.end())
|
|
ferencd@0
|
2176 {
|
|
ferencd@0
|
2177 i.second = load_sys_info(++tr);
|
|
ferencd@0
|
2178 tps = sys_seconds{(tp - i.second.offset).time_since_epoch()};
|
|
ferencd@0
|
2179 if (tps < i.second.begin)
|
|
ferencd@0
|
2180 i.result = local_info::nonexistent;
|
|
ferencd@0
|
2181 else
|
|
ferencd@0
|
2182 i.second = {};
|
|
ferencd@0
|
2183 }
|
|
ferencd@0
|
2184 return i;
|
|
ferencd@0
|
2185 }
|
|
ferencd@0
|
2186
|
|
ferencd@0
|
2187 std::ostream&
|
|
ferencd@0
|
2188 operator<<(std::ostream& os, const time_zone& z)
|
|
ferencd@0
|
2189 {
|
|
ferencd@0
|
2190 using namespace std::chrono;
|
|
ferencd@0
|
2191 z.init();
|
|
ferencd@0
|
2192 os << z.name_ << '\n';
|
|
ferencd@0
|
2193 os << "Initially: ";
|
|
ferencd@0
|
2194 auto const& t = z.transitions_.front();
|
|
ferencd@0
|
2195 if (t.info->offset >= seconds{0})
|
|
ferencd@0
|
2196 os << '+';
|
|
ferencd@0
|
2197 os << make_time(t.info->offset);
|
|
ferencd@0
|
2198 if (t.info->is_dst > 0)
|
|
ferencd@0
|
2199 os << " daylight ";
|
|
ferencd@0
|
2200 else
|
|
ferencd@0
|
2201 os << " standard ";
|
|
ferencd@0
|
2202 os << t.info->abbrev << '\n';
|
|
ferencd@0
|
2203 for (auto i = std::next(z.transitions_.cbegin()); i < z.transitions_.cend(); ++i)
|
|
ferencd@0
|
2204 os << *i << '\n';
|
|
ferencd@0
|
2205 return os;
|
|
ferencd@0
|
2206 }
|
|
ferencd@0
|
2207
|
|
ferencd@0
|
2208 #if !MISSING_LEAP_SECONDS
|
|
ferencd@0
|
2209
|
|
ferencd@0
|
2210 leap::leap(const sys_seconds& s, detail::undocumented)
|
|
ferencd@0
|
2211 : date_(s)
|
|
ferencd@0
|
2212 {
|
|
ferencd@0
|
2213 }
|
|
ferencd@0
|
2214
|
|
ferencd@0
|
2215 #endif // !MISSING_LEAP_SECONDS
|
|
ferencd@0
|
2216
|
|
ferencd@0
|
2217 #else // !USE_OS_TZDB
|
|
ferencd@0
|
2218
|
|
ferencd@0
|
2219 time_zone::time_zone(const std::string& s, detail::undocumented)
|
|
ferencd@0
|
2220 : adjusted_(new std::once_flag{})
|
|
ferencd@0
|
2221 {
|
|
ferencd@0
|
2222 try
|
|
ferencd@0
|
2223 {
|
|
ferencd@0
|
2224 using namespace date;
|
|
ferencd@0
|
2225 std::istringstream in(s);
|
|
ferencd@0
|
2226 in.exceptions(std::ios::failbit | std::ios::badbit);
|
|
ferencd@0
|
2227 std::string word;
|
|
ferencd@0
|
2228 in >> word >> name_;
|
|
ferencd@0
|
2229 parse_info(in);
|
|
ferencd@0
|
2230 }
|
|
ferencd@0
|
2231 catch (...)
|
|
ferencd@0
|
2232 {
|
|
ferencd@0
|
2233 std::cerr << s << '\n';
|
|
ferencd@0
|
2234 std::cerr << *this << '\n';
|
|
ferencd@0
|
2235 zonelets_.pop_back();
|
|
ferencd@0
|
2236 throw;
|
|
ferencd@0
|
2237 }
|
|
ferencd@0
|
2238 }
|
|
ferencd@0
|
2239
|
|
ferencd@0
|
2240 sys_info
|
|
ferencd@0
|
2241 time_zone::get_info_impl(sys_seconds tp) const
|
|
ferencd@0
|
2242 {
|
|
ferencd@0
|
2243 return get_info_impl(tp, static_cast<int>(tz::utc));
|
|
ferencd@0
|
2244 }
|
|
ferencd@0
|
2245
|
|
ferencd@0
|
2246 local_info
|
|
ferencd@0
|
2247 time_zone::get_info_impl(local_seconds tp) const
|
|
ferencd@0
|
2248 {
|
|
ferencd@0
|
2249 using namespace std::chrono;
|
|
ferencd@0
|
2250 local_info i{};
|
|
ferencd@0
|
2251 i.first = get_info_impl(sys_seconds{tp.time_since_epoch()}, static_cast<int>(tz::local));
|
|
ferencd@0
|
2252 auto tps = sys_seconds{(tp - i.first.offset).time_since_epoch()};
|
|
ferencd@0
|
2253 if (tps < i.first.begin)
|
|
ferencd@0
|
2254 {
|
|
ferencd@0
|
2255 i.second = std::move(i.first);
|
|
ferencd@0
|
2256 i.first = get_info_impl(i.second.begin - seconds{1}, static_cast<int>(tz::utc));
|
|
ferencd@0
|
2257 i.result = local_info::nonexistent;
|
|
ferencd@0
|
2258 }
|
|
ferencd@0
|
2259 else if (i.first.end - tps <= days{1})
|
|
ferencd@0
|
2260 {
|
|
ferencd@0
|
2261 i.second = get_info_impl(i.first.end, static_cast<int>(tz::utc));
|
|
ferencd@0
|
2262 tps = sys_seconds{(tp - i.second.offset).time_since_epoch()};
|
|
ferencd@0
|
2263 if (tps >= i.second.begin)
|
|
ferencd@0
|
2264 i.result = local_info::ambiguous;
|
|
ferencd@0
|
2265 else
|
|
ferencd@0
|
2266 i.second = {};
|
|
ferencd@0
|
2267 }
|
|
ferencd@0
|
2268 return i;
|
|
ferencd@0
|
2269 }
|
|
ferencd@0
|
2270
|
|
ferencd@0
|
2271 void
|
|
ferencd@0
|
2272 time_zone::add(const std::string& s)
|
|
ferencd@0
|
2273 {
|
|
ferencd@0
|
2274 try
|
|
ferencd@0
|
2275 {
|
|
ferencd@0
|
2276 std::istringstream in(s);
|
|
ferencd@0
|
2277 in.exceptions(std::ios::failbit | std::ios::badbit);
|
|
ferencd@0
|
2278 ws(in);
|
|
ferencd@0
|
2279 if (!in.eof() && in.peek() != '#')
|
|
ferencd@0
|
2280 parse_info(in);
|
|
ferencd@0
|
2281 }
|
|
ferencd@0
|
2282 catch (...)
|
|
ferencd@0
|
2283 {
|
|
ferencd@0
|
2284 std::cerr << s << '\n';
|
|
ferencd@0
|
2285 std::cerr << *this << '\n';
|
|
ferencd@0
|
2286 zonelets_.pop_back();
|
|
ferencd@0
|
2287 throw;
|
|
ferencd@0
|
2288 }
|
|
ferencd@0
|
2289 }
|
|
ferencd@0
|
2290
|
|
ferencd@0
|
2291 void
|
|
ferencd@0
|
2292 time_zone::parse_info(std::istream& in)
|
|
ferencd@0
|
2293 {
|
|
ferencd@0
|
2294 using namespace date;
|
|
ferencd@0
|
2295 using namespace std::chrono;
|
|
ferencd@0
|
2296 zonelets_.emplace_back();
|
|
ferencd@0
|
2297 auto& zonelet = zonelets_.back();
|
|
ferencd@0
|
2298 zonelet.gmtoff_ = parse_signed_time(in);
|
|
ferencd@0
|
2299 in >> zonelet.u.rule_;
|
|
ferencd@0
|
2300 if (zonelet.u.rule_ == "-")
|
|
ferencd@0
|
2301 zonelet.u.rule_.clear();
|
|
ferencd@0
|
2302 in >> zonelet.format_;
|
|
ferencd@0
|
2303 if (!in.eof())
|
|
ferencd@0
|
2304 ws(in);
|
|
ferencd@0
|
2305 if (in.eof() || in.peek() == '#')
|
|
ferencd@0
|
2306 {
|
|
ferencd@0
|
2307 zonelet.until_year_ = year::max();
|
|
ferencd@0
|
2308 zonelet.until_date_ = MonthDayTime(max_day, tz::utc);
|
|
ferencd@0
|
2309 }
|
|
ferencd@0
|
2310 else
|
|
ferencd@0
|
2311 {
|
|
ferencd@0
|
2312 int y;
|
|
ferencd@0
|
2313 in >> y;
|
|
ferencd@0
|
2314 zonelet.until_year_ = year{y};
|
|
ferencd@0
|
2315 in >> zonelet.until_date_;
|
|
ferencd@0
|
2316 zonelet.until_date_.canonicalize(zonelet.until_year_);
|
|
ferencd@0
|
2317 }
|
|
ferencd@0
|
2318 if ((zonelet.until_year_ < min_year) ||
|
|
ferencd@0
|
2319 (zonelets_.size() > 1 && zonelets_.end()[-2].until_year_ > max_year))
|
|
ferencd@0
|
2320 zonelets_.pop_back();
|
|
ferencd@0
|
2321 }
|
|
ferencd@0
|
2322
|
|
ferencd@0
|
2323 void
|
|
ferencd@0
|
2324 time_zone::adjust_infos(const std::vector<Rule>& rules)
|
|
ferencd@0
|
2325 {
|
|
ferencd@0
|
2326 using namespace std::chrono;
|
|
ferencd@0
|
2327 using namespace date;
|
|
ferencd@0
|
2328 const zonelet* prev_zonelet = nullptr;
|
|
ferencd@0
|
2329 for (auto& z : zonelets_)
|
|
ferencd@0
|
2330 {
|
|
ferencd@0
|
2331 std::pair<const Rule*, const Rule*> eqr{};
|
|
ferencd@0
|
2332 std::istringstream in;
|
|
ferencd@0
|
2333 in.exceptions(std::ios::failbit | std::ios::badbit);
|
|
ferencd@0
|
2334 // Classify info as rule-based, has save, or neither
|
|
ferencd@0
|
2335 if (!z.u.rule_.empty())
|
|
ferencd@0
|
2336 {
|
|
ferencd@0
|
2337 // Find out if this zonelet has a rule or a save
|
|
ferencd@0
|
2338 eqr = std::equal_range(rules.data(), rules.data() + rules.size(), z.u.rule_);
|
|
ferencd@0
|
2339 if (eqr.first == eqr.second)
|
|
ferencd@0
|
2340 {
|
|
ferencd@0
|
2341 // The rule doesn't exist. Assume this is a save
|
|
ferencd@0
|
2342 try
|
|
ferencd@0
|
2343 {
|
|
ferencd@0
|
2344 using namespace std::chrono;
|
|
ferencd@0
|
2345 using string = std::string;
|
|
ferencd@0
|
2346 in.str(z.u.rule_);
|
|
ferencd@0
|
2347 auto tmp = duration_cast<minutes>(parse_signed_time(in));
|
|
ferencd@0
|
2348 #if !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
|
ferencd@0
|
2349 z.u.rule_.~string();
|
|
ferencd@0
|
2350 z.tag_ = zonelet::has_save;
|
|
ferencd@0
|
2351 ::new(&z.u.save_) minutes(tmp);
|
|
ferencd@0
|
2352 #else
|
|
ferencd@0
|
2353 z.u.rule_.clear();
|
|
ferencd@0
|
2354 z.tag_ = zonelet::has_save;
|
|
ferencd@0
|
2355 z.u.save_ = tmp;
|
|
ferencd@0
|
2356 #endif
|
|
ferencd@0
|
2357 }
|
|
ferencd@0
|
2358 catch (...)
|
|
ferencd@0
|
2359 {
|
|
ferencd@0
|
2360 std::cerr << name_ << " : " << z.u.rule_ << '\n';
|
|
ferencd@0
|
2361 throw;
|
|
ferencd@0
|
2362 }
|
|
ferencd@0
|
2363 }
|
|
ferencd@0
|
2364 }
|
|
ferencd@0
|
2365 else
|
|
ferencd@0
|
2366 {
|
|
ferencd@0
|
2367 // This zone::zonelet has no rule and no save
|
|
ferencd@0
|
2368 z.tag_ = zonelet::is_empty;
|
|
ferencd@0
|
2369 }
|
|
ferencd@0
|
2370
|
|
ferencd@0
|
2371 minutes final_save{0};
|
|
ferencd@0
|
2372 if (z.tag_ == zonelet::has_save)
|
|
ferencd@0
|
2373 {
|
|
ferencd@0
|
2374 final_save = z.u.save_;
|
|
ferencd@0
|
2375 }
|
|
ferencd@0
|
2376 else if (z.tag_ == zonelet::has_rule)
|
|
ferencd@0
|
2377 {
|
|
ferencd@0
|
2378 z.last_rule_ = find_rule_for_zone(eqr, z.until_year_, z.gmtoff_,
|
|
ferencd@0
|
2379 z.until_date_);
|
|
ferencd@0
|
2380 if (z.last_rule_.first != nullptr)
|
|
ferencd@0
|
2381 final_save = z.last_rule_.first->save();
|
|
ferencd@0
|
2382 }
|
|
ferencd@0
|
2383 z.until_utc_ = z.until_date_.to_sys(z.until_year_, z.gmtoff_, final_save);
|
|
ferencd@0
|
2384 z.until_std_ = local_seconds{z.until_utc_.time_since_epoch()} + z.gmtoff_;
|
|
ferencd@0
|
2385 z.until_loc_ = z.until_std_ + final_save;
|
|
ferencd@0
|
2386
|
|
ferencd@0
|
2387 if (z.tag_ == zonelet::has_rule)
|
|
ferencd@0
|
2388 {
|
|
ferencd@0
|
2389 if (prev_zonelet != nullptr)
|
|
ferencd@0
|
2390 {
|
|
ferencd@0
|
2391 z.first_rule_ = find_rule_for_zone(eqr, prev_zonelet->until_utc_,
|
|
ferencd@0
|
2392 prev_zonelet->until_std_,
|
|
ferencd@0
|
2393 prev_zonelet->until_loc_);
|
|
ferencd@0
|
2394 if (z.first_rule_.first != nullptr)
|
|
ferencd@0
|
2395 {
|
|
ferencd@0
|
2396 z.initial_save_ = z.first_rule_.first->save();
|
|
ferencd@0
|
2397 z.initial_abbrev_ = z.first_rule_.first->abbrev();
|
|
ferencd@0
|
2398 if (z.first_rule_ != z.last_rule_)
|
|
ferencd@0
|
2399 {
|
|
ferencd@0
|
2400 z.first_rule_ = find_next_rule(eqr.first, eqr.second,
|
|
ferencd@0
|
2401 z.first_rule_.first,
|
|
ferencd@0
|
2402 z.first_rule_.second);
|
|
ferencd@0
|
2403 }
|
|
ferencd@0
|
2404 else
|
|
ferencd@0
|
2405 {
|
|
ferencd@0
|
2406 z.first_rule_ = std::make_pair(nullptr, year::min());
|
|
ferencd@0
|
2407 z.last_rule_ = std::make_pair(nullptr, year::max());
|
|
ferencd@0
|
2408 }
|
|
ferencd@0
|
2409 }
|
|
ferencd@0
|
2410 }
|
|
ferencd@0
|
2411 if (z.first_rule_.first == nullptr && z.last_rule_.first != nullptr)
|
|
ferencd@0
|
2412 {
|
|
ferencd@0
|
2413 z.first_rule_ = std::make_pair(eqr.first, eqr.first->starting_year());
|
|
ferencd@0
|
2414 z.initial_abbrev_ = find_first_std_rule(eqr)->abbrev();
|
|
ferencd@0
|
2415 }
|
|
ferencd@0
|
2416 }
|
|
ferencd@0
|
2417
|
|
ferencd@0
|
2418 #ifndef NDEBUG
|
|
ferencd@0
|
2419 if (z.first_rule_.first == nullptr)
|
|
ferencd@0
|
2420 {
|
|
ferencd@0
|
2421 assert(z.first_rule_.second == year::min());
|
|
ferencd@0
|
2422 assert(z.last_rule_.first == nullptr);
|
|
ferencd@0
|
2423 assert(z.last_rule_.second == year::max());
|
|
ferencd@0
|
2424 }
|
|
ferencd@0
|
2425 else
|
|
ferencd@0
|
2426 {
|
|
ferencd@0
|
2427 assert(z.last_rule_.first != nullptr);
|
|
ferencd@0
|
2428 }
|
|
ferencd@0
|
2429 #endif
|
|
ferencd@0
|
2430 prev_zonelet = &z;
|
|
ferencd@0
|
2431 }
|
|
ferencd@0
|
2432 }
|
|
ferencd@0
|
2433
|
|
ferencd@0
|
2434 static
|
|
ferencd@0
|
2435 std::string
|
|
ferencd@0
|
2436 format_abbrev(std::string format, const std::string& variable, std::chrono::seconds off,
|
|
ferencd@0
|
2437 std::chrono::minutes save)
|
|
ferencd@0
|
2438 {
|
|
ferencd@0
|
2439 using namespace std::chrono;
|
|
ferencd@0
|
2440 auto k = format.find("%s");
|
|
ferencd@0
|
2441 if (k != std::string::npos)
|
|
ferencd@0
|
2442 {
|
|
ferencd@0
|
2443 format.replace(k, 2, variable);
|
|
ferencd@0
|
2444 }
|
|
ferencd@0
|
2445 else
|
|
ferencd@0
|
2446 {
|
|
ferencd@0
|
2447 k = format.find('/');
|
|
ferencd@0
|
2448 if (k != std::string::npos)
|
|
ferencd@0
|
2449 {
|
|
ferencd@0
|
2450 if (save == minutes{0})
|
|
ferencd@0
|
2451 format.erase(k);
|
|
ferencd@0
|
2452 else
|
|
ferencd@0
|
2453 format.erase(0, k+1);
|
|
ferencd@0
|
2454 }
|
|
ferencd@0
|
2455 else
|
|
ferencd@0
|
2456 {
|
|
ferencd@0
|
2457 k = format.find("%z");
|
|
ferencd@0
|
2458 if (k != std::string::npos)
|
|
ferencd@0
|
2459 {
|
|
ferencd@0
|
2460 std::string temp;
|
|
ferencd@0
|
2461 if (off < seconds{0})
|
|
ferencd@0
|
2462 {
|
|
ferencd@0
|
2463 temp = '-';
|
|
ferencd@0
|
2464 off = -off;
|
|
ferencd@0
|
2465 }
|
|
ferencd@0
|
2466 else
|
|
ferencd@0
|
2467 temp = '+';
|
|
ferencd@0
|
2468 auto h = date::floor<hours>(off);
|
|
ferencd@0
|
2469 off -= h;
|
|
ferencd@0
|
2470 if (h < hours{10})
|
|
ferencd@0
|
2471 temp += '0';
|
|
ferencd@0
|
2472 temp += std::to_string(h.count());
|
|
ferencd@0
|
2473 if (off > seconds{0})
|
|
ferencd@0
|
2474 {
|
|
ferencd@0
|
2475 auto m = date::floor<minutes>(off);
|
|
ferencd@0
|
2476 off -= m;
|
|
ferencd@0
|
2477 if (m < minutes{10})
|
|
ferencd@0
|
2478 temp += '0';
|
|
ferencd@0
|
2479 temp += std::to_string(m.count());
|
|
ferencd@0
|
2480 if (off > seconds{0})
|
|
ferencd@0
|
2481 {
|
|
ferencd@0
|
2482 if (off < seconds{10})
|
|
ferencd@0
|
2483 temp += '0';
|
|
ferencd@0
|
2484 temp += std::to_string(off.count());
|
|
ferencd@0
|
2485 }
|
|
ferencd@0
|
2486 }
|
|
ferencd@0
|
2487 format.replace(k, 2, temp);
|
|
ferencd@0
|
2488 }
|
|
ferencd@0
|
2489 }
|
|
ferencd@0
|
2490 }
|
|
ferencd@0
|
2491 return format;
|
|
ferencd@0
|
2492 }
|
|
ferencd@0
|
2493
|
|
ferencd@0
|
2494 sys_info
|
|
ferencd@0
|
2495 time_zone::get_info_impl(sys_seconds tp, int tz_int) const
|
|
ferencd@0
|
2496 {
|
|
ferencd@0
|
2497 using namespace std::chrono;
|
|
ferencd@0
|
2498 using namespace date;
|
|
ferencd@0
|
2499 tz timezone = static_cast<tz>(tz_int);
|
|
ferencd@0
|
2500 assert(timezone != tz::standard);
|
|
ferencd@0
|
2501 auto y = year_month_day(floor<days>(tp)).year();
|
|
ferencd@0
|
2502 if (y < min_year || y > max_year)
|
|
ferencd@0
|
2503 throw std::runtime_error("The year " + std::to_string(static_cast<int>(y)) +
|
|
ferencd@0
|
2504 " is out of range:[" + std::to_string(static_cast<int>(min_year)) + ", "
|
|
ferencd@0
|
2505 + std::to_string(static_cast<int>(max_year)) + "]");
|
|
ferencd@0
|
2506 std::call_once(*adjusted_,
|
|
ferencd@0
|
2507 [this]()
|
|
ferencd@0
|
2508 {
|
|
ferencd@0
|
2509 const_cast<time_zone*>(this)->adjust_infos(get_tzdb().rules);
|
|
ferencd@0
|
2510 });
|
|
ferencd@0
|
2511 auto i = std::upper_bound(zonelets_.begin(), zonelets_.end(), tp,
|
|
ferencd@0
|
2512 [timezone](sys_seconds t, const zonelet& zl)
|
|
ferencd@0
|
2513 {
|
|
ferencd@0
|
2514 return timezone == tz::utc ? t < zl.until_utc_ :
|
|
ferencd@0
|
2515 t < sys_seconds{zl.until_loc_.time_since_epoch()};
|
|
ferencd@0
|
2516 });
|
|
ferencd@0
|
2517
|
|
ferencd@0
|
2518 sys_info r{};
|
|
ferencd@0
|
2519 if (i != zonelets_.end())
|
|
ferencd@0
|
2520 {
|
|
ferencd@0
|
2521 if (i->tag_ == zonelet::has_save)
|
|
ferencd@0
|
2522 {
|
|
ferencd@0
|
2523 if (i != zonelets_.begin())
|
|
ferencd@0
|
2524 r.begin = i[-1].until_utc_;
|
|
ferencd@0
|
2525 else
|
|
ferencd@0
|
2526 r.begin = sys_days(year::min()/min_day);
|
|
ferencd@0
|
2527 r.end = i->until_utc_;
|
|
ferencd@0
|
2528 r.offset = i->gmtoff_ + i->u.save_;
|
|
ferencd@0
|
2529 r.save = i->u.save_;
|
|
ferencd@0
|
2530 }
|
|
ferencd@0
|
2531 else if (i->u.rule_.empty())
|
|
ferencd@0
|
2532 {
|
|
ferencd@0
|
2533 if (i != zonelets_.begin())
|
|
ferencd@0
|
2534 r.begin = i[-1].until_utc_;
|
|
ferencd@0
|
2535 else
|
|
ferencd@0
|
2536 r.begin = sys_days(year::min()/min_day);
|
|
ferencd@0
|
2537 r.end = i->until_utc_;
|
|
ferencd@0
|
2538 r.offset = i->gmtoff_;
|
|
ferencd@0
|
2539 }
|
|
ferencd@0
|
2540 else
|
|
ferencd@0
|
2541 {
|
|
ferencd@0
|
2542 r = find_rule(i->first_rule_, i->last_rule_, y, i->gmtoff_,
|
|
ferencd@0
|
2543 MonthDayTime(local_seconds{tp.time_since_epoch()}, timezone),
|
|
ferencd@0
|
2544 i->initial_save_, i->initial_abbrev_);
|
|
ferencd@0
|
2545 r.offset = i->gmtoff_ + r.save;
|
|
ferencd@0
|
2546 if (i != zonelets_.begin() && r.begin < i[-1].until_utc_)
|
|
ferencd@0
|
2547 r.begin = i[-1].until_utc_;
|
|
ferencd@0
|
2548 if (r.end > i->until_utc_)
|
|
ferencd@0
|
2549 r.end = i->until_utc_;
|
|
ferencd@0
|
2550 }
|
|
ferencd@0
|
2551 r.abbrev = format_abbrev(i->format_, r.abbrev, r.offset, r.save);
|
|
ferencd@0
|
2552 assert(r.begin < r.end);
|
|
ferencd@0
|
2553 }
|
|
ferencd@0
|
2554 return r;
|
|
ferencd@0
|
2555 }
|
|
ferencd@0
|
2556
|
|
ferencd@0
|
2557 std::ostream&
|
|
ferencd@0
|
2558 operator<<(std::ostream& os, const time_zone& z)
|
|
ferencd@0
|
2559 {
|
|
ferencd@0
|
2560 using namespace date;
|
|
ferencd@0
|
2561 using namespace std::chrono;
|
|
ferencd@0
|
2562 detail::save_ostream<char> _(os);
|
|
ferencd@0
|
2563 os.fill(' ');
|
|
ferencd@0
|
2564 os.flags(std::ios::dec | std::ios::left);
|
|
ferencd@0
|
2565 std::call_once(*z.adjusted_,
|
|
ferencd@0
|
2566 [&z]()
|
|
ferencd@0
|
2567 {
|
|
ferencd@0
|
2568 const_cast<time_zone&>(z).adjust_infos(get_tzdb().rules);
|
|
ferencd@0
|
2569 });
|
|
ferencd@0
|
2570 os.width(35);
|
|
ferencd@0
|
2571 os << z.name_;
|
|
ferencd@0
|
2572 std::string indent;
|
|
ferencd@0
|
2573 for (auto const& s : z.zonelets_)
|
|
ferencd@0
|
2574 {
|
|
ferencd@0
|
2575 os << indent;
|
|
ferencd@0
|
2576 if (s.gmtoff_ >= seconds{0})
|
|
ferencd@0
|
2577 os << ' ';
|
|
ferencd@0
|
2578 os << make_time(s.gmtoff_) << " ";
|
|
ferencd@0
|
2579 os.width(15);
|
|
ferencd@0
|
2580 if (s.tag_ != zonelet::has_save)
|
|
ferencd@0
|
2581 os << s.u.rule_;
|
|
ferencd@0
|
2582 else
|
|
ferencd@0
|
2583 {
|
|
ferencd@0
|
2584 std::ostringstream tmp;
|
|
ferencd@0
|
2585 tmp << make_time(s.u.save_);
|
|
ferencd@0
|
2586 os << tmp.str();
|
|
ferencd@0
|
2587 }
|
|
ferencd@0
|
2588 os.width(8);
|
|
ferencd@0
|
2589 os << s.format_ << " ";
|
|
ferencd@0
|
2590 os << s.until_year_ << ' ' << s.until_date_;
|
|
ferencd@0
|
2591 os << " " << s.until_utc_ << " UTC";
|
|
ferencd@0
|
2592 os << " " << s.until_std_ << " STD";
|
|
ferencd@0
|
2593 os << " " << s.until_loc_;
|
|
ferencd@0
|
2594 os << " " << make_time(s.initial_save_);
|
|
ferencd@0
|
2595 os << " " << s.initial_abbrev_;
|
|
ferencd@0
|
2596 if (s.first_rule_.first != nullptr)
|
|
ferencd@0
|
2597 os << " {" << *s.first_rule_.first << ", " << s.first_rule_.second << '}';
|
|
ferencd@0
|
2598 else
|
|
ferencd@0
|
2599 os << " {" << "nullptr" << ", " << s.first_rule_.second << '}';
|
|
ferencd@0
|
2600 if (s.last_rule_.first != nullptr)
|
|
ferencd@0
|
2601 os << " {" << *s.last_rule_.first << ", " << s.last_rule_.second << '}';
|
|
ferencd@0
|
2602 else
|
|
ferencd@0
|
2603 os << " {" << "nullptr" << ", " << s.last_rule_.second << '}';
|
|
ferencd@0
|
2604 os << '\n';
|
|
ferencd@0
|
2605 if (indent.empty())
|
|
ferencd@0
|
2606 indent = std::string(35, ' ');
|
|
ferencd@0
|
2607 }
|
|
ferencd@0
|
2608 return os;
|
|
ferencd@0
|
2609 }
|
|
ferencd@0
|
2610
|
|
ferencd@0
|
2611 #endif // !USE_OS_TZDB
|
|
ferencd@0
|
2612
|
|
ferencd@0
|
2613 #if !MISSING_LEAP_SECONDS
|
|
ferencd@0
|
2614
|
|
ferencd@0
|
2615 std::ostream&
|
|
ferencd@0
|
2616 operator<<(std::ostream& os, const leap& x)
|
|
ferencd@0
|
2617 {
|
|
ferencd@0
|
2618 using namespace date;
|
|
ferencd@0
|
2619 return os << x.date_ << " +";
|
|
ferencd@0
|
2620 }
|
|
ferencd@0
|
2621
|
|
ferencd@0
|
2622 #endif // !MISSING_LEAP_SECONDS
|
|
ferencd@0
|
2623
|
|
ferencd@0
|
2624 #if USE_OS_TZDB
|
|
ferencd@0
|
2625
|
|
ferencd@0
|
2626 # ifdef __APPLE__
|
|
ferencd@0
|
2627 static
|
|
ferencd@0
|
2628 std::string
|
|
ferencd@0
|
2629 get_version()
|
|
ferencd@0
|
2630 {
|
|
ferencd@0
|
2631 using namespace std;
|
|
ferencd@0
|
2632 auto path = get_tz_dir() + string("/+VERSION");
|
|
ferencd@0
|
2633 ifstream in{path};
|
|
ferencd@0
|
2634 string version;
|
|
ferencd@0
|
2635 in >> version;
|
|
ferencd@0
|
2636 if (in.fail())
|
|
ferencd@0
|
2637 throw std::runtime_error("Unable to get Timezone database version from " + path);
|
|
ferencd@0
|
2638 return version;
|
|
ferencd@0
|
2639 }
|
|
ferencd@0
|
2640 # endif
|
|
ferencd@0
|
2641
|
|
ferencd@0
|
2642 static
|
|
ferencd@0
|
2643 std::unique_ptr<tzdb>
|
|
ferencd@0
|
2644 init_tzdb()
|
|
ferencd@0
|
2645 {
|
|
ferencd@0
|
2646 std::unique_ptr<tzdb> db(new tzdb);
|
|
ferencd@0
|
2647
|
|
ferencd@0
|
2648 //Iterate through folders
|
|
ferencd@0
|
2649 std::queue<std::string> subfolders;
|
|
ferencd@0
|
2650 subfolders.emplace(get_tz_dir());
|
|
ferencd@0
|
2651 struct dirent* d;
|
|
ferencd@0
|
2652 struct stat s;
|
|
ferencd@0
|
2653 while (!subfolders.empty())
|
|
ferencd@0
|
2654 {
|
|
ferencd@0
|
2655 auto dirname = std::move(subfolders.front());
|
|
ferencd@0
|
2656 subfolders.pop();
|
|
ferencd@0
|
2657 auto dir = opendir(dirname.c_str());
|
|
ferencd@0
|
2658 if (!dir)
|
|
ferencd@0
|
2659 continue;
|
|
ferencd@0
|
2660 while ((d = readdir(dir)) != nullptr)
|
|
ferencd@0
|
2661 {
|
|
ferencd@0
|
2662 // Ignore these files:
|
|
ferencd@0
|
2663 if (d->d_name[0] == '.' || // curdir, prevdir, hidden
|
|
ferencd@0
|
2664 memcmp(d->d_name, "posix", 5) == 0 || // starts with posix
|
|
ferencd@0
|
2665 strcmp(d->d_name, "Factory") == 0 ||
|
|
ferencd@0
|
2666 strcmp(d->d_name, "iso3166.tab") == 0 ||
|
|
ferencd@0
|
2667 strcmp(d->d_name, "right") == 0 ||
|
|
ferencd@0
|
2668 strcmp(d->d_name, "+VERSION") == 0 ||
|
|
ferencd@0
|
2669 strcmp(d->d_name, "zone.tab") == 0 ||
|
|
ferencd@0
|
2670 strcmp(d->d_name, "zone1970.tab") == 0 ||
|
|
ferencd@0
|
2671 strcmp(d->d_name, "tzdata.zi") == 0 ||
|
|
ferencd@0
|
2672 strcmp(d->d_name, "leapseconds") == 0 ||
|
|
ferencd@0
|
2673 strcmp(d->d_name, "leap-seconds.list") == 0 )
|
|
ferencd@0
|
2674 continue;
|
|
ferencd@0
|
2675 auto subname = dirname + folder_delimiter + d->d_name;
|
|
ferencd@0
|
2676 if(stat(subname.c_str(), &s) == 0)
|
|
ferencd@0
|
2677 {
|
|
ferencd@0
|
2678 if(S_ISDIR(s.st_mode))
|
|
ferencd@0
|
2679 {
|
|
ferencd@0
|
2680 if(!S_ISLNK(s.st_mode))
|
|
ferencd@0
|
2681 {
|
|
ferencd@0
|
2682 subfolders.push(subname);
|
|
ferencd@0
|
2683 }
|
|
ferencd@0
|
2684 }
|
|
ferencd@0
|
2685 else
|
|
ferencd@0
|
2686 {
|
|
ferencd@0
|
2687 db->zones.emplace_back(subname.substr(get_tz_dir().size()+1),
|
|
ferencd@0
|
2688 detail::undocumented{});
|
|
ferencd@0
|
2689 }
|
|
ferencd@0
|
2690 }
|
|
ferencd@0
|
2691 }
|
|
ferencd@0
|
2692 closedir(dir);
|
|
ferencd@0
|
2693 }
|
|
ferencd@0
|
2694 db->zones.shrink_to_fit();
|
|
ferencd@0
|
2695 std::sort(db->zones.begin(), db->zones.end());
|
|
ferencd@0
|
2696 # if !MISSING_LEAP_SECONDS
|
|
ferencd@0
|
2697 std::ifstream in(get_tz_dir() + std::string(1, folder_delimiter) + "right/UTC",
|
|
ferencd@0
|
2698 std::ios_base::binary);
|
|
ferencd@0
|
2699 if (in)
|
|
ferencd@0
|
2700 {
|
|
ferencd@0
|
2701 in.exceptions(std::ios::failbit | std::ios::badbit);
|
|
ferencd@0
|
2702 db->leaps = load_just_leaps(in);
|
|
ferencd@0
|
2703 }
|
|
ferencd@0
|
2704 else
|
|
ferencd@0
|
2705 {
|
|
ferencd@0
|
2706 in.clear();
|
|
ferencd@0
|
2707 in.open(get_tz_dir() + std::string(1, folder_delimiter) +
|
|
ferencd@0
|
2708 "UTC", std::ios_base::binary);
|
|
ferencd@0
|
2709 if (!in)
|
|
ferencd@0
|
2710 throw std::runtime_error("Unable to extract leap second information");
|
|
ferencd@0
|
2711 in.exceptions(std::ios::failbit | std::ios::badbit);
|
|
ferencd@0
|
2712 db->leaps = load_just_leaps(in);
|
|
ferencd@0
|
2713 }
|
|
ferencd@0
|
2714 # endif // !MISSING_LEAP_SECONDS
|
|
ferencd@0
|
2715 # ifdef __APPLE__
|
|
ferencd@0
|
2716 db->version = get_version();
|
|
ferencd@0
|
2717 # endif
|
|
ferencd@0
|
2718 return db;
|
|
ferencd@0
|
2719 }
|
|
ferencd@0
|
2720
|
|
ferencd@0
|
2721 #else // !USE_OS_TZDB
|
|
ferencd@0
|
2722
|
|
ferencd@0
|
2723 // link
|
|
ferencd@0
|
2724
|
|
ferencd@0
|
2725 link::link(const std::string& s)
|
|
ferencd@0
|
2726 {
|
|
ferencd@0
|
2727 using namespace date;
|
|
ferencd@0
|
2728 std::istringstream in(s);
|
|
ferencd@0
|
2729 in.exceptions(std::ios::failbit | std::ios::badbit);
|
|
ferencd@0
|
2730 std::string word;
|
|
ferencd@0
|
2731 in >> word >> target_ >> name_;
|
|
ferencd@0
|
2732 }
|
|
ferencd@0
|
2733
|
|
ferencd@0
|
2734 std::ostream&
|
|
ferencd@0
|
2735 operator<<(std::ostream& os, const link& x)
|
|
ferencd@0
|
2736 {
|
|
ferencd@0
|
2737 using namespace date;
|
|
ferencd@0
|
2738 detail::save_ostream<char> _(os);
|
|
ferencd@0
|
2739 os.fill(' ');
|
|
ferencd@0
|
2740 os.flags(std::ios::dec | std::ios::left);
|
|
ferencd@0
|
2741 os.width(35);
|
|
ferencd@0
|
2742 return os << x.name_ << " --> " << x.target_;
|
|
ferencd@0
|
2743 }
|
|
ferencd@0
|
2744
|
|
ferencd@0
|
2745 // leap
|
|
ferencd@0
|
2746
|
|
ferencd@0
|
2747 leap::leap(const std::string& s, detail::undocumented)
|
|
ferencd@0
|
2748 {
|
|
ferencd@0
|
2749 using namespace date;
|
|
ferencd@0
|
2750 std::istringstream in(s);
|
|
ferencd@0
|
2751 in.exceptions(std::ios::failbit | std::ios::badbit);
|
|
ferencd@0
|
2752 std::string word;
|
|
ferencd@0
|
2753 int y;
|
|
ferencd@0
|
2754 MonthDayTime date;
|
|
ferencd@0
|
2755 in >> word >> y >> date;
|
|
ferencd@0
|
2756 date_ = date.to_time_point(year(y));
|
|
ferencd@0
|
2757 }
|
|
ferencd@0
|
2758
|
|
ferencd@0
|
2759 static
|
|
ferencd@0
|
2760 bool
|
|
ferencd@0
|
2761 file_exists(const std::string& filename)
|
|
ferencd@0
|
2762 {
|
|
ferencd@0
|
2763 #ifdef _WIN32
|
|
ferencd@0
|
2764 return ::_access(filename.c_str(), 0) == 0;
|
|
ferencd@0
|
2765 #else
|
|
ferencd@0
|
2766 return ::access(filename.c_str(), F_OK) == 0;
|
|
ferencd@0
|
2767 #endif
|
|
ferencd@0
|
2768 }
|
|
ferencd@0
|
2769
|
|
ferencd@0
|
2770 #if HAS_REMOTE_API
|
|
ferencd@0
|
2771
|
|
ferencd@0
|
2772 // CURL tools
|
|
ferencd@0
|
2773
|
|
ferencd@0
|
2774 static
|
|
ferencd@0
|
2775 int
|
|
ferencd@0
|
2776 curl_global()
|
|
ferencd@0
|
2777 {
|
|
ferencd@0
|
2778 if (::curl_global_init(CURL_GLOBAL_DEFAULT) != 0)
|
|
ferencd@0
|
2779 throw std::runtime_error("CURL global initialization failed");
|
|
ferencd@0
|
2780 return 0;
|
|
ferencd@0
|
2781 }
|
|
ferencd@0
|
2782
|
|
ferencd@0
|
2783 namespace
|
|
ferencd@0
|
2784 {
|
|
ferencd@0
|
2785
|
|
ferencd@0
|
2786 struct curl_deleter
|
|
ferencd@0
|
2787 {
|
|
ferencd@0
|
2788 void operator()(CURL* p) const
|
|
ferencd@0
|
2789 {
|
|
ferencd@0
|
2790 ::curl_easy_cleanup(p);
|
|
ferencd@0
|
2791 }
|
|
ferencd@0
|
2792 };
|
|
ferencd@0
|
2793
|
|
ferencd@0
|
2794 } // unnamed namespace
|
|
ferencd@0
|
2795
|
|
ferencd@0
|
2796 static
|
|
ferencd@0
|
2797 std::unique_ptr<CURL, curl_deleter>
|
|
ferencd@0
|
2798 curl_init()
|
|
ferencd@0
|
2799 {
|
|
ferencd@0
|
2800 static const auto curl_is_now_initiailized = curl_global();
|
|
ferencd@0
|
2801 (void)curl_is_now_initiailized;
|
|
ferencd@0
|
2802 return std::unique_ptr<CURL, curl_deleter>{::curl_easy_init()};
|
|
ferencd@0
|
2803 }
|
|
ferencd@0
|
2804
|
|
ferencd@0
|
2805 static
|
|
ferencd@0
|
2806 bool
|
|
ferencd@0
|
2807 download_to_string(const std::string& url, std::string& str)
|
|
ferencd@0
|
2808 {
|
|
ferencd@0
|
2809 str.clear();
|
|
ferencd@0
|
2810 auto curl = curl_init();
|
|
ferencd@0
|
2811 if (!curl)
|
|
ferencd@0
|
2812 return false;
|
|
ferencd@0
|
2813 std::string version;
|
|
ferencd@0
|
2814 curl_easy_setopt(curl.get(), CURLOPT_USERAGENT, "curl");
|
|
ferencd@0
|
2815 curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
|
|
ferencd@0
|
2816 curl_write_callback write_cb = [](char* contents, std::size_t size, std::size_t nmemb,
|
|
ferencd@0
|
2817 void* userp) -> std::size_t
|
|
ferencd@0
|
2818 {
|
|
ferencd@0
|
2819 auto& userstr = *static_cast<std::string*>(userp);
|
|
ferencd@0
|
2820 auto realsize = size * nmemb;
|
|
ferencd@0
|
2821 userstr.append(contents, realsize);
|
|
ferencd@0
|
2822 return realsize;
|
|
ferencd@0
|
2823 };
|
|
ferencd@0
|
2824 curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_cb);
|
|
ferencd@0
|
2825 curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &str);
|
|
ferencd@0
|
2826 curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, false);
|
|
ferencd@0
|
2827 auto res = curl_easy_perform(curl.get());
|
|
ferencd@0
|
2828 return (res == CURLE_OK);
|
|
ferencd@0
|
2829 }
|
|
ferencd@0
|
2830
|
|
ferencd@0
|
2831 namespace
|
|
ferencd@0
|
2832 {
|
|
ferencd@0
|
2833 enum class download_file_options { binary, text };
|
|
ferencd@0
|
2834 }
|
|
ferencd@0
|
2835
|
|
ferencd@0
|
2836 static
|
|
ferencd@0
|
2837 bool
|
|
ferencd@0
|
2838 download_to_file(const std::string& url, const std::string& local_filename,
|
|
ferencd@0
|
2839 download_file_options opts)
|
|
ferencd@0
|
2840 {
|
|
ferencd@0
|
2841 auto curl = curl_init();
|
|
ferencd@0
|
2842 if (!curl)
|
|
ferencd@0
|
2843 return false;
|
|
ferencd@0
|
2844 curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
|
|
ferencd@0
|
2845 curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, false);
|
|
ferencd@0
|
2846 curl_write_callback write_cb = [](char* contents, std::size_t size, std::size_t nmemb,
|
|
ferencd@0
|
2847 void* userp) -> std::size_t
|
|
ferencd@0
|
2848 {
|
|
ferencd@0
|
2849 auto& of = *static_cast<std::ofstream*>(userp);
|
|
ferencd@0
|
2850 auto realsize = size * nmemb;
|
|
ferencd@0
|
2851 of.write(contents, static_cast<std::streamsize>(realsize));
|
|
ferencd@0
|
2852 return realsize;
|
|
ferencd@0
|
2853 };
|
|
ferencd@0
|
2854 curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_cb);
|
|
ferencd@0
|
2855 decltype(curl_easy_perform(curl.get())) res;
|
|
ferencd@0
|
2856 {
|
|
ferencd@0
|
2857 std::ofstream of(local_filename,
|
|
ferencd@0
|
2858 opts == download_file_options::binary ?
|
|
ferencd@0
|
2859 std::ofstream::out | std::ofstream::binary :
|
|
ferencd@0
|
2860 std::ofstream::out);
|
|
ferencd@0
|
2861 of.exceptions(std::ios::badbit);
|
|
ferencd@0
|
2862 curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &of);
|
|
ferencd@0
|
2863 res = curl_easy_perform(curl.get());
|
|
ferencd@0
|
2864 }
|
|
ferencd@0
|
2865 return res == CURLE_OK;
|
|
ferencd@0
|
2866 }
|
|
ferencd@0
|
2867
|
|
ferencd@0
|
2868 std::string
|
|
ferencd@0
|
2869 remote_version()
|
|
ferencd@0
|
2870 {
|
|
ferencd@0
|
2871 std::string version;
|
|
ferencd@0
|
2872 std::string str;
|
|
ferencd@0
|
2873 if (download_to_string("https://www.iana.org/time-zones", str))
|
|
ferencd@0
|
2874 {
|
|
ferencd@0
|
2875 CONSTDATA char db[] = "/time-zones/releases/tzdata";
|
|
ferencd@0
|
2876 CONSTDATA auto db_size = sizeof(db) - 1;
|
|
ferencd@0
|
2877 auto p = str.find(db, 0, db_size);
|
|
ferencd@0
|
2878 const int ver_str_len = 5;
|
|
ferencd@0
|
2879 if (p != std::string::npos && p + (db_size + ver_str_len) <= str.size())
|
|
ferencd@0
|
2880 version = str.substr(p + db_size, ver_str_len);
|
|
ferencd@0
|
2881 }
|
|
ferencd@0
|
2882 return version;
|
|
ferencd@0
|
2883 }
|
|
ferencd@0
|
2884
|
|
ferencd@0
|
2885
|
|
ferencd@0
|
2886 // TODO! Using system() create a process and a console window.
|
|
ferencd@0
|
2887 // This is useful to see what errors may occur but is slow and distracting.
|
|
ferencd@0
|
2888 // Consider implementing this functionality more directly, such as
|
|
ferencd@0
|
2889 // using _mkdir and CreateProcess etc.
|
|
ferencd@0
|
2890 // But use the current means now as matches Unix implementations and while
|
|
ferencd@0
|
2891 // in proof of concept / testing phase.
|
|
ferencd@0
|
2892 // TODO! Use <filesystem> eventually.
|
|
ferencd@0
|
2893 static
|
|
ferencd@0
|
2894 bool
|
|
ferencd@0
|
2895 remove_folder_and_subfolders(const std::string& folder)
|
|
ferencd@0
|
2896 {
|
|
ferencd@0
|
2897 # ifdef _WIN32
|
|
ferencd@0
|
2898 # if USE_SHELL_API
|
|
ferencd@0
|
2899 // Delete the folder contents by deleting the folder.
|
|
ferencd@0
|
2900 std::string cmd = "rd /s /q \"";
|
|
ferencd@0
|
2901 cmd += folder;
|
|
ferencd@0
|
2902 cmd += '\"';
|
|
ferencd@0
|
2903 return std::system(cmd.c_str()) == EXIT_SUCCESS;
|
|
ferencd@0
|
2904 # else // !USE_SHELL_API
|
|
ferencd@0
|
2905 // Create a buffer containing the path to delete. It must be terminated
|
|
ferencd@0
|
2906 // by two nuls. Who designs these API's...
|
|
ferencd@0
|
2907 std::vector<char> from;
|
|
ferencd@0
|
2908 from.assign(folder.begin(), folder.end());
|
|
ferencd@0
|
2909 from.push_back('\0');
|
|
ferencd@0
|
2910 from.push_back('\0');
|
|
ferencd@0
|
2911 SHFILEOPSTRUCT fo{}; // Zero initialize.
|
|
ferencd@0
|
2912 fo.wFunc = FO_DELETE;
|
|
ferencd@0
|
2913 fo.pFrom = from.data();
|
|
ferencd@0
|
2914 fo.fFlags = FOF_NO_UI;
|
|
ferencd@0
|
2915 int ret = SHFileOperation(&fo);
|
|
ferencd@0
|
2916 if (ret == 0 && !fo.fAnyOperationsAborted)
|
|
ferencd@0
|
2917 return true;
|
|
ferencd@0
|
2918 return false;
|
|
ferencd@0
|
2919 # endif // !USE_SHELL_API
|
|
ferencd@0
|
2920 # else // !_WIN32
|
|
ferencd@0
|
2921 # if USE_SHELL_API
|
|
ferencd@0
|
2922 return std::system(("rm -R " + folder).c_str()) == EXIT_SUCCESS;
|
|
ferencd@0
|
2923 # else // !USE_SHELL_API
|
|
ferencd@0
|
2924 struct dir_deleter {
|
|
ferencd@0
|
2925 dir_deleter() {}
|
|
ferencd@0
|
2926 void operator()(DIR* d) const
|
|
ferencd@0
|
2927 {
|
|
ferencd@0
|
2928 if (d != nullptr)
|
|
ferencd@0
|
2929 {
|
|
ferencd@0
|
2930 int result = closedir(d);
|
|
ferencd@0
|
2931 assert(result == 0);
|
|
ferencd@0
|
2932 }
|
|
ferencd@0
|
2933 }
|
|
ferencd@0
|
2934 };
|
|
ferencd@0
|
2935 using closedir_ptr = std::unique_ptr<DIR, dir_deleter>;
|
|
ferencd@0
|
2936
|
|
ferencd@0
|
2937 std::string filename;
|
|
ferencd@0
|
2938 struct stat statbuf;
|
|
ferencd@0
|
2939 std::size_t folder_len = folder.length();
|
|
ferencd@0
|
2940 struct dirent* p = nullptr;
|
|
ferencd@0
|
2941
|
|
ferencd@0
|
2942 closedir_ptr d(opendir(folder.c_str()));
|
|
ferencd@0
|
2943 bool r = d.get() != nullptr;
|
|
ferencd@0
|
2944 while (r && (p=readdir(d.get())) != nullptr)
|
|
ferencd@0
|
2945 {
|
|
ferencd@0
|
2946 if (strcmp(p->d_name, ".") == 0 || strcmp(p->d_name, "..") == 0)
|
|
ferencd@0
|
2947 continue;
|
|
ferencd@0
|
2948
|
|
ferencd@0
|
2949 // + 2 for path delimiter and nul terminator.
|
|
ferencd@0
|
2950 std::size_t buf_len = folder_len + strlen(p->d_name) + 2;
|
|
ferencd@0
|
2951 filename.resize(buf_len);
|
|
ferencd@0
|
2952 std::size_t path_len = static_cast<std::size_t>(
|
|
ferencd@0
|
2953 snprintf(&filename[0], buf_len, "%s/%s", folder.c_str(), p->d_name));
|
|
ferencd@0
|
2954 assert(path_len == buf_len - 1);
|
|
ferencd@0
|
2955 filename.resize(path_len);
|
|
ferencd@0
|
2956
|
|
ferencd@0
|
2957 if (stat(filename.c_str(), &statbuf) == 0)
|
|
ferencd@0
|
2958 r = S_ISDIR(statbuf.st_mode)
|
|
ferencd@0
|
2959 ? remove_folder_and_subfolders(filename)
|
|
ferencd@0
|
2960 : unlink(filename.c_str()) == 0;
|
|
ferencd@0
|
2961 }
|
|
ferencd@0
|
2962 d.reset();
|
|
ferencd@0
|
2963
|
|
ferencd@0
|
2964 if (r)
|
|
ferencd@0
|
2965 r = rmdir(folder.c_str()) == 0;
|
|
ferencd@0
|
2966
|
|
ferencd@0
|
2967 return r;
|
|
ferencd@0
|
2968 # endif // !USE_SHELL_API
|
|
ferencd@0
|
2969 # endif // !_WIN32
|
|
ferencd@0
|
2970 }
|
|
ferencd@0
|
2971
|
|
ferencd@0
|
2972 static
|
|
ferencd@0
|
2973 bool
|
|
ferencd@0
|
2974 make_directory(const std::string& folder)
|
|
ferencd@0
|
2975 {
|
|
ferencd@0
|
2976 # ifdef _WIN32
|
|
ferencd@0
|
2977 # if USE_SHELL_API
|
|
ferencd@0
|
2978 // Re-create the folder.
|
|
ferencd@0
|
2979 std::string cmd = "mkdir \"";
|
|
ferencd@0
|
2980 cmd += folder;
|
|
ferencd@0
|
2981 cmd += '\"';
|
|
ferencd@0
|
2982 return std::system(cmd.c_str()) == EXIT_SUCCESS;
|
|
ferencd@0
|
2983 # else // !USE_SHELL_API
|
|
ferencd@0
|
2984 return _mkdir(folder.c_str()) == 0;
|
|
ferencd@0
|
2985 # endif // !USE_SHELL_API
|
|
ferencd@0
|
2986 # else // !_WIN32
|
|
ferencd@0
|
2987 # if USE_SHELL_API
|
|
ferencd@0
|
2988 return std::system(("mkdir -p " + folder).c_str()) == EXIT_SUCCESS;
|
|
ferencd@0
|
2989 # else // !USE_SHELL_API
|
|
ferencd@0
|
2990 return mkdir(folder.c_str(), 0777) == 0;
|
|
ferencd@0
|
2991 # endif // !USE_SHELL_API
|
|
ferencd@0
|
2992 # endif // !_WIN32
|
|
ferencd@0
|
2993 }
|
|
ferencd@0
|
2994
|
|
ferencd@0
|
2995 static
|
|
ferencd@0
|
2996 bool
|
|
ferencd@0
|
2997 delete_file(const std::string& file)
|
|
ferencd@0
|
2998 {
|
|
ferencd@0
|
2999 # ifdef _WIN32
|
|
ferencd@0
|
3000 # if USE_SHELL_API
|
|
ferencd@0
|
3001 std::string cmd = "del \"";
|
|
ferencd@0
|
3002 cmd += file;
|
|
ferencd@0
|
3003 cmd += '\"';
|
|
ferencd@0
|
3004 return std::system(cmd.c_str()) == 0;
|
|
ferencd@0
|
3005 # else // !USE_SHELL_API
|
|
ferencd@0
|
3006 return _unlink(file.c_str()) == 0;
|
|
ferencd@0
|
3007 # endif // !USE_SHELL_API
|
|
ferencd@0
|
3008 # else // !_WIN32
|
|
ferencd@0
|
3009 # if USE_SHELL_API
|
|
ferencd@0
|
3010 return std::system(("rm " + file).c_str()) == EXIT_SUCCESS;
|
|
ferencd@0
|
3011 # else // !USE_SHELL_API
|
|
ferencd@0
|
3012 return unlink(file.c_str()) == 0;
|
|
ferencd@0
|
3013 # endif // !USE_SHELL_API
|
|
ferencd@0
|
3014 # endif // !_WIN32
|
|
ferencd@0
|
3015 }
|
|
ferencd@0
|
3016
|
|
ferencd@0
|
3017 # ifdef _WIN32
|
|
ferencd@0
|
3018
|
|
ferencd@0
|
3019 static
|
|
ferencd@0
|
3020 bool
|
|
ferencd@0
|
3021 move_file(const std::string& from, const std::string& to)
|
|
ferencd@0
|
3022 {
|
|
ferencd@0
|
3023 # if USE_SHELL_API
|
|
ferencd@0
|
3024 std::string cmd = "move \"";
|
|
ferencd@0
|
3025 cmd += from;
|
|
ferencd@0
|
3026 cmd += "\" \"";
|
|
ferencd@0
|
3027 cmd += to;
|
|
ferencd@0
|
3028 cmd += '\"';
|
|
ferencd@0
|
3029 return std::system(cmd.c_str()) == EXIT_SUCCESS;
|
|
ferencd@0
|
3030 # else // !USE_SHELL_API
|
|
ferencd@0
|
3031 return !!::MoveFile(from.c_str(), to.c_str());
|
|
ferencd@0
|
3032 # endif // !USE_SHELL_API
|
|
ferencd@0
|
3033 }
|
|
ferencd@0
|
3034
|
|
ferencd@0
|
3035 // Usually something like "c:\Program Files".
|
|
ferencd@0
|
3036 static
|
|
ferencd@0
|
3037 std::string
|
|
ferencd@0
|
3038 get_program_folder()
|
|
ferencd@0
|
3039 {
|
|
ferencd@0
|
3040 return get_known_folder(FOLDERID_ProgramFiles);
|
|
ferencd@0
|
3041 }
|
|
ferencd@0
|
3042
|
|
ferencd@0
|
3043 // Note folder can and usually does contain spaces.
|
|
ferencd@0
|
3044 static
|
|
ferencd@0
|
3045 std::string
|
|
ferencd@0
|
3046 get_unzip_program()
|
|
ferencd@0
|
3047 {
|
|
ferencd@0
|
3048 std::string path;
|
|
ferencd@0
|
3049
|
|
ferencd@0
|
3050 // 7-Zip appears to note its location in the registry.
|
|
ferencd@0
|
3051 // If that doesn't work, fall through and take a guess, but it will likely be wrong.
|
|
ferencd@0
|
3052 HKEY hKey = nullptr;
|
|
ferencd@0
|
3053 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\7-Zip", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
|
|
ferencd@0
|
3054 {
|
|
ferencd@0
|
3055 char value_buffer[MAX_PATH + 1]; // fyi 260 at time of writing.
|
|
ferencd@0
|
3056 // in/out parameter. Documentation say that size is a count of bytes not chars.
|
|
ferencd@0
|
3057 DWORD size = sizeof(value_buffer) - sizeof(value_buffer[0]);
|
|
ferencd@0
|
3058 DWORD tzi_type = REG_SZ;
|
|
ferencd@0
|
3059 // Testing shows Path key value is "C:\Program Files\7-Zip\" i.e. always with trailing \.
|
|
ferencd@0
|
3060 bool got_value = (RegQueryValueExA(hKey, "Path", nullptr, &tzi_type,
|
|
ferencd@0
|
3061 reinterpret_cast<LPBYTE>(value_buffer), &size) == ERROR_SUCCESS);
|
|
ferencd@0
|
3062 RegCloseKey(hKey); // Close now incase of throw later.
|
|
ferencd@0
|
3063 if (got_value)
|
|
ferencd@0
|
3064 {
|
|
ferencd@0
|
3065 // Function does not guarantee to null terminate.
|
|
ferencd@0
|
3066 value_buffer[size / sizeof(value_buffer[0])] = '\0';
|
|
ferencd@0
|
3067 path = value_buffer;
|
|
ferencd@0
|
3068 if (!path.empty())
|
|
ferencd@0
|
3069 {
|
|
ferencd@0
|
3070 path += "7z.exe";
|
|
ferencd@0
|
3071 return path;
|
|
ferencd@0
|
3072 }
|
|
ferencd@0
|
3073 }
|
|
ferencd@0
|
3074 }
|
|
ferencd@0
|
3075 path += get_program_folder();
|
|
ferencd@0
|
3076 path += folder_delimiter;
|
|
ferencd@0
|
3077 path += "7-Zip\\7z.exe";
|
|
ferencd@0
|
3078 return path;
|
|
ferencd@0
|
3079 }
|
|
ferencd@0
|
3080
|
|
ferencd@0
|
3081 # if !USE_SHELL_API
|
|
ferencd@0
|
3082 static
|
|
ferencd@0
|
3083 int
|
|
ferencd@0
|
3084 run_program(const std::string& command)
|
|
ferencd@0
|
3085 {
|
|
ferencd@0
|
3086 STARTUPINFO si{};
|
|
ferencd@0
|
3087 si.cb = sizeof(si);
|
|
ferencd@0
|
3088 PROCESS_INFORMATION pi{};
|
|
ferencd@0
|
3089
|
|
ferencd@0
|
3090 // Allegedly CreateProcess overwrites the command line. Ugh.
|
|
ferencd@0
|
3091 std::string mutable_command(command);
|
|
ferencd@0
|
3092 if (CreateProcess(nullptr, &mutable_command[0],
|
|
ferencd@0
|
3093 nullptr, nullptr, FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi))
|
|
ferencd@0
|
3094 {
|
|
ferencd@0
|
3095 WaitForSingleObject(pi.hProcess, INFINITE);
|
|
ferencd@0
|
3096 DWORD exit_code;
|
|
ferencd@0
|
3097 bool got_exit_code = !!GetExitCodeProcess(pi.hProcess, &exit_code);
|
|
ferencd@0
|
3098 CloseHandle(pi.hProcess);
|
|
ferencd@0
|
3099 CloseHandle(pi.hThread);
|
|
ferencd@0
|
3100 // Not 100% sure about this still active thing is correct,
|
|
ferencd@0
|
3101 // but I'm going with it because I *think* WaitForSingleObject might
|
|
ferencd@0
|
3102 // return in some cases without INFINITE-ly waiting.
|
|
ferencd@0
|
3103 // But why/wouldn't GetExitCodeProcess return false in that case?
|
|
ferencd@0
|
3104 if (got_exit_code && exit_code != STILL_ACTIVE)
|
|
ferencd@0
|
3105 return static_cast<int>(exit_code);
|
|
ferencd@0
|
3106 }
|
|
ferencd@0
|
3107 return EXIT_FAILURE;
|
|
ferencd@0
|
3108 }
|
|
ferencd@0
|
3109 # endif // !USE_SHELL_API
|
|
ferencd@0
|
3110
|
|
ferencd@0
|
3111 static
|
|
ferencd@0
|
3112 std::string
|
|
ferencd@0
|
3113 get_download_tar_file(const std::string& version)
|
|
ferencd@0
|
3114 {
|
|
ferencd@0
|
3115 auto file = get_install();
|
|
ferencd@0
|
3116 file += folder_delimiter;
|
|
ferencd@0
|
3117 file += "tzdata";
|
|
ferencd@0
|
3118 file += version;
|
|
ferencd@0
|
3119 file += ".tar";
|
|
ferencd@0
|
3120 return file;
|
|
ferencd@0
|
3121 }
|
|
ferencd@0
|
3122
|
|
ferencd@0
|
3123 static
|
|
ferencd@0
|
3124 bool
|
|
ferencd@0
|
3125 extract_gz_file(const std::string& version, const std::string& gz_file,
|
|
ferencd@0
|
3126 const std::string& dest_folder)
|
|
ferencd@0
|
3127 {
|
|
ferencd@0
|
3128 auto unzip_prog = get_unzip_program();
|
|
ferencd@0
|
3129 bool unzip_result = false;
|
|
ferencd@0
|
3130 // Use the unzip program to extract the tar file from the archive.
|
|
ferencd@0
|
3131
|
|
ferencd@0
|
3132 // Aim to create a string like:
|
|
ferencd@0
|
3133 // "C:\Program Files\7-Zip\7z.exe" x "C:\Users\SomeUser\Downloads\tzdata2016d.tar.gz"
|
|
ferencd@0
|
3134 // -o"C:\Users\SomeUser\Downloads\tzdata"
|
|
ferencd@0
|
3135 std::string cmd;
|
|
ferencd@0
|
3136 cmd = '\"';
|
|
ferencd@0
|
3137 cmd += unzip_prog;
|
|
ferencd@0
|
3138 cmd += "\" x \"";
|
|
ferencd@0
|
3139 cmd += gz_file;
|
|
ferencd@0
|
3140 cmd += "\" -o\"";
|
|
ferencd@0
|
3141 cmd += dest_folder;
|
|
ferencd@0
|
3142 cmd += '\"';
|
|
ferencd@0
|
3143
|
|
ferencd@0
|
3144 # if USE_SHELL_API
|
|
ferencd@0
|
3145 // When using shelling out with std::system() extra quotes are required around the
|
|
ferencd@0
|
3146 // whole command. It's weird but necessary it seems, see:
|
|
ferencd@0
|
3147 // http://stackoverflow.com/q/27975969/576911
|
|
ferencd@0
|
3148
|
|
ferencd@0
|
3149 cmd = "\"" + cmd + "\"";
|
|
ferencd@0
|
3150 if (std::system(cmd.c_str()) == EXIT_SUCCESS)
|
|
ferencd@0
|
3151 unzip_result = true;
|
|
ferencd@0
|
3152 # else // !USE_SHELL_API
|
|
ferencd@0
|
3153 if (run_program(cmd) == EXIT_SUCCESS)
|
|
ferencd@0
|
3154 unzip_result = true;
|
|
ferencd@0
|
3155 # endif // !USE_SHELL_API
|
|
ferencd@0
|
3156 if (unzip_result)
|
|
ferencd@0
|
3157 delete_file(gz_file);
|
|
ferencd@0
|
3158
|
|
ferencd@0
|
3159 // Use the unzip program extract the data from the tar file that was
|
|
ferencd@0
|
3160 // just extracted from the archive.
|
|
ferencd@0
|
3161 auto tar_file = get_download_tar_file(version);
|
|
ferencd@0
|
3162 cmd = '\"';
|
|
ferencd@0
|
3163 cmd += unzip_prog;
|
|
ferencd@0
|
3164 cmd += "\" x \"";
|
|
ferencd@0
|
3165 cmd += tar_file;
|
|
ferencd@0
|
3166 cmd += "\" -o\"";
|
|
ferencd@0
|
3167 cmd += get_install();
|
|
ferencd@0
|
3168 cmd += '\"';
|
|
ferencd@0
|
3169 # if USE_SHELL_API
|
|
ferencd@0
|
3170 cmd = "\"" + cmd + "\"";
|
|
ferencd@0
|
3171 if (std::system(cmd.c_str()) == EXIT_SUCCESS)
|
|
ferencd@0
|
3172 unzip_result = true;
|
|
ferencd@0
|
3173 # else // !USE_SHELL_API
|
|
ferencd@0
|
3174 if (run_program(cmd) == EXIT_SUCCESS)
|
|
ferencd@0
|
3175 unzip_result = true;
|
|
ferencd@0
|
3176 # endif // !USE_SHELL_API
|
|
ferencd@0
|
3177
|
|
ferencd@0
|
3178 if (unzip_result)
|
|
ferencd@0
|
3179 delete_file(tar_file);
|
|
ferencd@0
|
3180
|
|
ferencd@0
|
3181 return unzip_result;
|
|
ferencd@0
|
3182 }
|
|
ferencd@0
|
3183
|
|
ferencd@0
|
3184 static
|
|
ferencd@0
|
3185 std::string
|
|
ferencd@0
|
3186 get_download_mapping_file(const std::string& version)
|
|
ferencd@0
|
3187 {
|
|
ferencd@0
|
3188 auto file = get_install() + version + "windowsZones.xml";
|
|
ferencd@0
|
3189 return file;
|
|
ferencd@0
|
3190 }
|
|
ferencd@0
|
3191
|
|
ferencd@0
|
3192 # else // !_WIN32
|
|
ferencd@0
|
3193
|
|
ferencd@0
|
3194 # if !USE_SHELL_API
|
|
ferencd@0
|
3195 static
|
|
ferencd@0
|
3196 int
|
|
ferencd@0
|
3197 run_program(const char* prog, const char*const args[])
|
|
ferencd@0
|
3198 {
|
|
ferencd@0
|
3199 pid_t pid = fork();
|
|
ferencd@0
|
3200 if (pid == -1) // Child failed to start.
|
|
ferencd@0
|
3201 return EXIT_FAILURE;
|
|
ferencd@0
|
3202
|
|
ferencd@0
|
3203 if (pid != 0)
|
|
ferencd@0
|
3204 {
|
|
ferencd@0
|
3205 // We are in the parent. Child started. Wait for it.
|
|
ferencd@0
|
3206 pid_t ret;
|
|
ferencd@0
|
3207 int status;
|
|
ferencd@0
|
3208 while ((ret = waitpid(pid, &status, 0)) == -1)
|
|
ferencd@0
|
3209 {
|
|
ferencd@0
|
3210 if (errno != EINTR)
|
|
ferencd@0
|
3211 break;
|
|
ferencd@0
|
3212 }
|
|
ferencd@0
|
3213 if (ret != -1)
|
|
ferencd@0
|
3214 {
|
|
ferencd@0
|
3215 if (WIFEXITED(status))
|
|
ferencd@0
|
3216 return WEXITSTATUS(status);
|
|
ferencd@0
|
3217 }
|
|
ferencd@0
|
3218 printf("Child issues!\n");
|
|
ferencd@0
|
3219
|
|
ferencd@0
|
3220 return EXIT_FAILURE; // Not sure what status of child is.
|
|
ferencd@0
|
3221 }
|
|
ferencd@0
|
3222 else // We are in the child process. Start the program the parent wants to run.
|
|
ferencd@0
|
3223 {
|
|
ferencd@0
|
3224
|
|
ferencd@0
|
3225 if (execv(prog, const_cast<char**>(args)) == -1) // Does not return.
|
|
ferencd@0
|
3226 {
|
|
ferencd@0
|
3227 perror("unreachable 0\n");
|
|
ferencd@0
|
3228 _Exit(127);
|
|
ferencd@0
|
3229 }
|
|
ferencd@0
|
3230 printf("unreachable 2\n");
|
|
ferencd@0
|
3231 }
|
|
ferencd@0
|
3232 printf("unreachable 2\n");
|
|
ferencd@0
|
3233 // Unreachable.
|
|
ferencd@0
|
3234 assert(false);
|
|
ferencd@0
|
3235 exit(EXIT_FAILURE);
|
|
ferencd@0
|
3236 return EXIT_FAILURE;
|
|
ferencd@0
|
3237 }
|
|
ferencd@0
|
3238 # endif // !USE_SHELL_API
|
|
ferencd@0
|
3239
|
|
ferencd@0
|
3240 static
|
|
ferencd@0
|
3241 bool
|
|
ferencd@0
|
3242 extract_gz_file(const std::string&, const std::string& gz_file, const std::string&)
|
|
ferencd@0
|
3243 {
|
|
ferencd@0
|
3244 # if USE_SHELL_API
|
|
ferencd@0
|
3245 bool unzipped = std::system(("tar -xzf " + gz_file + " -C " + get_install()).c_str()) == EXIT_SUCCESS;
|
|
ferencd@0
|
3246 # else // !USE_SHELL_API
|
|
ferencd@0
|
3247 const char prog[] = {"/usr/bin/tar"};
|
|
ferencd@0
|
3248 const char*const args[] =
|
|
ferencd@0
|
3249 {
|
|
ferencd@0
|
3250 prog, "-xzf", gz_file.c_str(), "-C", get_install().c_str(), nullptr
|
|
ferencd@0
|
3251 };
|
|
ferencd@0
|
3252 bool unzipped = (run_program(prog, args) == EXIT_SUCCESS);
|
|
ferencd@0
|
3253 # endif // !USE_SHELL_API
|
|
ferencd@0
|
3254 if (unzipped)
|
|
ferencd@0
|
3255 {
|
|
ferencd@0
|
3256 delete_file(gz_file);
|
|
ferencd@0
|
3257 return true;
|
|
ferencd@0
|
3258 }
|
|
ferencd@0
|
3259 return false;
|
|
ferencd@0
|
3260 }
|
|
ferencd@0
|
3261
|
|
ferencd@0
|
3262 # endif // !_WIN32
|
|
ferencd@0
|
3263
|
|
ferencd@0
|
3264 bool
|
|
ferencd@0
|
3265 remote_download(const std::string& version)
|
|
ferencd@0
|
3266 {
|
|
ferencd@0
|
3267 assert(!version.empty());
|
|
ferencd@0
|
3268
|
|
ferencd@0
|
3269 # ifdef _WIN32
|
|
ferencd@0
|
3270 // Download folder should be always available for Windows
|
|
ferencd@0
|
3271 # else // !_WIN32
|
|
ferencd@0
|
3272 // Create download folder if it does not exist on UNIX system
|
|
ferencd@0
|
3273 auto download_folder = get_install();
|
|
ferencd@0
|
3274 if (!file_exists(download_folder))
|
|
ferencd@0
|
3275 {
|
|
ferencd@0
|
3276 if (!make_directory(download_folder))
|
|
ferencd@0
|
3277 return false;
|
|
ferencd@0
|
3278 }
|
|
ferencd@0
|
3279 # endif // _WIN32
|
|
ferencd@0
|
3280
|
|
ferencd@0
|
3281 auto url = "https://data.iana.org/time-zones/releases/tzdata" + version +
|
|
ferencd@0
|
3282 ".tar.gz";
|
|
ferencd@0
|
3283 bool result = download_to_file(url, get_download_gz_file(version),
|
|
ferencd@0
|
3284 download_file_options::binary);
|
|
ferencd@0
|
3285 # ifdef _WIN32
|
|
ferencd@0
|
3286 if (result)
|
|
ferencd@0
|
3287 {
|
|
ferencd@0
|
3288 auto mapping_file = get_download_mapping_file(version);
|
|
ferencd@0
|
3289 result = download_to_file(
|
|
ferencd@0
|
3290 "https://raw.githubusercontent.com/unicode-org/cldr/master/"
|
|
ferencd@0
|
3291 "common/supplemental/windowsZones.xml",
|
|
ferencd@0
|
3292 mapping_file, download_file_options::text);
|
|
ferencd@0
|
3293 }
|
|
ferencd@0
|
3294 # endif // _WIN32
|
|
ferencd@0
|
3295 return result;
|
|
ferencd@0
|
3296 }
|
|
ferencd@0
|
3297
|
|
ferencd@0
|
3298 bool
|
|
ferencd@0
|
3299 remote_install(const std::string& version)
|
|
ferencd@0
|
3300 {
|
|
ferencd@0
|
3301 auto success = false;
|
|
ferencd@0
|
3302 assert(!version.empty());
|
|
ferencd@0
|
3303
|
|
ferencd@0
|
3304 std::string install = get_install();
|
|
ferencd@0
|
3305 auto gz_file = get_download_gz_file(version);
|
|
ferencd@0
|
3306 if (file_exists(gz_file))
|
|
ferencd@0
|
3307 {
|
|
ferencd@0
|
3308 if (file_exists(install))
|
|
ferencd@0
|
3309 remove_folder_and_subfolders(install);
|
|
ferencd@0
|
3310 if (make_directory(install))
|
|
ferencd@0
|
3311 {
|
|
ferencd@0
|
3312 if (extract_gz_file(version, gz_file, install))
|
|
ferencd@0
|
3313 success = true;
|
|
ferencd@0
|
3314 # ifdef _WIN32
|
|
ferencd@0
|
3315 auto mapping_file_source = get_download_mapping_file(version);
|
|
ferencd@0
|
3316 auto mapping_file_dest = get_install();
|
|
ferencd@0
|
3317 mapping_file_dest += folder_delimiter;
|
|
ferencd@0
|
3318 mapping_file_dest += "windowsZones.xml";
|
|
ferencd@0
|
3319 if (!move_file(mapping_file_source, mapping_file_dest))
|
|
ferencd@0
|
3320 success = false;
|
|
ferencd@0
|
3321 # endif // _WIN32
|
|
ferencd@0
|
3322 }
|
|
ferencd@0
|
3323 }
|
|
ferencd@0
|
3324 return success;
|
|
ferencd@0
|
3325 }
|
|
ferencd@0
|
3326
|
|
ferencd@0
|
3327 #endif // HAS_REMOTE_API
|
|
ferencd@0
|
3328
|
|
ferencd@0
|
3329 static
|
|
ferencd@0
|
3330 std::string
|
|
ferencd@0
|
3331 get_version(const std::string& path)
|
|
ferencd@0
|
3332 {
|
|
ferencd@0
|
3333 std::string version;
|
|
ferencd@0
|
3334 std::ifstream infile(path + "version");
|
|
ferencd@0
|
3335 if (infile.is_open())
|
|
ferencd@0
|
3336 {
|
|
ferencd@0
|
3337 infile >> version;
|
|
ferencd@0
|
3338 if (!infile.fail())
|
|
ferencd@0
|
3339 return version;
|
|
ferencd@0
|
3340 }
|
|
ferencd@0
|
3341 else
|
|
ferencd@0
|
3342 {
|
|
ferencd@0
|
3343 infile.open(path + "NEWS");
|
|
ferencd@0
|
3344 while (infile)
|
|
ferencd@0
|
3345 {
|
|
ferencd@0
|
3346 infile >> version;
|
|
ferencd@0
|
3347 if (version == "Release")
|
|
ferencd@0
|
3348 {
|
|
ferencd@0
|
3349 infile >> version;
|
|
ferencd@0
|
3350 return version;
|
|
ferencd@0
|
3351 }
|
|
ferencd@0
|
3352 }
|
|
ferencd@0
|
3353 }
|
|
ferencd@0
|
3354 throw std::runtime_error("Unable to get Timezone database version from " + path);
|
|
ferencd@0
|
3355 }
|
|
ferencd@0
|
3356
|
|
ferencd@0
|
3357 static
|
|
ferencd@0
|
3358 std::unique_ptr<tzdb>
|
|
ferencd@0
|
3359 init_tzdb()
|
|
ferencd@0
|
3360 {
|
|
ferencd@0
|
3361 using namespace date;
|
|
ferencd@0
|
3362 const std::string install = get_install();
|
|
ferencd@0
|
3363 const std::string path = install + folder_delimiter;
|
|
ferencd@0
|
3364 std::string line;
|
|
ferencd@0
|
3365 bool continue_zone = false;
|
|
ferencd@0
|
3366 std::unique_ptr<tzdb> db(new tzdb);
|
|
ferencd@0
|
3367
|
|
ferencd@0
|
3368 #if AUTO_DOWNLOAD
|
|
ferencd@0
|
3369 if (!file_exists(install))
|
|
ferencd@0
|
3370 {
|
|
ferencd@0
|
3371 auto rv = remote_version();
|
|
ferencd@0
|
3372 if (!rv.empty() && remote_download(rv))
|
|
ferencd@0
|
3373 {
|
|
ferencd@0
|
3374 if (!remote_install(rv))
|
|
ferencd@0
|
3375 {
|
|
ferencd@0
|
3376 std::string msg = "Timezone database version \"";
|
|
ferencd@0
|
3377 msg += rv;
|
|
ferencd@0
|
3378 msg += "\" did not install correctly to \"";
|
|
ferencd@0
|
3379 msg += install;
|
|
ferencd@0
|
3380 msg += "\"";
|
|
ferencd@0
|
3381 throw std::runtime_error(msg);
|
|
ferencd@0
|
3382 }
|
|
ferencd@0
|
3383 }
|
|
ferencd@0
|
3384 if (!file_exists(install))
|
|
ferencd@0
|
3385 {
|
|
ferencd@0
|
3386 std::string msg = "Timezone database not found at \"";
|
|
ferencd@0
|
3387 msg += install;
|
|
ferencd@0
|
3388 msg += "\"";
|
|
ferencd@0
|
3389 throw std::runtime_error(msg);
|
|
ferencd@0
|
3390 }
|
|
ferencd@0
|
3391 db->version = get_version(path);
|
|
ferencd@0
|
3392 }
|
|
ferencd@0
|
3393 else
|
|
ferencd@0
|
3394 {
|
|
ferencd@0
|
3395 db->version = get_version(path);
|
|
ferencd@0
|
3396 auto rv = remote_version();
|
|
ferencd@0
|
3397 if (!rv.empty() && db->version != rv)
|
|
ferencd@0
|
3398 {
|
|
ferencd@0
|
3399 if (remote_download(rv))
|
|
ferencd@0
|
3400 {
|
|
ferencd@0
|
3401 remote_install(rv);
|
|
ferencd@0
|
3402 db->version = get_version(path);
|
|
ferencd@0
|
3403 }
|
|
ferencd@0
|
3404 }
|
|
ferencd@0
|
3405 }
|
|
ferencd@0
|
3406 #else // !AUTO_DOWNLOAD
|
|
ferencd@0
|
3407 if (!file_exists(install))
|
|
ferencd@0
|
3408 {
|
|
ferencd@0
|
3409 std::string msg = "Timezone database not found at \"";
|
|
ferencd@0
|
3410 msg += install;
|
|
ferencd@0
|
3411 msg += "\"";
|
|
ferencd@0
|
3412 throw std::runtime_error(msg);
|
|
ferencd@0
|
3413 }
|
|
ferencd@0
|
3414 db->version = get_version(path);
|
|
ferencd@0
|
3415 #endif // !AUTO_DOWNLOAD
|
|
ferencd@0
|
3416
|
|
ferencd@0
|
3417 CONSTDATA char*const files[] =
|
|
ferencd@0
|
3418 {
|
|
ferencd@0
|
3419 "africa", "antarctica", "asia", "australasia", "backward", "etcetera", "europe",
|
|
ferencd@0
|
3420 "pacificnew", "northamerica", "southamerica", "systemv", "leapseconds"
|
|
ferencd@0
|
3421 };
|
|
ferencd@0
|
3422
|
|
ferencd@0
|
3423 for (const auto& filename : files)
|
|
ferencd@0
|
3424 {
|
|
ferencd@0
|
3425 std::ifstream infile(path + filename);
|
|
ferencd@0
|
3426 while (infile)
|
|
ferencd@0
|
3427 {
|
|
ferencd@0
|
3428 std::getline(infile, line);
|
|
ferencd@0
|
3429 if (!line.empty() && line[0] != '#')
|
|
ferencd@0
|
3430 {
|
|
ferencd@0
|
3431 std::istringstream in(line);
|
|
ferencd@0
|
3432 std::string word;
|
|
ferencd@0
|
3433 in >> word;
|
|
ferencd@0
|
3434 if (word == "Rule")
|
|
ferencd@0
|
3435 {
|
|
ferencd@0
|
3436 db->rules.push_back(Rule(line));
|
|
ferencd@0
|
3437 continue_zone = false;
|
|
ferencd@0
|
3438 }
|
|
ferencd@0
|
3439 else if (word == "Link")
|
|
ferencd@0
|
3440 {
|
|
ferencd@0
|
3441 db->links.push_back(link(line));
|
|
ferencd@0
|
3442 continue_zone = false;
|
|
ferencd@0
|
3443 }
|
|
ferencd@0
|
3444 else if (word == "Leap")
|
|
ferencd@0
|
3445 {
|
|
ferencd@0
|
3446 db->leaps.push_back(leap(line, detail::undocumented{}));
|
|
ferencd@0
|
3447 continue_zone = false;
|
|
ferencd@0
|
3448 }
|
|
ferencd@0
|
3449 else if (word == "Zone")
|
|
ferencd@0
|
3450 {
|
|
ferencd@0
|
3451 db->zones.push_back(time_zone(line, detail::undocumented{}));
|
|
ferencd@0
|
3452 continue_zone = true;
|
|
ferencd@0
|
3453 }
|
|
ferencd@0
|
3454 else if (line[0] == '\t' && continue_zone)
|
|
ferencd@0
|
3455 {
|
|
ferencd@0
|
3456 db->zones.back().add(line);
|
|
ferencd@0
|
3457 }
|
|
ferencd@0
|
3458 else
|
|
ferencd@0
|
3459 {
|
|
ferencd@0
|
3460 std::cerr << line << '\n';
|
|
ferencd@0
|
3461 }
|
|
ferencd@0
|
3462 }
|
|
ferencd@0
|
3463 }
|
|
ferencd@0
|
3464 }
|
|
ferencd@0
|
3465 std::sort(db->rules.begin(), db->rules.end());
|
|
ferencd@0
|
3466 Rule::split_overlaps(db->rules);
|
|
ferencd@0
|
3467 std::sort(db->zones.begin(), db->zones.end());
|
|
ferencd@0
|
3468 db->zones.shrink_to_fit();
|
|
ferencd@0
|
3469 std::sort(db->links.begin(), db->links.end());
|
|
ferencd@0
|
3470 db->links.shrink_to_fit();
|
|
ferencd@0
|
3471 std::sort(db->leaps.begin(), db->leaps.end());
|
|
ferencd@0
|
3472 db->leaps.shrink_to_fit();
|
|
ferencd@0
|
3473
|
|
ferencd@0
|
3474 #ifdef _WIN32
|
|
ferencd@0
|
3475 std::string mapping_file = get_install() + folder_delimiter + "windowsZones.xml";
|
|
ferencd@0
|
3476 db->mappings = load_timezone_mappings_from_xml_file(mapping_file);
|
|
ferencd@0
|
3477 sort_zone_mappings(db->mappings);
|
|
ferencd@0
|
3478 #endif // _WIN32
|
|
ferencd@0
|
3479
|
|
ferencd@0
|
3480 return db;
|
|
ferencd@0
|
3481 }
|
|
ferencd@0
|
3482
|
|
ferencd@0
|
3483 const tzdb&
|
|
ferencd@0
|
3484 reload_tzdb()
|
|
ferencd@0
|
3485 {
|
|
ferencd@0
|
3486 #if AUTO_DOWNLOAD
|
|
ferencd@0
|
3487 auto const& v = get_tzdb_list().front().version;
|
|
ferencd@0
|
3488 if (!v.empty() && v == remote_version())
|
|
ferencd@0
|
3489 return get_tzdb_list().front();
|
|
ferencd@0
|
3490 #endif // AUTO_DOWNLOAD
|
|
ferencd@0
|
3491 tzdb_list::undocumented_helper::push_front(get_tzdb_list(), init_tzdb().release());
|
|
ferencd@0
|
3492 return get_tzdb_list().front();
|
|
ferencd@0
|
3493 }
|
|
ferencd@0
|
3494
|
|
ferencd@0
|
3495 #endif // !USE_OS_TZDB
|
|
ferencd@0
|
3496
|
|
ferencd@0
|
3497 const tzdb&
|
|
ferencd@0
|
3498 get_tzdb()
|
|
ferencd@0
|
3499 {
|
|
ferencd@0
|
3500 return get_tzdb_list().front();
|
|
ferencd@0
|
3501 }
|
|
ferencd@0
|
3502
|
|
ferencd@0
|
3503 const time_zone*
|
|
ferencd@0
|
3504 #if HAS_STRING_VIEW
|
|
ferencd@0
|
3505 tzdb::locate_zone(std::string_view tz_name) const
|
|
ferencd@0
|
3506 #else
|
|
ferencd@0
|
3507 tzdb::locate_zone(const std::string& tz_name) const
|
|
ferencd@0
|
3508 #endif
|
|
ferencd@0
|
3509 {
|
|
ferencd@0
|
3510 auto zi = std::lower_bound(zones.begin(), zones.end(), tz_name,
|
|
ferencd@0
|
3511 #if HAS_STRING_VIEW
|
|
ferencd@0
|
3512 [](const time_zone& z, const std::string_view& nm)
|
|
ferencd@0
|
3513 #else
|
|
ferencd@0
|
3514 [](const time_zone& z, const std::string& nm)
|
|
ferencd@0
|
3515 #endif
|
|
ferencd@0
|
3516 {
|
|
ferencd@0
|
3517 return z.name() < nm;
|
|
ferencd@0
|
3518 });
|
|
ferencd@0
|
3519 if (zi == zones.end() || zi->name() != tz_name)
|
|
ferencd@0
|
3520 {
|
|
ferencd@0
|
3521 #if !USE_OS_TZDB
|
|
ferencd@0
|
3522 auto li = std::lower_bound(links.begin(), links.end(), tz_name,
|
|
ferencd@0
|
3523 #if HAS_STRING_VIEW
|
|
ferencd@0
|
3524 [](const link& z, const std::string_view& nm)
|
|
ferencd@0
|
3525 #else
|
|
ferencd@0
|
3526 [](const link& z, const std::string& nm)
|
|
ferencd@0
|
3527 #endif
|
|
ferencd@0
|
3528 {
|
|
ferencd@0
|
3529 return z.name() < nm;
|
|
ferencd@0
|
3530 });
|
|
ferencd@0
|
3531 if (li != links.end() && li->name() == tz_name)
|
|
ferencd@0
|
3532 {
|
|
ferencd@0
|
3533 zi = std::lower_bound(zones.begin(), zones.end(), li->target(),
|
|
ferencd@0
|
3534 [](const time_zone& z, const std::string& nm)
|
|
ferencd@0
|
3535 {
|
|
ferencd@0
|
3536 return z.name() < nm;
|
|
ferencd@0
|
3537 });
|
|
ferencd@0
|
3538 if (zi != zones.end() && zi->name() == li->target())
|
|
ferencd@0
|
3539 return &*zi;
|
|
ferencd@0
|
3540 }
|
|
ferencd@0
|
3541 #endif // !USE_OS_TZDB
|
|
ferencd@0
|
3542 throw std::runtime_error(std::string(tz_name) + " not found in timezone database");
|
|
ferencd@0
|
3543 }
|
|
ferencd@0
|
3544 return &*zi;
|
|
ferencd@0
|
3545 }
|
|
ferencd@0
|
3546
|
|
ferencd@0
|
3547 const time_zone*
|
|
ferencd@0
|
3548 #if HAS_STRING_VIEW
|
|
ferencd@0
|
3549 locate_zone(std::string_view tz_name)
|
|
ferencd@0
|
3550 #else
|
|
ferencd@0
|
3551 locate_zone(const std::string& tz_name)
|
|
ferencd@0
|
3552 #endif
|
|
ferencd@0
|
3553 {
|
|
ferencd@0
|
3554 return get_tzdb().locate_zone(tz_name);
|
|
ferencd@0
|
3555 }
|
|
ferencd@0
|
3556
|
|
ferencd@0
|
3557 #if USE_OS_TZDB
|
|
ferencd@0
|
3558
|
|
ferencd@0
|
3559 std::ostream&
|
|
ferencd@0
|
3560 operator<<(std::ostream& os, const tzdb& db)
|
|
ferencd@0
|
3561 {
|
|
ferencd@0
|
3562 os << "Version: " << db.version << "\n\n";
|
|
ferencd@0
|
3563 for (const auto& x : db.zones)
|
|
ferencd@0
|
3564 os << x << '\n';
|
|
ferencd@0
|
3565 #if !MISSING_LEAP_SECONDS
|
|
ferencd@0
|
3566 os << '\n';
|
|
ferencd@0
|
3567 for (const auto& x : db.leaps)
|
|
ferencd@0
|
3568 os << x << '\n';
|
|
ferencd@0
|
3569 #endif // !MISSING_LEAP_SECONDS
|
|
ferencd@0
|
3570 return os;
|
|
ferencd@0
|
3571 }
|
|
ferencd@0
|
3572
|
|
ferencd@0
|
3573 #else // !USE_OS_TZDB
|
|
ferencd@0
|
3574
|
|
ferencd@0
|
3575 std::ostream&
|
|
ferencd@0
|
3576 operator<<(std::ostream& os, const tzdb& db)
|
|
ferencd@0
|
3577 {
|
|
ferencd@0
|
3578 os << "Version: " << db.version << '\n';
|
|
ferencd@0
|
3579 std::string title("--------------------------------------------"
|
|
ferencd@0
|
3580 "--------------------------------------------\n"
|
|
ferencd@0
|
3581 "Name ""Start Y ""End Y "
|
|
ferencd@0
|
3582 "Beginning ""Offset "
|
|
ferencd@0
|
3583 "Designator\n"
|
|
ferencd@0
|
3584 "--------------------------------------------"
|
|
ferencd@0
|
3585 "--------------------------------------------\n");
|
|
ferencd@0
|
3586 int count = 0;
|
|
ferencd@0
|
3587 for (const auto& x : db.rules)
|
|
ferencd@0
|
3588 {
|
|
ferencd@0
|
3589 if (count++ % 50 == 0)
|
|
ferencd@0
|
3590 os << title;
|
|
ferencd@0
|
3591 os << x << '\n';
|
|
ferencd@0
|
3592 }
|
|
ferencd@0
|
3593 os << '\n';
|
|
ferencd@0
|
3594 title = std::string("---------------------------------------------------------"
|
|
ferencd@0
|
3595 "--------------------------------------------------------\n"
|
|
ferencd@0
|
3596 "Name ""Offset "
|
|
ferencd@0
|
3597 "Rule ""Abrev ""Until\n"
|
|
ferencd@0
|
3598 "---------------------------------------------------------"
|
|
ferencd@0
|
3599 "--------------------------------------------------------\n");
|
|
ferencd@0
|
3600 count = 0;
|
|
ferencd@0
|
3601 for (const auto& x : db.zones)
|
|
ferencd@0
|
3602 {
|
|
ferencd@0
|
3603 if (count++ % 10 == 0)
|
|
ferencd@0
|
3604 os << title;
|
|
ferencd@0
|
3605 os << x << '\n';
|
|
ferencd@0
|
3606 }
|
|
ferencd@0
|
3607 os << '\n';
|
|
ferencd@0
|
3608 title = std::string("---------------------------------------------------------"
|
|
ferencd@0
|
3609 "--------------------------------------------------------\n"
|
|
ferencd@0
|
3610 "Alias ""To\n"
|
|
ferencd@0
|
3611 "---------------------------------------------------------"
|
|
ferencd@0
|
3612 "--------------------------------------------------------\n");
|
|
ferencd@0
|
3613 count = 0;
|
|
ferencd@0
|
3614 for (const auto& x : db.links)
|
|
ferencd@0
|
3615 {
|
|
ferencd@0
|
3616 if (count++ % 45 == 0)
|
|
ferencd@0
|
3617 os << title;
|
|
ferencd@0
|
3618 os << x << '\n';
|
|
ferencd@0
|
3619 }
|
|
ferencd@0
|
3620 os << '\n';
|
|
ferencd@0
|
3621 title = std::string("---------------------------------------------------------"
|
|
ferencd@0
|
3622 "--------------------------------------------------------\n"
|
|
ferencd@0
|
3623 "Leap second on\n"
|
|
ferencd@0
|
3624 "---------------------------------------------------------"
|
|
ferencd@0
|
3625 "--------------------------------------------------------\n");
|
|
ferencd@0
|
3626 os << title;
|
|
ferencd@0
|
3627 for (const auto& x : db.leaps)
|
|
ferencd@0
|
3628 os << x << '\n';
|
|
ferencd@0
|
3629 return os;
|
|
ferencd@0
|
3630 }
|
|
ferencd@0
|
3631
|
|
ferencd@0
|
3632 #endif // !USE_OS_TZDB
|
|
ferencd@0
|
3633
|
|
ferencd@0
|
3634 // -----------------------
|
|
ferencd@0
|
3635
|
|
ferencd@0
|
3636 #ifdef _WIN32
|
|
ferencd@0
|
3637
|
|
ferencd@0
|
3638 static
|
|
ferencd@0
|
3639 std::string
|
|
ferencd@0
|
3640 getTimeZoneKeyName()
|
|
ferencd@0
|
3641 {
|
|
ferencd@0
|
3642 DYNAMIC_TIME_ZONE_INFORMATION dtzi{};
|
|
ferencd@0
|
3643 auto result = GetDynamicTimeZoneInformation(&dtzi);
|
|
ferencd@0
|
3644 if (result == TIME_ZONE_ID_INVALID)
|
|
ferencd@0
|
3645 throw std::runtime_error("current_zone(): GetDynamicTimeZoneInformation()"
|
|
ferencd@0
|
3646 " reported TIME_ZONE_ID_INVALID.");
|
|
ferencd@0
|
3647 auto wlen = wcslen(dtzi.TimeZoneKeyName);
|
|
ferencd@0
|
3648 char buf[128] = {};
|
|
ferencd@0
|
3649 assert(sizeof(buf) >= wlen+1);
|
|
ferencd@0
|
3650 wcstombs(buf, dtzi.TimeZoneKeyName, wlen);
|
|
ferencd@0
|
3651 if (strcmp(buf, "Coordinated Universal Time") == 0)
|
|
ferencd@0
|
3652 return "UTC";
|
|
ferencd@0
|
3653 return buf;
|
|
ferencd@0
|
3654 }
|
|
ferencd@0
|
3655
|
|
ferencd@0
|
3656 const time_zone*
|
|
ferencd@0
|
3657 tzdb::current_zone() const
|
|
ferencd@0
|
3658 {
|
|
ferencd@0
|
3659 std::string win_tzid = getTimeZoneKeyName();
|
|
ferencd@0
|
3660 std::string standard_tzid;
|
|
ferencd@0
|
3661 if (!native_to_standard_timezone_name(win_tzid, standard_tzid))
|
|
ferencd@0
|
3662 {
|
|
ferencd@0
|
3663 std::string msg;
|
|
ferencd@0
|
3664 msg = "current_zone() failed: A mapping from the Windows Time Zone id \"";
|
|
ferencd@0
|
3665 msg += win_tzid;
|
|
ferencd@0
|
3666 msg += "\" was not found in the time zone mapping database.";
|
|
ferencd@0
|
3667 throw std::runtime_error(msg);
|
|
ferencd@0
|
3668 }
|
|
ferencd@0
|
3669 return locate_zone(standard_tzid);
|
|
ferencd@0
|
3670 }
|
|
ferencd@0
|
3671
|
|
ferencd@0
|
3672 #else // !_WIN32
|
|
ferencd@0
|
3673
|
|
ferencd@0
|
3674 const time_zone*
|
|
ferencd@0
|
3675 tzdb::current_zone() const
|
|
ferencd@0
|
3676 {
|
|
ferencd@0
|
3677 // On some OS's a file called /etc/localtime may
|
|
ferencd@0
|
3678 // exist and it may be either a real file
|
|
ferencd@0
|
3679 // containing time zone details or a symlink to such a file.
|
|
ferencd@0
|
3680 // On MacOS and BSD Unix if this file is a symlink it
|
|
ferencd@0
|
3681 // might resolve to a path like this:
|
|
ferencd@0
|
3682 // "/usr/share/zoneinfo/America/Los_Angeles"
|
|
ferencd@0
|
3683 // If it does, we try to determine the current
|
|
ferencd@0
|
3684 // timezone from the remainder of the path by removing the prefix
|
|
ferencd@0
|
3685 // and hoping the rest resolves to a valid timezone.
|
|
ferencd@0
|
3686 // It may not always work though. If it doesn't then an
|
|
ferencd@0
|
3687 // exception will be thrown by local_timezone.
|
|
ferencd@0
|
3688 // The path may also take a relative form:
|
|
ferencd@0
|
3689 // "../usr/share/zoneinfo/America/Los_Angeles".
|
|
ferencd@0
|
3690 {
|
|
ferencd@0
|
3691 struct stat sb;
|
|
ferencd@0
|
3692 CONSTDATA auto timezone = "/etc/localtime";
|
|
ferencd@0
|
3693 if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) {
|
|
ferencd@0
|
3694 using namespace std;
|
|
ferencd@0
|
3695 char rp[PATH_MAX+1] = {};
|
|
ferencd@0
|
3696 if (realpath(timezone, rp) == nullptr)
|
|
ferencd@0
|
3697 throw system_error(errno, system_category(), "realpath() failed");
|
|
ferencd@0
|
3698 #if HAS_STRING_VIEW
|
|
ferencd@0
|
3699 string_view result = rp;
|
|
ferencd@0
|
3700 CONSTDATA string_view zoneinfo = "/zoneinfo/";
|
|
ferencd@0
|
3701 const size_t pos = result.rfind(zoneinfo);
|
|
ferencd@0
|
3702 if (pos == result.npos)
|
|
ferencd@0
|
3703 throw runtime_error(
|
|
ferencd@0
|
3704 "current_zone() failed to find \"/zoneinfo/\" in " + string(result));
|
|
ferencd@0
|
3705 result.remove_prefix(pos + zoneinfo.size());
|
|
ferencd@0
|
3706 #else
|
|
ferencd@0
|
3707 string result = rp;
|
|
ferencd@0
|
3708 CONSTDATA char zoneinfo[] = "/zoneinfo/";
|
|
ferencd@0
|
3709 const size_t pos = result.rfind(zoneinfo);
|
|
ferencd@0
|
3710 if (pos == result.npos)
|
|
ferencd@0
|
3711 throw runtime_error(
|
|
ferencd@0
|
3712 "current_zone() failed to find \"/zoneinfo/\" in " + result);
|
|
ferencd@0
|
3713 result.erase(0, pos + sizeof(zoneinfo) - 1);
|
|
ferencd@0
|
3714 #endif
|
|
ferencd@0
|
3715 return locate_zone(result);
|
|
ferencd@0
|
3716 }
|
|
ferencd@0
|
3717 }
|
|
ferencd@0
|
3718 // On embedded systems e.g. buildroot with uclibc the timezone is linked
|
|
ferencd@0
|
3719 // into /etc/TZ which is a symlink to path like this:
|
|
ferencd@0
|
3720 // "/usr/share/zoneinfo/uclibc/America/Los_Angeles"
|
|
ferencd@0
|
3721 // If it does, we try to determine the current
|
|
ferencd@0
|
3722 // timezone from the remainder of the path by removing the prefix
|
|
ferencd@0
|
3723 // and hoping the rest resolves to valid timezone.
|
|
ferencd@0
|
3724 // It may not always work though. If it doesn't then an
|
|
ferencd@0
|
3725 // exception will be thrown by local_timezone.
|
|
ferencd@0
|
3726 // The path may also take a relative form:
|
|
ferencd@0
|
3727 // "../usr/share/zoneinfo/uclibc/America/Los_Angeles".
|
|
ferencd@0
|
3728 {
|
|
ferencd@0
|
3729 struct stat sb;
|
|
ferencd@0
|
3730 CONSTDATA auto timezone = "/etc/TZ";
|
|
ferencd@0
|
3731 if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) {
|
|
ferencd@0
|
3732 using namespace std;
|
|
ferencd@0
|
3733 string result;
|
|
ferencd@0
|
3734 char rp[PATH_MAX+1] = {};
|
|
ferencd@0
|
3735 if (readlink(timezone, rp, sizeof(rp)-1) > 0)
|
|
ferencd@0
|
3736 result = string(rp);
|
|
ferencd@0
|
3737 else
|
|
ferencd@0
|
3738 throw system_error(errno, system_category(), "readlink() failed");
|
|
ferencd@0
|
3739
|
|
ferencd@0
|
3740 const size_t pos = result.find(get_tz_dir());
|
|
ferencd@0
|
3741 if (pos != result.npos)
|
|
ferencd@0
|
3742 result.erase(0, get_tz_dir().size() + 1 + pos);
|
|
ferencd@0
|
3743 return locate_zone(result);
|
|
ferencd@0
|
3744 }
|
|
ferencd@0
|
3745 }
|
|
ferencd@0
|
3746 {
|
|
ferencd@0
|
3747 // On some versions of some linux distro's (e.g. Ubuntu),
|
|
ferencd@0
|
3748 // the current timezone might be in the first line of
|
|
ferencd@0
|
3749 // the /etc/timezone file.
|
|
ferencd@0
|
3750 std::ifstream timezone_file("/etc/timezone");
|
|
ferencd@0
|
3751 if (timezone_file.is_open())
|
|
ferencd@0
|
3752 {
|
|
ferencd@0
|
3753 std::string result;
|
|
ferencd@0
|
3754 std::getline(timezone_file, result);
|
|
ferencd@0
|
3755 if (!result.empty())
|
|
ferencd@0
|
3756 return locate_zone(result);
|
|
ferencd@0
|
3757 }
|
|
ferencd@0
|
3758 // Fall through to try other means.
|
|
ferencd@0
|
3759 }
|
|
ferencd@0
|
3760 {
|
|
ferencd@0
|
3761 // On some versions of some bsd distro's (e.g. FreeBSD),
|
|
ferencd@0
|
3762 // the current timezone might be in the first line of
|
|
ferencd@0
|
3763 // the /var/db/zoneinfo file.
|
|
ferencd@0
|
3764 std::ifstream timezone_file("/var/db/zoneinfo");
|
|
ferencd@0
|
3765 if (timezone_file.is_open())
|
|
ferencd@0
|
3766 {
|
|
ferencd@0
|
3767 std::string result;
|
|
ferencd@0
|
3768 std::getline(timezone_file, result);
|
|
ferencd@0
|
3769 if (!result.empty())
|
|
ferencd@0
|
3770 return locate_zone(result);
|
|
ferencd@0
|
3771 }
|
|
ferencd@0
|
3772 // Fall through to try other means.
|
|
ferencd@0
|
3773 }
|
|
ferencd@0
|
3774 {
|
|
ferencd@0
|
3775 // On some versions of some bsd distro's (e.g. iOS),
|
|
ferencd@0
|
3776 // it is not possible to use file based approach,
|
|
ferencd@0
|
3777 // we switch to system API, calling functions in
|
|
ferencd@0
|
3778 // CoreFoundation framework.
|
|
ferencd@0
|
3779 #if TARGET_OS_IPHONE
|
|
ferencd@0
|
3780 std::string result = date::iOSUtils::get_current_timezone();
|
|
ferencd@0
|
3781 if (!result.empty())
|
|
ferencd@0
|
3782 return locate_zone(result);
|
|
ferencd@0
|
3783 #endif
|
|
ferencd@0
|
3784 // Fall through to try other means.
|
|
ferencd@0
|
3785 }
|
|
ferencd@0
|
3786 {
|
|
ferencd@0
|
3787 // On some versions of some linux distro's (e.g. Red Hat),
|
|
ferencd@0
|
3788 // the current timezone might be in the first line of
|
|
ferencd@0
|
3789 // the /etc/sysconfig/clock file as:
|
|
ferencd@0
|
3790 // ZONE="US/Eastern"
|
|
ferencd@0
|
3791 std::ifstream timezone_file("/etc/sysconfig/clock");
|
|
ferencd@0
|
3792 std::string result;
|
|
ferencd@0
|
3793 while (timezone_file)
|
|
ferencd@0
|
3794 {
|
|
ferencd@0
|
3795 std::getline(timezone_file, result);
|
|
ferencd@0
|
3796 auto p = result.find("ZONE=\"");
|
|
ferencd@0
|
3797 if (p != std::string::npos)
|
|
ferencd@0
|
3798 {
|
|
ferencd@0
|
3799 result.erase(p, p+6);
|
|
ferencd@0
|
3800 result.erase(result.rfind('"'));
|
|
ferencd@0
|
3801 return locate_zone(result);
|
|
ferencd@0
|
3802 }
|
|
ferencd@0
|
3803 }
|
|
ferencd@0
|
3804 // Fall through to try other means.
|
|
ferencd@0
|
3805 }
|
|
ferencd@0
|
3806 throw std::runtime_error("Could not get current timezone");
|
|
ferencd@0
|
3807 }
|
|
ferencd@0
|
3808
|
|
ferencd@0
|
3809 #endif // !_WIN32
|
|
ferencd@0
|
3810
|
|
ferencd@0
|
3811 const time_zone*
|
|
ferencd@0
|
3812 current_zone()
|
|
ferencd@0
|
3813 {
|
|
ferencd@0
|
3814 return get_tzdb().current_zone();
|
|
ferencd@0
|
3815 }
|
|
ferencd@0
|
3816
|
|
ferencd@0
|
3817 } // namespace date
|
|
ferencd@0
|
3818
|
|
ferencd@0
|
3819 #if defined(__GNUC__) && __GNUC__ < 5
|
|
ferencd@0
|
3820 # pragma GCC diagnostic pop
|
|
ferencd@0
|
3821 #endif
|