43# ifndef WIN32_LEAN_AND_MEAN
44# define WIN32_LEAN_AND_MEAN
48# ifndef _CRT_SECURE_NO_WARNINGS
49# define _CRT_SECURE_NO_WARNINGS
65# define NTDDI_VERSION 0x06000000
66# define _WIN32_WINNT _WIN32_WINNT_VISTA
67# elif NTDDI_VERSION < 0x06000000
68# warning "If this fails to compile NTDDI_VERSION may be to low. See comments above."
79# include <knownfolders.h>
91# define TARGET_OS_IPHONE 0
92# define TARGET_OS_SIMULATOR 0
123# include <winapifamily.h>
124# if WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP
132# if defined(__clang__)
140# include <shellapi.h>
144# if !USE_OS_TZDB && !defined(INSTALL)
150# include <sys/stat.h>
151# include <sys/fcntl.h>
154# include <sys/wait.h>
155# include <sys/types.h>
163#if defined(_MSC_VER) && defined(SHORTENED_CURL_INCLUDE)
167# include <curl/curl.h>
177#if defined(__GNUC__) && __GNUC__ < 5
179# pragma GCC diagnostic push
180# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
190 struct task_mem_deleter
192 void operator()(
wchar_t buf[])
198 using co_task_mem_ptr = std::unique_ptr<wchar_t[], task_mem_deleter>;
205get_known_folder(
const GUID& folderid)
208 PWSTR pfolder =
nullptr;
209 HRESULT hr = SHGetKnownFolderPath(folderid, KF_FLAG_DEFAULT,
nullptr, &pfolder);
212 co_task_mem_ptr folder_ptr(pfolder);
213 const wchar_t* fptr = folder_ptr.get();
214 auto state = std::mbstate_t();
215 const auto required = std::wcsrtombs(
nullptr, &fptr, 0, &state);
216 if (required != 0 && required != std::size_t(-1))
218 folder.resize(required);
219 std::wcsrtombs(&folder[0], &fptr, folder.size(), &state);
232 return get_known_folder(FOLDERID_Downloads);
240# if !defined(INSTALL)
247 return date::iOSUtils::get_tzdata_path();
250 std::unique_ptr<::wordexp_t, void(*)(::wordexp_t*)> hold{&w, ::wordfree};
251 ::wordexp(path.c_str(), &w, 0);
253 throw std::runtime_error(
"Cannot expand path: " + path);
254 path = w.we_wordv[0];
278using namespace detail;
286 static std::string install
293# define STRINGIZEIMP(x) #x
294# define STRINGIZE(x) STRINGIZEIMP(x)
322get_download_gz_file(
const std::string&
version)
353 CONSTDATA auto tz_dir_default =
"/usr/share/zoneinfo";
354 CONSTDATA auto tz_dir_buildroot =
"/usr/share/zoneinfo/uclibc";
357 if(stat(tz_dir_buildroot, &sb) == 0 && S_ISDIR(sb.st_mode))
358 return tz_dir_buildroot;
359 else if(stat(tz_dir_default, &sb) == 0 && S_ISDIR(sb.st_mode))
360 return tz_dir_default;
362 throw runtime_error(
"discover_tz_dir failed to find zoneinfo\n");
365# if TARGET_OS_SIMULATOR
366 return "/usr/share/zoneinfo";
368 return "/var/db/timezone/zoneinfo";
371 CONSTDATA auto timezone =
"/etc/localtime";
372 if (!(lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0))
373 throw runtime_error(
"discover_tz_dir failed\n");
375 char rp[PATH_MAX+1] = {};
376 if (readlink(timezone, rp,
sizeof(rp)-1) > 0)
379 throw system_error(errno, system_category(),
"readlink() failed");
380 auto i = result.find(
"zoneinfo");
381 if (i == string::npos)
382 throw runtime_error(
"discover_tz_dir failed to find zoneinfo\n");
383 i = result.find(
'/', i);
384 if (i == string::npos)
385 throw runtime_error(
"discover_tz_dir failed to find '/'\n");
386 return result.substr(0, i);
415 while (ptr !=
nullptr)
417 auto next = ptr->
next;
424 : head_{x.head_.exchange(
nullptr)}
439 p.p_->next = p.p_->next->next;
448 db_list.push_front(
tzdb);
472 std::string r(3,
' ');
474 r[0] =
static_cast<char>(in.get());
475 r[1] =
static_cast<char>(in.get());
476 r[2] =
static_cast<char>(in.get());
485 {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
486 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"};
488 auto m = std::find(std::begin(month_names), std::end(month_names), s) - month_names;
489 if (m >= std::end(month_names) - std::begin(month_names))
490 throw std::runtime_error(
"oops: bad month name: " + s);
491 return static_cast<unsigned>(++m);
500sort_zone_mappings(std::vector<date::detail::timezone_mapping>& mappings)
502 std::sort(mappings.begin(), mappings.end(),
503 [](
const date::detail::timezone_mapping& lhs,
504 const date::detail::timezone_mapping& rhs)->bool
506 auto other_result = lhs.other.compare(rhs.other);
507 if (other_result < 0)
509 else if (other_result == 0)
511 auto territory_result = lhs.territory.compare(rhs.territory);
512 if (territory_result < 0)
514 else if (territory_result == 0)
516 if (lhs.type < rhs.type)
526native_to_standard_timezone_name(
const std::string& native_tz_name,
527 std::string& standard_tz_name)
530 if (native_tz_name ==
"UTC")
532 standard_tz_name =
"Etc/UTC";
535 standard_tz_name.clear();
538 for (
const auto& tzm : mappings)
540 if (tzm.other == native_tz_name)
542 standard_tz_name = tzm.type;
555std::vector<detail::timezone_mapping>
556load_timezone_mappings_from_xml_file(
const std::string& input_path)
558 std::size_t line_num = 0;
559 std::vector<detail::timezone_mapping> mappings;
562 std::ifstream is(input_path);
566 std::string msg =
"Error opening time zone mapping file \"";
569 throw std::runtime_error(msg);
572 auto error = [&input_path, &line_num](
const char* info)
574 std::string msg =
"Error loading time zone mapping file \"";
576 msg +=
"\" at line ";
577 msg += std::to_string(line_num);
580 throw std::runtime_error(msg);
583 auto read_attribute = [&line, &error]
584 (
const char*
name, std::string& value, std::size_t startPos)
589 std::size_t spos = line.find_first_not_of(
' ', startPos);
590 if (spos == std::string::npos)
594 std::size_t epos = line.find(
'=', spos);
595 if (epos == std::string::npos)
596 error(
"Expected \'=\' right after attribute name.");
597 std::size_t name_len = epos - spos;
599 if (line.compare(spos, name_len,
name) != 0)
602 msg =
"Expected attribute name \'";
604 msg +=
"\' around position ";
605 msg += std::to_string(spos);
606 msg +=
" but found something else.";
611 if (spos < line.length() && line[spos] ==
'\"')
615 std::string msg =
"Expected '\"' to begin value of attribute \'";
620 epos = line.find(
'\"', spos);
621 if (epos == std::string::npos)
623 std::string msg =
"Expected '\"' to end value of attribute \'";
629 std::size_t value_len = epos - spos;
630 value.assign(line, spos, value_len);
636 bool mapTimezonesOpenTagFound =
false;
637 bool mapTimezonesCloseTagFound =
false;
638 std::size_t mapZonePos = std::string::npos;
639 std::size_t mapTimezonesPos = std::string::npos;
640 CONSTDATA char mapTimeZonesOpeningTag[] = {
"<mapTimezones " };
641 CONSTDATA char mapZoneOpeningTag[] = {
"<mapZone " };
642 CONSTDATA std::size_t mapZoneOpeningTagLen =
sizeof(mapZoneOpeningTag) /
643 sizeof(mapZoneOpeningTag[0]) - 1;
644 while (!mapTimezonesOpenTagFound)
646 std::getline(is, line);
658 error(
"Expected a mapTimezones opening tag.");
660 mapTimezonesPos = line.find(mapTimeZonesOpeningTag);
661 mapTimezonesOpenTagFound = (mapTimezonesPos != std::string::npos);
668 while (!mapTimezonesCloseTagFound)
671 std::getline(is, line);
674 error(
"Expected a mapTimezones closing tag.");
677 mapZonePos = line.find(mapZoneOpeningTag);
678 if (mapZonePos != std::string::npos)
680 mapZonePos += mapZoneOpeningTagLen;
681 detail::timezone_mapping zm{};
682 std::size_t pos = read_attribute(
"other", zm.other, mapZonePos);
683 pos = read_attribute(
"territory", zm.territory, pos);
684 read_attribute(
"type", zm.type, pos);
685 mappings.push_back(std::move(zm));
689 mapTimezonesPos = line.find(
"</mapTimezones>");
690 mapTimezonesCloseTagFound = (mapTimezonesPos != std::string::npos);
691 if (!mapTimezonesCloseTagFound)
693 std::size_t commentPos = line.find(
"<!--");
694 if (commentPos == std::string::npos)
695 error(
"Unexpected mapping record found. A xml mapZone or comment "
696 "attribute or mapTimezones closing tag was expected.");
713 {
"Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat"};
715 auto dow = std::find(std::begin(dow_names), std::end(dow_names), s) - dow_names;
716 if (dow >= std::end(dow_names) - std::begin(dow_names))
717 throw std::runtime_error(
"oops: bad dow name: " + s);
718 return static_cast<unsigned>(dow);
725 using namespace std::chrono;
728 auto r = seconds{hours{x}};
729 if (!in.eof() && in.peek() ==
':')
734 if (!in.eof() && in.peek() ==
':')
750 if (in.peek() ==
'-')
755 else if (in.peek() ==
'+')
765 using namespace date;
766 const auto dp = date::floor<days>(tp);
769 u = ymd.month() / ymd.day();
787 return u.month_day_.
day();
794 return u.month_day_weekday_.month_day_.
day();
803 return u.month_day_.
month();
805 return u.month_weekday_last_.
month();
810 return u.month_day_weekday_.month_day_.month();
815 std::chrono::seconds offset, std::chrono::minutes prev_save)
const
817 if (zone_ != x.
zone_)
819 auto dp0 = to_sys_days(y);
821 if (
std::abs((dp0-dp1).count()) > 1)
822 return dp0 < dp1 ? -1 : 1;
825 auto tp0 = to_time_point(y) - prev_save;
829 return tp0 < tp1 ? -1 : tp0 == tp1 ? 0 : 1;
833 auto tp0 = to_time_point(y);
839 return tp0 < tp1 ? -1 : tp0 == tp1 ? 0 : 1;
842 auto tp0 = to_time_point(y);
845 tp1 -= offset + prev_save;
848 return tp0 < tp1 ? -1 : tp0 == tp1 ? 0 : 1;
850 auto const t0 = to_time_point(y);
852 return t0 < t1 ? -1 : t0 == t1 ? 0 : 1;
857 std::chrono::seconds save)
const
859 using namespace date;
860 using namespace std::chrono;
861 auto until_utc = to_time_point(y);
865 until_utc -= offset + save;
879 month_weekday_last_ = x;
886 month_day_weekday_ = x;
893 using namespace std::chrono;
894 using namespace date;
900 return sys_days(y/u.month_weekday_last_);
903 auto const x = y/u.month_day_weekday_.month_day_;
905 auto const wd0 = u.month_day_weekday_.weekday_;
911 auto const x = y/u.month_day_weekday_.month_day_;
912 auto const wd1 = u.month_day_weekday_.weekday_;
921 return to_sys_days(y) + s_ + h_ + m_;
927 using namespace std::chrono;
928 using namespace date;
936 u.month_day_ = ymd.month()/ymd.day();
942 auto const x = y/u.month_day_weekday_.month_day_;
944 auto const wd0 = u.month_day_weekday_.weekday_;
946 u.month_day_ = ymd.month()/ymd.day();
952 auto const x = y/u.month_day_weekday_.month_day_;
953 auto const wd1 = u.month_day_weekday_.weekday_;
956 u.month_day_ = ymd.month()/ymd.day();
966 using namespace date;
967 using namespace std::chrono;
968 assert(((std::ios::failbit | std::ios::badbit) & is.exceptions()) ==
969 (std::ios::failbit | std::ios::badbit));
971 if (!is.eof() && ws(is) && !is.eof() && is.peek() !=
'#')
974 if (!is.eof() && ws(is) && !is.eof() && is.peek() !=
'#')
976 if (is.peek() ==
'l')
978 for (
int i = 0; i < 4; ++i)
984 else if (std::isalpha(is.peek()))
989 if (c ==
'<' || c ==
'>')
994 throw std::runtime_error(std::string(
"bad operator: ") + c + c2);
998 throw std::runtime_error(std::string(
"bad operator: ") + c + c2
999 + std::to_string(d));
1004 throw std::runtime_error(std::string(
"bad operator: ") + c);
1010 if (d < 1 || d > 31)
1011 throw std::runtime_error(std::string(
"day of month: ")
1012 + std::to_string(d));
1016 if (!is.eof() && ws(is) && !is.eof() && is.peek() !=
'#')
1021 if (!is.eof() && is.peek() ==
':')
1026 if (!is.eof() && is.peek() ==
':')
1033 if (!is.eof() && std::isalpha(is.peek()))
1073 if ((
static_cast<unsigned>(x.
day()) - 1) % 7 == 0)
1077 (
static_cast<unsigned>(x.
day()) - 1)/7+1]) <<
" ";
1102 using namespace date;
1103 using namespace std::chrono;
1104 std::istringstream in(s);
1105 in.exceptions(std::ios::failbit | std::ios::badbit);
1107 in >> word >> name_;
1110 if (std::isalpha(in.peek()))
1118 throw std::runtime_error(
"Didn't find expected word: " + word);
1123 starting_year_ =
year{x};
1126 if (std::isalpha(in.peek()))
1131 ending_year_ = starting_year_;
1133 else if (word ==
"max")
1138 throw std::runtime_error(
"Didn't find expected word: " + word);
1143 ending_year_ =
year{x};
1146 assert(word ==
"-");
1152 assert(hours{-1} <= save_ && save_ <= hours{2});
1156 std::cerr << s <<
'\n';
1157 std::cerr << *
this <<
'\n';
1164 , starting_year_(starting_year)
1165 , ending_year_(ending_year)
1166 , starting_at_(r.starting_at_)
1168 , abbrev_(r.abbrev_)
1184 using namespace std::chrono;
1185 auto const xm = x.
month();
1186 auto const ym = y.
month();
1193 return x.
day() < y.
day();
1223 return x.
name() == y;
1229 return x.
name() < y;
1235 return y.
name() == x;
1241 return x < y.
name();
1247 using namespace date;
1248 using namespace std::chrono;
1256 if (r.
save_ >= minutes{0})
1266 return starting_at_.
day();
1272 return starting_at_.
month();
1279 return x.
name() < nm;
1284 return nm < x.
name();
1294 std::cerr << x <<
'\n';
1295 std::cerr << y <<
'\n';
1296 assert(x.starting_year_ <= y.starting_year_);
1306 using namespace date;
1307 using difference_type = std::vector<Rule>::iterator::difference_type;
1312 assert(rules[i].starting_year_ <= rules[k].starting_year_ &&
1313 rules[i].ending_year_ >= rules[k].starting_year_ &&
1314 (rules[i].starting_year_ != rules[k].starting_year_ ||
1315 rules[i].ending_year_ != rules[k].ending_year_));
1316 if (rules[i].starting_year_ == rules[k].starting_year_)
1318 if (rules[k].ending_year_ < rules[i].ending_year_)
1320 rules.insert(rules.begin() +
static_cast<difference_type
>(k+1),
1321 Rule(rules[i], rules[k].ending_year_ +
years{1},
1322 std::move(rules[i].ending_year_)));
1324 rules[i].ending_year_ = rules[k].ending_year_;
1328 rules.insert(rules.begin() +
static_cast<difference_type
>(k+1),
1329 Rule(rules[k], rules[i].ending_year_ +
years{1},
1330 std::move(rules[k].ending_year_)));
1332 rules[k].ending_year_ = rules[i].ending_year_;
1337 if (rules[k].ending_year_ < rules[i].ending_year_)
1339 rules.insert(rules.begin() +
static_cast<difference_type
>(k),
1340 Rule(rules[i], rules[k].starting_year_, rules[k].ending_year_));
1342 rules.insert(rules.begin() +
static_cast<difference_type
>(k+1),
1343 Rule(rules[i], rules[k].ending_year_ +
years{1},
1344 std::move(rules[i].ending_year_)));
1345 rules[i].ending_year_ = rules[k].starting_year_ -
years{1};
1348 else if (rules[k].ending_year_ > rules[i].ending_year_)
1350 rules.insert(rules.begin() +
static_cast<difference_type
>(k),
1351 Rule(rules[i], rules[k].starting_year_, rules[i].ending_year_));
1353 rules.insert(rules.begin() +
static_cast<difference_type
>(k+1),
1354 Rule(rules[k], rules[i].ending_year_ +
years{1},
1355 std::move(rules[k].ending_year_)));
1357 rules[k].ending_year_ = std::move(rules[i].ending_year_);
1358 rules[i].ending_year_ = rules[k].starting_year_ -
years{1};
1362 rules.insert(rules.begin() +
static_cast<difference_type
>(k),
1363 Rule(rules[i], rules[k].starting_year_,
1364 std::move(rules[i].ending_year_)));
1367 rules[i].ending_year_ = rules[k].starting_year_ -
years{1};
1375 using difference_type = std::vector<Rule>::iterator::difference_type;
1377 for (; i + 1 < e; ++i)
1379 for (
auto k = i + 1; k < e; ++k)
1381 if (overlaps(rules[i], rules[k]))
1383 split(rules, i, k, e);
1384 std::sort(rules.begin() +
static_cast<difference_type
>(i),
1385 rules.begin() +
static_cast<difference_type
>(e));
1391 if (rules[j].starting_year() == rules[j].ending_year())
1392 rules[j].starting_at_.canonicalize(rules[j].starting_year());
1399 using difference_type = std::vector<Rule>::iterator::difference_type;
1400 for (std::size_t i = 0; i < rules.size();)
1402 auto e =
static_cast<std::size_t
>(std::upper_bound(
1403 rules.cbegin()+
static_cast<difference_type
>(i), rules.cend(), rules[i].name(),
1404 [](
const std::string& nm,
const Rule& x)
1406 return nm < x.name();
1407 }) - rules.cbegin());
1408 split_overlaps(rules, i, e);
1409 auto first_rule = rules.begin() +
static_cast<difference_type
>(i);
1410 auto last_rule = rules.begin() +
static_cast<difference_type
>(e);
1411 auto t = std::lower_bound(first_rule, last_rule,
min_year);
1412 if (t > first_rule+1)
1414 if (t == last_rule || t->starting_year() >=
min_year)
1416 auto d =
static_cast<std::size_t
>(t - first_rule);
1417 rules.erase(first_rule, t);
1420 first_rule = rules.begin() +
static_cast<difference_type
>(i);
1421 last_rule = rules.begin() +
static_cast<difference_type
>(e);
1422 t = std::upper_bound(first_rule, last_rule,
max_year);
1425 auto d =
static_cast<std::size_t
>(last_rule - t);
1426 rules.erase(t, last_rule);
1431 rules.shrink_to_fit();
1442std::pair<const Rule*, date::year>
1445 using namespace date;
1449 if (r == &rules.front() || r->
name() != r[-1].
name())
1456 if (r == &rules.front() || r->
name() != r[-1].
name() ||
1459 while (r < &rules.back() && r->
name() == r[1].
name() &&
1478std::pair<const Rule*, date::year>
1481 using namespace date;
1484 if (r == last_rule-1)
1509std::pair<const Rule*, date::year>
1512 using namespace date;
1516 if (r == &rules.back() || r->
name() != r[1].
name())
1523 if (r == &rules.back() || r->
name() != r[1].
name() ||
1526 while (r > &rules.front() && r->
name() == r[-1].
name() &&
1541 while (r->save() != std::chrono::minutes{0})
1545 throw std::runtime_error(
"Could not find standard offset in rule "
1546 + eqr.first->name());
1552std::pair<const Rule*, date::year>
1554 const date::year& y,
const std::chrono::seconds& offset,
1557 assert(eqr.first !=
nullptr);
1558 assert(eqr.second !=
nullptr);
1560 using namespace std::chrono;
1561 using namespace date;
1563 auto ry = r->starting_year();
1564 auto prev_save = minutes{0};
1566 const Rule* prev_rule =
nullptr;
1567 while (r !=
nullptr)
1569 if (mdt.
compare(y, r->mdt(), ry, offset, prev_save) <= 0)
1573 prev_save = prev_rule->
save();
1576 return {prev_rule, prev_year};
1580std::pair<const Rule*, date::year>
1586 using namespace std::chrono;
1587 using namespace date;
1589 auto ry = r->starting_year();
1590 auto prev_save = minutes{0};
1592 const Rule* prev_rule =
nullptr;
1593 while (r !=
nullptr)
1596 switch (r->mdt().zone())
1599 found = tp_utc < r->mdt().to_time_point(ry);
1602 found =
sys_seconds{tp_std.time_since_epoch()} < r->mdt().to_time_point(ry);
1605 found =
sys_seconds{tp_loc.time_since_epoch()} < r->mdt().to_time_point(ry);
1612 prev_save = prev_rule->
save();
1615 return {prev_rule, prev_year};
1620find_rule(
const std::pair<const Rule*, date::year>& first_rule,
1621 const std::pair<const Rule*, date::year>& last_rule,
1622 const date::year& y,
const std::chrono::seconds& offset,
1623 const MonthDayTime& mdt,
const std::chrono::minutes& initial_save,
1624 const std::string& initial_abbrev)
1626 using namespace std::chrono;
1627 using namespace date;
1628 auto r = first_rule.first;
1629 auto ry = first_rule.second;
1631 seconds{0}, initial_save, initial_abbrev};
1632 while (r !=
nullptr)
1634 auto tr = r->mdt().to_sys(ry, offset, x.save);
1635 auto tx = mdt.
to_sys(y, offset, x.save);
1637 if (tx <= tr || (r == last_rule.first && ry == last_rule.second))
1639 if (tx < tr && r == first_rule.first && ry == first_rule.second)
1641 x.end = r->mdt().to_sys(ry, offset, x.save);
1647 assert(r !=
nullptr);
1650 auto prev_save = initial_save;
1651 if (!(r == first_rule.first && ry == first_rule.second))
1653 x.begin = r->mdt().to_sys(ry, offset, prev_save);
1655 x.abbrev = r->abbrev();
1656 if (!(r == last_rule.first && ry == last_rule.second))
1659 assert(r !=
nullptr);
1660 x.end = r->mdt().to_sys(ry, offset, x.save);
1668 assert(r !=
nullptr);
1677#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
1678 using minutes = std::chrono::minutes;
1679 using string = std::string;
1680 if (tag_ == has_save)
1689#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
1690 ::new(&u.rule_) std::string();
1695 : gmtoff_(i.gmtoff_)
1697 , format_(i.format_)
1698 , until_year_(i.until_year_)
1699 , until_date_(i.until_date_)
1700 , until_utc_(i.until_utc_)
1701 , until_std_(i.until_std_)
1702 , until_loc_(i.until_loc_)
1703 , initial_save_(i.initial_save_)
1704 , initial_abbrev_(i.initial_abbrev_)
1705 , first_rule_(i.first_rule_)
1706 , last_rule_(i.last_rule_)
1708#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
1729 , adjusted_(new std::once_flag{})
1735 native = __BYTE_ORDER__,
1736 little = __ORDER_LITTLE_ENDIAN__,
1737 big = __ORDER_BIG_ENDIAN__
1743reverse_bytes(std::uint32_t i)
1746 (i & 0xff000000u) >> 24 |
1747 (i & 0x00ff0000u) >> 8 |
1748 (i & 0x0000ff00u) << 8 |
1749 (i & 0x000000ffu) << 24;
1755reverse_bytes(std::uint64_t i)
1758 (i & 0xff00000000000000ull) >> 56 |
1759 (i & 0x00ff000000000000ull) >> 40 |
1760 (i & 0x0000ff0000000000ull) >> 24 |
1761 (i & 0x000000ff00000000ull) >> 8 |
1762 (i & 0x00000000ff000000ull) << 8 |
1763 (i & 0x0000000000ff0000ull) << 24 |
1764 (i & 0x000000000000ff00ull) << 40 |
1765 (i & 0x00000000000000ffull) << 56;
1772maybe_reverse_bytes(T&, std::false_type)
1779maybe_reverse_bytes(std::int32_t& t, std::true_type)
1781 t =
static_cast<std::int32_t
>(reverse_bytes(
static_cast<std::uint32_t
>(t)));
1787maybe_reverse_bytes(std::int64_t& t, std::true_type)
1789 t =
static_cast<std::int64_t
>(reverse_bytes(
static_cast<std::uint64_t
>(t)));
1796maybe_reverse_bytes(T& t)
1798 maybe_reverse_bytes(t, std::integral_constant<
bool,
1799 endian::native == endian::little>{});
1804load_header(std::istream& inf)
1826load_version(std::istream& inf)
1831 return static_cast<unsigned char>(v);
1836skip_reserve(std::istream& inf)
1843load_counts(std::istream& inf,
1844 std::int32_t& tzh_ttisgmtcnt, std::int32_t& tzh_ttisstdcnt,
1845 std::int32_t& tzh_leapcnt, std::int32_t& tzh_timecnt,
1846 std::int32_t& tzh_typecnt, std::int32_t& tzh_charcnt)
1849 inf.read(
reinterpret_cast<char*
>(&tzh_ttisgmtcnt), 4);
1850 maybe_reverse_bytes(tzh_ttisgmtcnt);
1851 inf.read(
reinterpret_cast<char*
>(&tzh_ttisstdcnt), 4);
1852 maybe_reverse_bytes(tzh_ttisstdcnt);
1853 inf.read(
reinterpret_cast<char*
>(&tzh_leapcnt), 4);
1854 maybe_reverse_bytes(tzh_leapcnt);
1855 inf.read(
reinterpret_cast<char*
>(&tzh_timecnt), 4);
1856 maybe_reverse_bytes(tzh_timecnt);
1857 inf.read(
reinterpret_cast<char*
>(&tzh_typecnt), 4);
1858 maybe_reverse_bytes(tzh_typecnt);
1859 inf.read(
reinterpret_cast<char*
>(&tzh_charcnt), 4);
1860 maybe_reverse_bytes(tzh_charcnt);
1863template <
class TimeType>
1865std::vector<detail::transition>
1866load_transitions(std::istream& inf, std::int32_t tzh_timecnt)
1869 using namespace std::chrono;
1870 std::vector<detail::transition> transitions;
1871 transitions.reserve(
static_cast<unsigned>(tzh_timecnt));
1872 for (std::int32_t i = 0; i < tzh_timecnt; ++i)
1875 inf.read(
reinterpret_cast<char*
>(&t),
sizeof(t));
1876 maybe_reverse_bytes(t);
1877 transitions.emplace_back(
sys_seconds{seconds{t}});
1878 if (transitions.back().timepoint < min_seconds)
1879 transitions.back().timepoint = min_seconds;
1885std::vector<std::uint8_t>
1886load_indices(std::istream& inf, std::int32_t tzh_timecnt)
1889 std::vector<std::uint8_t> indices;
1890 indices.reserve(
static_cast<unsigned>(tzh_timecnt));
1891 for (std::int32_t i = 0; i < tzh_timecnt; ++i)
1894 inf.read(
reinterpret_cast<char*
>(&t),
sizeof(t));
1895 indices.emplace_back(t);
1902load_ttinfo(std::istream& inf, std::int32_t tzh_typecnt)
1905 std::vector<ttinfo> ttinfos;
1906 ttinfos.reserve(
static_cast<unsigned>(tzh_typecnt));
1907 for (std::int32_t i = 0; i < tzh_typecnt; ++i)
1910 inf.read(
reinterpret_cast<char*
>(&t), 6);
1911 maybe_reverse_bytes(t.tt_gmtoff);
1912 ttinfos.emplace_back(t);
1919load_abbreviations(std::istream& inf, std::int32_t tzh_charcnt)
1923 abbrev.resize(
static_cast<unsigned>(tzh_charcnt),
'\0');
1924 inf.read(&abbrev[0], tzh_charcnt);
1928#if !MISSING_LEAP_SECONDS
1930template <
class TimeType>
1932std::vector<leap_second>
1933load_leaps(std::istream& inf, std::int32_t tzh_leapcnt)
1936 using namespace std::chrono;
1937 std::vector<leap_second> leap_seconds;
1938 leap_seconds.reserve(
static_cast<std::size_t
>(tzh_leapcnt));
1939 for (std::int32_t i = 0; i < tzh_leapcnt; ++i)
1943 inf.read(
reinterpret_cast<char*
>(&t0),
sizeof(t0));
1944 inf.read(
reinterpret_cast<char*
>(&t1),
sizeof(t1));
1945 maybe_reverse_bytes(t0);
1946 maybe_reverse_bytes(t1);
1947 leap_seconds.emplace_back(
sys_seconds{seconds{t0 - (t1-1)}},
1950 return leap_seconds;
1953template <
class TimeType>
1955std::vector<leap_second>
1956load_leap_data(std::istream& inf,
1957 std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt,
1958 std::int32_t tzh_typecnt, std::int32_t tzh_charcnt)
1960 inf.ignore(tzh_timecnt*
static_cast<std::int32_t
>(
sizeof(TimeType)) + tzh_timecnt +
1961 tzh_typecnt*6 + tzh_charcnt);
1962 return load_leaps<TimeType>(inf, tzh_leapcnt);
1966std::vector<leap_second>
1967load_just_leaps(std::istream& inf)
1970 using namespace std::chrono;
1972 auto v = load_version(inf);
1973 std::int32_t tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
1974 tzh_timecnt, tzh_typecnt, tzh_charcnt;
1976 load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
1977 tzh_timecnt, tzh_typecnt, tzh_charcnt);
1979 return load_leap_data<int32_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt,
1982 inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt +
1983 tzh_ttisstdcnt + tzh_ttisgmtcnt);
1985 auto v2 = load_version(inf);
1989 inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt +
1990 tzh_ttisstdcnt + tzh_ttisgmtcnt + (4+1+15));
1992 load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
1993 tzh_timecnt, tzh_typecnt, tzh_charcnt);
1994 return load_leap_data<int64_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt,
2000template <
class TimeType>
2003 std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt,
2004 std::int32_t tzh_typecnt, std::int32_t tzh_charcnt)
2006 using namespace std::chrono;
2007 transitions_ = load_transitions<TimeType>(inf, tzh_timecnt);
2008 auto indices = load_indices(inf, tzh_timecnt);
2009 auto infos = load_ttinfo(inf, tzh_typecnt);
2010 auto abbrev = load_abbreviations(inf, tzh_charcnt);
2011#if !MISSING_LEAP_SECONDS
2013 if (leap_seconds.empty() && tzh_leapcnt > 0)
2014 leap_seconds = load_leaps<TimeType>(inf, tzh_leapcnt);
2016 ttinfos_.reserve(infos.size());
2017 for (
auto& info : infos)
2019 ttinfos_.push_back({seconds{info.tt_gmtoff},
2020 abbrev.c_str() + info.tt_abbrind,
2021 info.tt_isdst != 0});
2024 if (transitions_.empty() || transitions_.front().timepoint != min_seconds)
2026 transitions_.emplace(transitions_.begin(), min_seconds);
2027 auto tf = std::find_if(ttinfos_.begin(), ttinfos_.end(),
2028 [](
const expanded_ttinfo& ti)
2029 {return ti.is_dst == 0;});
2030 if (tf == ttinfos_.end())
2031 tf = ttinfos_.begin();
2032 transitions_[i].info = &*tf;
2035 for (
auto j = 0u; i < transitions_.size(); ++i, ++j)
2036 transitions_[i].info = ttinfos_.data() + indices[j];
2040time_zone::init_impl()
2042 using namespace std;
2043 using namespace std::chrono;
2045 std::ifstream inf(
name);
2047 throw std::runtime_error{
"Unable to open " +
name};
2048 inf.exceptions(std::ios::failbit | std::ios::badbit);
2050 auto v = load_version(inf);
2051 std::int32_t tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
2052 tzh_timecnt, tzh_typecnt, tzh_charcnt;
2054 load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
2055 tzh_timecnt, tzh_typecnt, tzh_charcnt);
2058 load_data<int32_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt, tzh_charcnt);
2063 inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt +
2064 tzh_ttisstdcnt + tzh_ttisgmtcnt);
2066 auto v2 = load_version(inf);
2070 inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt +
2071 tzh_ttisstdcnt + tzh_ttisgmtcnt + (4+1+15));
2073 load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
2074 tzh_timecnt, tzh_typecnt, tzh_charcnt);
2075 load_data<int64_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt, tzh_charcnt);
2077#if !MISSING_LEAP_SECONDS
2078 if (tzh_leapcnt > 0)
2081 auto itr = leap_seconds.begin();
2082 auto l = itr->date();
2083 seconds leap_count{0};
2084 for (
auto t = std::upper_bound(transitions_.begin(), transitions_.end(), l,
2087 return x < ct.timepoint;
2089 t != transitions_.end(); ++t)
2091 while (t->timepoint >= l)
2094 if (++itr == leap_seconds.end())
2097 l = itr->date() + leap_count;
2099 t->timepoint -= leap_count;
2103 auto b = transitions_.begin();
2104 auto i = transitions_.end();
2107 for (--i; i != b; --i)
2109 if (i->info->offset == i[-1].info->offset &&
2110 i->info->abbrev == i[-1].info->abbrev &&
2111 i->info->is_dst == i[-1].info->is_dst)
2112 i = transitions_.erase(i);
2118time_zone::init()
const
2120 std::call_once(*adjusted_, [
this]() {
const_cast<time_zone*
>(
this)->init_impl();});
2124time_zone::load_sys_info(std::vector<detail::transition>::const_iterator i)
const
2126 using namespace std::chrono;
2127 assert(!transitions_.empty());
2129 if (i != transitions_.begin())
2131 r.
begin = i[-1].timepoint;
2132 r.
end = i != transitions_.end() ? i->timepoint :
2134 r.
offset = i[-1].info->offset;
2135 r.
save = i[-1].info->is_dst ? minutes{1} : minutes{0};
2136 r.
abbrev = i[-1].info->abbrev;
2141 r.
end = i+1 != transitions_.end() ? i[1].timepoint :
2143 r.
offset = i[0].info->offset;
2144 r.
save = i[0].info->is_dst ? minutes{1} : minutes{0};
2145 r.
abbrev = i[0].info->abbrev;
2153 using namespace std;
2155 return load_sys_info(upper_bound(transitions_.begin(), transitions_.end(), tp,
2158 return x < t.timepoint;
2165 using namespace std::chrono;
2169 auto tr = upper_bound(transitions_.begin(), transitions_.end(), tp,
2172 return sys_seconds{x.time_since_epoch()} -
2173 t.info->offset < t.timepoint;
2175 i.first = load_sys_info(tr);
2176 auto tps =
sys_seconds{(tp - i.first.offset).time_since_epoch()};
2177 if (tps < i.first.begin +
days{1} && tr != transitions_.begin())
2179 i.second = load_sys_info(--tr);
2180 tps =
sys_seconds{(tp - i.second.offset).time_since_epoch()};
2181 if (tps < i.second.end && i.first.end != i.second.end)
2184 std::swap(i.first, i.second);
2191 else if (tps >= i.first.end && tr != transitions_.end())
2193 i.second = load_sys_info(++tr);
2194 tps =
sys_seconds{(tp - i.second.offset).time_since_epoch()};
2195 if (tps < i.second.begin)
2206 using namespace std::chrono;
2208 os << z.
name_ <<
'\n';
2209 os <<
"Initially: ";
2210 auto const& t = z.transitions_.front();
2211 if (t.info->offset >= seconds{0})
2214 if (t.info->is_dst > 0)
2218 os << t.info->abbrev <<
'\n';
2219 for (
auto i = std::next(z.transitions_.cbegin()); i < z.transitions_.cend(); ++i)
2232 : adjusted_(new std::once_flag{})
2236 using namespace date;
2237 std::istringstream in(s);
2238 in.exceptions(std::ios::failbit | std::ios::badbit);
2240 in >> word >>
name_;
2245 std::cerr << s <<
'\n';
2246 std::cerr << *
this <<
'\n';
2261 using namespace std::chrono;
2264 auto tps =
sys_seconds{(tp - i.first.offset).time_since_epoch()};
2265 if (tps < i.first.begin)
2267 i.second = std::move(i.first);
2268 i.first =
get_info_impl(i.second.begin - seconds{1},
static_cast<int>(tz::utc));
2271 else if (i.first.end - tps <=
days{1})
2273 i.second =
get_info_impl(i.first.end,
static_cast<int>(tz::utc));
2274 tps =
sys_seconds{(tp - i.second.offset).time_since_epoch()};
2275 if (tps >= i.second.begin)
2288 std::istringstream in(s);
2289 in.exceptions(std::ios::failbit | std::ios::badbit);
2291 if (!in.eof() && in.peek() !=
'#')
2296 std::cerr << s <<
'\n';
2297 std::cerr << *
this <<
'\n';
2306 using namespace date;
2307 using namespace std::chrono;
2317 if (in.eof() || in.peek() ==
'#')
2338 using namespace std::chrono;
2339 using namespace date;
2340 const zonelet* prev_zonelet =
nullptr;
2343 std::pair<const Rule*, const Rule*> eqr{};
2344 std::istringstream in;
2345 in.exceptions(std::ios::failbit | std::ios::badbit);
2347 if (!z.u.rule_.empty())
2350 eqr = std::equal_range(rules.data(), rules.data() + rules.size(), z.u.rule_);
2351 if (eqr.first == eqr.second)
2356 using namespace std::chrono;
2357 using string = std::string;
2360#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
2361 z.u.rule_.~string();
2363 ::new(&z.u.save_) minutes(tmp);
2372 std::cerr <<
name_ <<
" : " << z.u.rule_ <<
'\n';
2383 minutes final_save{0};
2386 final_save = z.u.save_;
2392 if (z.last_rule_.first !=
nullptr)
2393 final_save = z.last_rule_.first->save();
2395 z.until_utc_ = z.until_date_.
to_sys(z.until_year_, z.gmtoff_, final_save);
2396 z.until_std_ =
local_seconds{z.until_utc_.time_since_epoch()} + z.gmtoff_;
2397 z.until_loc_ = z.until_std_ + final_save;
2401 if (prev_zonelet !=
nullptr)
2406 if (z.first_rule_.first !=
nullptr)
2408 z.initial_save_ = z.first_rule_.first->save();
2409 z.initial_abbrev_ = z.first_rule_.first->abbrev();
2410 if (z.first_rule_ != z.last_rule_)
2413 z.first_rule_.first,
2414 z.first_rule_.second);
2418 z.first_rule_ = std::make_pair(
nullptr,
year::min());
2419 z.last_rule_ = std::make_pair(
nullptr,
year::max());
2423 if (z.first_rule_.first ==
nullptr && z.last_rule_.first !=
nullptr)
2425 z.first_rule_ = std::make_pair(eqr.first, eqr.first->starting_year());
2431 if (z.first_rule_.first ==
nullptr)
2433 assert(z.first_rule_.second ==
year::min());
2434 assert(z.last_rule_.first ==
nullptr);
2435 assert(z.last_rule_.second ==
year::max());
2439 assert(z.last_rule_.first !=
nullptr);
2449 std::chrono::minutes save)
2451 using namespace std::chrono;
2452 auto k =
format.find(
"%s");
2453 if (k != std::string::npos)
2455 format.replace(k, 2, variable);
2460 if (k != std::string::npos)
2462 if (save == minutes{0})
2470 if (k != std::string::npos)
2473 if (off < seconds{0})
2480 auto h = date::floor<hours>(off);
2484 temp += std::to_string(h.count());
2485 if (off > seconds{0})
2487 auto m = date::floor<minutes>(off);
2489 if (m < minutes{10})
2491 temp += std::to_string(m.count());
2492 if (off > seconds{0})
2494 if (off < seconds{10})
2496 temp += std::to_string(off.count());
2499 format.replace(k, 2, temp);
2509 using namespace std::chrono;
2510 using namespace date;
2511 tz timezone =
static_cast<tz>(tz_int);
2512 assert(timezone != tz::standard);
2515 throw std::runtime_error(
"The year " + std::to_string(
static_cast<int>(y)) +
2516 " is out of range:[" + std::to_string(
static_cast<int>(
min_year)) +
", "
2517 + std::to_string(
static_cast<int>(
max_year)) +
"]");
2526 return timezone == tz::utc ? t < zl.until_utc_ :
2527 t < sys_seconds{zl.until_loc_.time_since_epoch()};
2531 if (i != zonelets_.end())
2535 if (i != zonelets_.begin())
2536 r.
begin = i[-1].until_utc_;
2539 r.
end = i->until_utc_;
2540 r.
offset = i->gmtoff_ + i->u.save_;
2541 r.
save = i->u.save_;
2543 else if (i->u.rule_.empty())
2545 if (i != zonelets_.begin())
2546 r.
begin = i[-1].until_utc_;
2549 r.
end = i->until_utc_;
2554 r =
find_rule(i->first_rule_, i->last_rule_, y, i->gmtoff_,
2556 i->initial_save_, i->initial_abbrev_);
2558 if (i != zonelets_.begin() && r.
begin < i[-1].until_utc_)
2559 r.
begin = i[-1].until_utc_;
2560 if (r.
end > i->until_utc_)
2561 r.
end = i->until_utc_;
2572 using namespace date;
2573 using namespace std::chrono;
2580 const_cast<time_zone&>(z).adjust_infos(get_tzdb().rules);
2588 if (s.gmtoff_ >= seconds{0})
2596 std::ostringstream tmp;
2601 os << s.format_ <<
" ";
2602 os << s.until_year_ <<
' ' << s.until_date_;
2603 os <<
" " << s.until_utc_ <<
" UTC";
2604 os <<
" " << s.until_std_ <<
" STD";
2605 os <<
" " << s.until_loc_;
2606 os <<
" " <<
make_time(s.initial_save_);
2607 os <<
" " << s.initial_abbrev_;
2608 if (s.first_rule_.first !=
nullptr)
2609 os <<
" {" << *s.first_rule_.first <<
", " << s.first_rule_.second <<
'}';
2611 os <<
" {" <<
"nullptr" <<
", " << s.first_rule_.second <<
'}';
2612 if (s.last_rule_.first !=
nullptr)
2613 os <<
" {" << *s.last_rule_.first <<
", " << s.last_rule_.second <<
'}';
2615 os <<
" {" <<
"nullptr" <<
", " << s.last_rule_.second <<
'}';
2618 indent = std::string(35,
' ');
2628 using namespace date;
2629 return os << x.
date_ <<
" +";
2638 using namespace std;
2639 auto path =
get_tz_dir() + string(
"/+VERSION");
2658std::vector<leap_second>
2659find_read_and_leap_seconds()
2662 std::ios_base::binary);
2665 std::vector<leap_second> leap_seconds;
2669 std::getline(in, line);
2670 if (!line.empty() && line[0] !=
'#')
2672 std::istringstream iss(line);
2673 iss.exceptions(std::ios::failbit | std::ios::badbit);
2687 std::cerr << line <<
'\n';
2691 return leap_seconds;
2695 std::ios_base::binary);
2698 std::vector<leap_second> leap_seconds;
2703 std::getline(in, line);
2704 if (!line.empty() && line[0] !=
'#')
2706 std::istringstream iss(line);
2707 iss.exceptions(std::ios::failbit | std::ios::badbit);
2708 using seconds = std::chrono::seconds;
2711 if (s == 2272060800)
2717 return leap_seconds;
2721 std::ios_base::binary);
2724 return load_just_leaps(in);
2728 std::ios_base::binary);
2731 return load_just_leaps(in);
2737std::unique_ptr<tzdb>
2740 std::unique_ptr<tzdb> db(
new tzdb);
2743 std::queue<std::string> subfolders;
2747 while (!subfolders.empty())
2749 auto dirname = std::move(subfolders.front());
2751 auto dir = opendir(dirname.c_str());
2754 while ((d = readdir(dir)) !=
nullptr)
2757 if (d->d_name[0] ==
'.' ||
2758 memcmp(d->d_name,
"posix", 5) == 0 ||
2759 strcmp(d->d_name,
"Factory") == 0 ||
2760 strcmp(d->d_name,
"iso3166.tab") == 0 ||
2761 strcmp(d->d_name,
"right") == 0 ||
2762 strcmp(d->d_name,
"+VERSION") == 0 ||
2763 strcmp(d->d_name,
"version") == 0 ||
2764 strcmp(d->d_name,
"zone.tab") == 0 ||
2765 strcmp(d->d_name,
"zone1970.tab") == 0 ||
2766 strcmp(d->d_name,
"tzdata.zi") == 0 ||
2767 strcmp(d->d_name,
"leapseconds") == 0 ||
2768 strcmp(d->d_name,
"leap-seconds.list") == 0 )
2771 if(stat(subname.c_str(), &s) == 0)
2773 if(S_ISDIR(s.st_mode))
2775 if(!S_ISLNK(s.st_mode))
2777 subfolders.push(subname);
2782 db->zones.emplace_back(subname.substr(
get_tz_dir().size()+1),
2789 db->zones.shrink_to_fit();
2790 std::sort(db->zones.begin(), db->zones.end());
2791 db->leap_seconds = find_read_and_leap_seconds();
2802 using namespace date;
2803 std::istringstream in(s);
2804 in.exceptions(std::ios::failbit | std::ios::badbit);
2812 using namespace date;
2824 using namespace date;
2825 std::istringstream in(s);
2826 in.exceptions(std::ios::failbit | std::ios::badbit);
2830 in >> word >> y >>
date;
2839 return ::_access(filename.c_str(), 0) == 0;
2841 return ::access(filename.c_str(), F_OK) == 0;
2852struct curl_global_init_and_cleanup
2854 ~curl_global_init_and_cleanup()
2856 ::curl_global_cleanup();
2858 curl_global_init_and_cleanup()
2860 if (::curl_global_init(CURL_GLOBAL_DEFAULT) != 0)
2861 throw std::runtime_error(
"CURL global initialization failed");
2863 curl_global_init_and_cleanup(curl_global_init_and_cleanup
const&) =
delete;
2864 curl_global_init_and_cleanup& operator=(curl_global_init_and_cleanup
const&) =
delete;
2869 void operator()(CURL* p)
const
2871 ::curl_easy_cleanup(p);
2878std::unique_ptr<CURL, curl_deleter>
2881 static const curl_global_init_and_cleanup _{};
2882 return std::unique_ptr<CURL, curl_deleter>{::curl_easy_init()};
2887download_to_string(
const std::string&
url, std::string& str)
2890 auto curl = curl_init();
2894 curl_easy_setopt(curl.get(), CURLOPT_USERAGENT,
"curl");
2895 curl_easy_setopt(curl.get(), CURLOPT_URL,
url.c_str());
2896 curl_write_callback write_cb = [](
char* contents, std::size_t size, std::size_t nmemb,
2897 void* userp) -> std::size_t
2899 auto& userstr = *
static_cast<std::string*
>(userp);
2900 auto realsize = size * nmemb;
2901 userstr.append(contents, realsize);
2904 curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_cb);
2905 curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &str);
2906 curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER,
false);
2907 auto res = curl_easy_perform(curl.get());
2908 return (res == CURLE_OK);
2913 enum class download_file_options { binary, text };
2918download_to_file(
const std::string&
url,
const std::string& local_filename,
2919 download_file_options opts,
char* error_buffer)
2921 auto curl = curl_init();
2924 curl_easy_setopt(curl.get(), CURLOPT_URL,
url.c_str());
2925 curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER,
false);
2927 curl_easy_setopt(curl.get(), CURLOPT_ERRORBUFFER, error_buffer);
2928 curl_write_callback write_cb = [](
char* contents, std::size_t size, std::size_t nmemb,
2929 void* userp) -> std::size_t
2931 auto& of = *
static_cast<std::ofstream*
>(userp);
2932 auto realsize = size * nmemb;
2933 of.write(contents,
static_cast<std::streamsize
>(realsize));
2936 curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_cb);
2937 decltype(curl_easy_perform(curl.get())) res;
2939 std::ofstream of(local_filename,
2940 opts == download_file_options::binary ?
2941 std::ofstream::out | std::ofstream::binary :
2942 std::ofstream::out);
2943 of.exceptions(std::ios::badbit);
2944 curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &of);
2945 res = curl_easy_perform(curl.get());
2947 return res == CURLE_OK;
2955 if (download_to_string(
"https://www.iana.org/time-zones", str))
2957 CONSTDATA char db[] =
"/time-zones/releases/tzdata";
2958 CONSTDATA auto db_size =
sizeof(db) - 1;
2959 auto p = str.find(db, 0, db_size);
2960 const int ver_str_len = 5;
2961 if (p != std::string::npos && p + (db_size + ver_str_len) <= str.size())
2962 version = str.substr(p + db_size, ver_str_len);
2977remove_folder_and_subfolders(
const std::string& folder)
2982 std::string cmd =
"rd /s /q \"";
2985 return std::system(cmd.c_str()) == EXIT_SUCCESS;
2989 std::vector<char> from;
2990 from.assign(folder.begin(), folder.end());
2991 from.push_back(
'\0');
2992 from.push_back(
'\0');
2993 SHFILEOPSTRUCT fo{};
2994 fo.wFunc = FO_DELETE;
2995 fo.pFrom = from.data();
2996 fo.fFlags = FOF_NO_UI;
2997 int ret = SHFileOperation(&fo);
2998 if (ret == 0 && !fo.fAnyOperationsAborted)
3004 return std::system((
"rm -R " + folder).c_str()) == EXIT_SUCCESS;
3006 struct dir_deleter {
3008 void operator()(DIR* d)
const
3012 int result = closedir(d);
3013 assert(result == 0);
3017 using closedir_ptr = std::unique_ptr<DIR, dir_deleter>;
3019 std::string filename;
3020 struct stat statbuf;
3021 std::size_t folder_len = folder.length();
3022 struct dirent* p =
nullptr;
3024 closedir_ptr d(opendir(folder.c_str()));
3025 bool r = d.get() !=
nullptr;
3026 while (r && (p=readdir(d.get())) !=
nullptr)
3028 if (strcmp(p->d_name,
".") == 0 || strcmp(p->d_name,
"..") == 0)
3032 std::size_t buf_len = folder_len + strlen(p->d_name) + 2;
3033 filename.resize(buf_len);
3034 std::size_t path_len =
static_cast<std::size_t
>(
3035 snprintf(&filename[0], buf_len,
"%s/%s", folder.c_str(), p->d_name));
3036 assert(path_len == buf_len - 1);
3037 filename.resize(path_len);
3039 if (stat(filename.c_str(), &statbuf) == 0)
3040 r = S_ISDIR(statbuf.st_mode)
3041 ? remove_folder_and_subfolders(filename)
3042 : unlink(filename.c_str()) == 0;
3047 r = rmdir(folder.c_str()) == 0;
3056make_directory(
const std::string& folder)
3061 std::string cmd =
"mkdir \"";
3064 return std::system(cmd.c_str()) == EXIT_SUCCESS;
3066 return _mkdir(folder.c_str()) == 0;
3070 return std::system((
"mkdir -p " + folder).c_str()) == EXIT_SUCCESS;
3072 return mkdir(folder.c_str(), 0777) == 0;
3079delete_file(
const std::string& file)
3083 std::string cmd =
"del \"";
3086 return std::system(cmd.c_str()) == 0;
3088 return _unlink(file.c_str()) == 0;
3092 return std::system((
"rm " + file).c_str()) == EXIT_SUCCESS;
3094 return unlink(file.c_str()) == 0;
3103move_file(
const std::string& from,
const std::string& to)
3106 std::string cmd =
"move \"";
3111 return std::system(cmd.c_str()) == EXIT_SUCCESS;
3113 return !!::MoveFile(from.c_str(), to.c_str());
3122 return get_known_folder(FOLDERID_ProgramFiles);
3134 HKEY hKey =
nullptr;
3135 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
"SOFTWARE\\7-Zip", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
3137 char value_buffer[MAX_PATH + 1];
3139 DWORD size =
sizeof(value_buffer) -
sizeof(value_buffer[0]);
3140 DWORD tzi_type = REG_SZ;
3142 bool got_value = (RegQueryValueExA(hKey,
"Path",
nullptr, &tzi_type,
3143 reinterpret_cast<LPBYTE
>(value_buffer), &size) == ERROR_SUCCESS);
3148 value_buffer[size /
sizeof(value_buffer[0])] =
'\0';
3149 path = value_buffer;
3157 path += get_program_folder();
3159 path +=
"7-Zip\\7z.exe";
3166run_program(
const std::string& command)
3170 PROCESS_INFORMATION pi{};
3173 std::string mutable_command(command);
3174 if (CreateProcess(
nullptr, &mutable_command[0],
3175 nullptr,
nullptr,
FALSE, CREATE_NO_WINDOW,
nullptr,
nullptr, &si, &pi))
3177 WaitForSingleObject(pi.hProcess, INFINITE);
3179 bool got_exit_code = !!GetExitCodeProcess(pi.hProcess, &exit_code);
3180 CloseHandle(pi.hProcess);
3181 CloseHandle(pi.hThread);
3186 if (got_exit_code && exit_code != STILL_ACTIVE)
3187 return static_cast<int>(exit_code);
3189 return EXIT_FAILURE;
3195get_download_tar_file(
const std::string&
version)
3207extract_gz_file(
const std::string&
version,
const std::string& gz_file,
3208 const std::string& dest_folder)
3210 auto unzip_prog = get_unzip_program();
3211 bool unzip_result =
false;
3231 cmd =
"\"" + cmd +
"\"";
3232 if (std::system(cmd.c_str()) == EXIT_SUCCESS)
3233 unzip_result =
true;
3235 if (run_program(cmd) == EXIT_SUCCESS)
3236 unzip_result =
true;
3239 delete_file(gz_file);
3243 auto tar_file = get_download_tar_file(
version);
3252 cmd =
"\"" + cmd +
"\"";
3253 if (std::system(cmd.c_str()) == EXIT_SUCCESS)
3254 unzip_result =
true;
3256 if (run_program(cmd) == EXIT_SUCCESS)
3257 unzip_result =
true;
3261 delete_file(tar_file);
3263 return unzip_result;
3268get_download_mapping_file(
const std::string&
version)
3279run_program(
const char* prog,
const char*
const args[])
3283 return EXIT_FAILURE;
3290 while ((ret = waitpid(pid, &status, 0)) == -1)
3297 if (WIFEXITED(status))
3298 return WEXITSTATUS(status);
3300 printf(
"Child issues!\n");
3302 return EXIT_FAILURE;
3307 if (execv(prog,
const_cast<char**
>(args)) == -1)
3309 perror(
"unreachable 0\n");
3312 printf(
"unreachable 2\n");
3314 printf(
"unreachable 2\n");
3318 return EXIT_FAILURE;
3324extract_gz_file(
const std::string&,
const std::string& gz_file,
const std::string&)
3327 bool unzipped = std::system((
"tar -xzf " + gz_file +
" -C " +
get_install()).c_str()) == EXIT_SUCCESS;
3329 const char prog[] = {
"/usr/bin/tar"};
3330 const char*
const args[] =
3332 prog,
"-xzf", gz_file.c_str(),
"-C",
get_install().c_str(),
nullptr
3334 bool unzipped = (run_program(prog, args) == EXIT_SUCCESS);
3338 delete_file(gz_file);
3358 if (!make_directory(download_folder))
3363 auto url =
"https://data.iana.org/time-zones/releases/tzdata" +
version +
3365 bool result = download_to_file(
url, get_download_gz_file(
version),
3366 download_file_options::binary, error_buffer);
3370 auto mapping_file = get_download_mapping_file(
version);
3371 result = download_to_file(
3372 "https://raw.githubusercontent.com/unicode-org/cldr/master/"
3373 "common/supplemental/windowsZones.xml",
3374 mapping_file, download_file_options::text, error_buffer);
3383 auto success =
false;
3387 auto gz_file = get_download_gz_file(
version);
3391 remove_folder_and_subfolders(install);
3392 if (make_directory(install))
3394 if (extract_gz_file(
version, gz_file, install))
3397 auto mapping_file_source = get_download_mapping_file(
version);
3400 mapping_file_dest +=
"windowsZones.xml";
3401 if (!move_file(mapping_file_source, mapping_file_dest))
3416 std::ifstream infile(path +
"version");
3417 if (infile.is_open())
3425 infile.open(path +
"NEWS");
3436 throw std::runtime_error(
"Unable to get Timezone database version from " + path);
3440std::unique_ptr<tzdb>
3443 using namespace date;
3447 bool continue_zone =
false;
3448 std::unique_ptr<tzdb> db(
new tzdb);
3458 std::string msg =
"Timezone database version \"";
3460 msg +=
"\" did not install correctly to \"";
3463 throw std::runtime_error(msg);
3468 std::string msg =
"Timezone database not found at \"";
3471 throw std::runtime_error(msg);
3479 if (!rv.empty() && db->version != rv)
3491 std::string msg =
"Timezone database not found at \"";
3494 throw std::runtime_error(msg);
3501 "africa",
"antarctica",
"asia",
"australasia",
"backward",
"etcetera",
"europe",
3502 "pacificnew",
"northamerica",
"southamerica",
"systemv",
"leapseconds"
3505 for (
const auto& filename : files)
3507 std::ifstream infile(path + filename);
3510 std::getline(infile, line);
3511 if (!line.empty() && line[0] !=
'#')
3513 std::istringstream in(line);
3518 db->rules.push_back(
Rule(line));
3519 continue_zone =
false;
3521 else if (word ==
"Link")
3524 continue_zone =
false;
3526 else if (word ==
"Leap")
3529 continue_zone =
false;
3531 else if (word ==
"Zone")
3534 continue_zone =
true;
3536 else if (line[0] ==
'\t' && continue_zone)
3538 db->zones.back().add(line);
3542 std::cerr << line <<
'\n';
3547 std::sort(db->rules.begin(), db->rules.end());
3549 std::sort(db->zones.begin(), db->zones.end());
3550 db->zones.shrink_to_fit();
3551 std::sort(db->links.begin(), db->links.end());
3552 db->links.shrink_to_fit();
3553 std::sort(db->leap_seconds.begin(), db->leap_seconds.end());
3554 db->leap_seconds.shrink_to_fit();
3558 db->mappings = load_timezone_mappings_from_xml_file(mapping_file);
3559 sort_zone_mappings(db->mappings);
3592 auto zi = std::lower_bound(zones.begin(), zones.end(), tz_name,
3594 [](
const time_zone& z,
const std::string_view& nm)
3596 [](
const time_zone& z,
const std::string& nm)
3599 return z.name() < nm;
3601 if (zi == zones.end() || zi->name() != tz_name)
3604 auto li = std::lower_bound(links.begin(), links.end(), tz_name,
3611 return z.name() < nm;
3613 if (li != links.end() && li->name() == tz_name)
3615 zi = std::lower_bound(zones.begin(), zones.end(), li->target(),
3616 [](
const time_zone& z,
const std::string& nm)
3618 return z.name() < nm;
3620 if (zi != zones.end() && zi->name() == li->target())
3624 throw std::runtime_error(std::string(tz_name) +
" not found in timezone database");
3644 os <<
"Version: " << db.
version <<
"\n\n";
3645 for (
const auto& x : db.
zones)
3658 os <<
"Version: " << db.
version <<
'\n';
3659 std::string title(
"--------------------------------------------"
3660 "--------------------------------------------\n"
3661 "Name ""Start Y ""End Y "
3662 "Beginning ""Offset "
3664 "--------------------------------------------"
3665 "--------------------------------------------\n");
3667 for (
const auto& x : db.
rules)
3669 if (count++ % 50 == 0)
3674 title = std::string(
"---------------------------------------------------------"
3675 "--------------------------------------------------------\n"
3677 "Rule ""Abrev ""Until\n"
3678 "---------------------------------------------------------"
3679 "--------------------------------------------------------\n");
3681 for (
const auto& x : db.
zones)
3683 if (count++ % 10 == 0)
3688 title = std::string(
"---------------------------------------------------------"
3689 "--------------------------------------------------------\n"
3691 "---------------------------------------------------------"
3692 "--------------------------------------------------------\n");
3694 for (
const auto& x : db.
links)
3696 if (count++ % 45 == 0)
3701 title = std::string(
"---------------------------------------------------------"
3702 "--------------------------------------------------------\n"
3704 "---------------------------------------------------------"
3705 "--------------------------------------------------------\n");
3722 DYNAMIC_TIME_ZONE_INFORMATION dtzi{};
3723 auto result = GetDynamicTimeZoneInformation(&dtzi);
3724 if (result == TIME_ZONE_ID_INVALID)
3725 throw std::runtime_error(
"current_zone(): GetDynamicTimeZoneInformation()"
3726 " reported TIME_ZONE_ID_INVALID.");
3727 auto wlen = wcslen(dtzi.TimeZoneKeyName);
3729 assert(
sizeof(buf) >= wlen+1);
3730 wcstombs(buf, dtzi.TimeZoneKeyName, wlen);
3731 if (strcmp(buf,
"Coordinated Universal Time") == 0)
3739 std::string win_tzid = getTimeZoneKeyName();
3740 std::string standard_tzid;
3741 if (!native_to_standard_timezone_name(win_tzid, standard_tzid))
3744 msg =
"current_zone() failed: A mapping from the Windows Time Zone id \"";
3746 msg +=
"\" was not found in the time zone mapping database.";
3747 throw std::runtime_error(msg);
3760 using namespace std;
3761 string_view result = rp;
3762 CONSTDATA string_view zoneinfo =
"zoneinfo";
3763 size_t pos = result.rfind(zoneinfo);
3764 if (pos == result.npos)
3765 throw runtime_error(
3766 "current_zone() failed to find \"zoneinfo\" in " +
string(result));
3767 pos = result.find(
'/', pos);
3768 result.remove_prefix(pos + 1);
3778 using namespace std;
3781 size_t pos = result.rfind(zoneinfo);
3782 if (pos == result.npos)
3783 throw runtime_error(
3784 "current_zone() failed to find \"zoneinfo\" in " + result);
3785 pos = result.find(
'/', pos);
3786 result.erase(0, pos + 1);
3796 using namespace std;
3797 char rp[PATH_MAX+1] = {};
3798 if (realpath(timezone, rp) ==
nullptr)
3799 throw system_error(errno, system_category(),
"realpath() failed");
3801 return result !=
"posixrules";
3822 CONSTDATA auto timezone =
"/etc/localtime";
3823 if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0)
3825 using namespace std;
3827 char rp[PATH_MAX+1] = {};
3830 if (realpath(timezone, rp) ==
nullptr)
3831 throw system_error(errno, system_category(),
"realpath() failed");
3835 if (readlink(timezone, rp,
sizeof(rp)-1) <= 0)
3836 throw system_error(errno, system_category(),
"readlink() failed");
3854 if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) {
3855 using namespace std;
3857 char rp[PATH_MAX+1] = {};
3858 if (readlink(timezone, rp,
sizeof(rp)-1) > 0)
3859 result = string(rp);
3861 throw system_error(errno, system_category(),
"readlink() failed");
3863 const size_t pos = result.find(
get_tz_dir());
3864 if (pos != result.npos)
3865 result.erase(0,
get_tz_dir().size() + 1 + pos);
3873 std::ifstream timezone_file(
"/etc/timezone");
3874 if (timezone_file.is_open())
3877 std::getline(timezone_file, result);
3878 if (!result.empty())
3887 std::ifstream timezone_file(
"/var/db/zoneinfo");
3888 if (timezone_file.is_open())
3891 std::getline(timezone_file, result);
3892 if (!result.empty())
3903 std::string result = date::iOSUtils::get_current_timezone();
3904 if (!result.empty())
3914 std::ifstream timezone_file(
"/etc/sysconfig/clock");
3916 while (timezone_file)
3918 std::getline(timezone_file, result);
3919 auto p = result.find(
"ZONE=\"");
3920 if (p != std::string::npos)
3922 result.erase(p, p+6);
3923 result.erase(result.rfind(
'"'));
3929 throw std::runtime_error(
"Could not get current timezone");
3942#if defined(__GNUC__) && __GNUC__ < 5
3943# pragma GCC diagnostic pop
void canonicalize(date::year y)
union date::detail::MonthDayTime::U u
int compare(date::year y, const MonthDayTime &x, date::year yx, std::chrono::seconds offset, std::chrono::minutes prev_save) const
sys_seconds to_sys(date::year y, std::chrono::seconds offset, std::chrono::seconds save) const
sys_days to_sys_days(date::year y) const
date::month month() const
sys_seconds to_time_point(date::year y) const
static void split_overlaps(std::vector< Rule > &rules)
MonthDayTime starting_at_
const std::string & abbrev() const
const std::chrono::minutes & save() const
const std::string & name() const
const date::year & starting_year() const
date::year starting_year_
const date::year & ending_year() const
static bool overlaps(const Rule &x, const Rule &y)
date::month month() const
std::chrono::minutes save_
static void split(std::vector< Rule > &rules, std::size_t i, std::size_t k, std::size_t &e)
DATE_API leap_second(const std::string &s, detail::undocumented)
CONSTCD11 date::day day() const NOEXCEPT
CONSTCD11 date::month month() const NOEXCEPT
DATE_API time_zone_link(const std::string &s)
DATE_API void parse_info(std::istream &in)
std::unique_ptr< std::once_flag > adjusted_
DATE_API void add(const std::string &s)
sys_time< typename std::common_type< Duration, std::chrono::seconds >::type > to_sys(local_time< Duration > tp) const
DATE_API sys_info get_info_impl(sys_seconds tp) const
DATE_API void adjust_infos(const std::vector< detail::Rule > &rules)
std::vector< detail::zonelet > zonelets_
time_zone(time_zone &&)=default
const_iterator erase_after(const_iterator p) NOEXCEPT
void push_front(tzdb *tzdb) NOEXCEPT
std::atomic< tzdb * > head_
const tzdb & front() const NOEXCEPT
CONSTCD11 date::year year() const NOEXCEPT
static CONSTCD11 year max() NOEXCEPT
static CONSTCD11 year min() NOEXCEPT
void load_data(MemoryManager &_data, Settings &_option, Parser &_parser, string sFileName)
This function is a wrapper for the Datafile object. It will simply do the whole UI stuff and let the ...
bool operator==(const Rule &x, const Rule &y)
std::ostream & operator<<(std::ostream &os, const MonthDayTime &x)
std::istream & operator>>(std::istream &is, MonthDayTime &x)
bool operator<(const Rule &x, const Rule &y)
CONSTDATA date::last_spec last
CONSTDATA date::month dec
static tzdb_list create_tzdb()
static const std::string & get_tz_dir()
DATE_API bool remote_download(const std::string &version, char *error_buffer=nullptr)
tzdb_list & get_tzdb_list()
local_time< std::chrono::seconds > local_seconds
std::chrono::duration< int, detail::ratio_multiply< std::ratio< 146097, 400 >, days::period > > years
static std::string format_abbrev(std::string format, const std::string &variable, std::chrono::seconds off, std::chrono::minutes save)
const tzdb & reload_tzdb()
static unsigned parse_month(std::istream &in)
static bool file_exists(const std::string &filename)
std::basic_ostream< CharT, Traits > & operator<<(std::basic_ostream< CharT, Traits > &os, const day &d)
DATE_API bool remote_install(const std::string &version)
auto format(const std::locale &loc, const CharT *fmt, const Streamable &tp) -> decltype(to_stream(std::declval< std::basic_ostream< CharT > & >(), fmt, tp), std::basic_string< CharT >{})
static std::string parse3(std::istream &in)
static const std::string & get_install()
CONSTDATA date::month January
static std::pair< const Rule *, date::year > find_rule_for_zone(const std::pair< const Rule *, const Rule * > &eqr, const date::year &y, const std::chrono::seconds &offset, const MonthDayTime &mdt)
static std::string & access_install()
static std::string discover_tz_dir()
static std::string get_version(const std::string &path)
static unsigned parse_dow(std::istream &in)
static std::pair< const Rule *, date::year > find_previous_rule(const Rule *r, date::year y)
static std::chrono::seconds parse_unsigned_time(std::istream &in)
static std::pair< const Rule *, date::year > find_next_rule(const Rule *first_rule, const Rule *last_rule, const Rule *r, date::year y)
CONSTCD11 hh_mm_ss< std::chrono::duration< Rep, Period > > make_time(const std::chrono::duration< Rep, Period > &d)
static bool sniff_realpath(const char *timezone)
CONSTDATA date::month December
CONSTCD11 std::chrono::duration< Rep, Period > abs(std::chrono::duration< Rep, Period > d)
const time_zone * locate_zone(const std::string &tz_name)
static sys_info find_rule(const std::pair< const Rule *, date::year > &first_rule, const std::pair< const Rule *, date::year > &last_rule, const date::year &y, const std::chrono::seconds &offset, const MonthDayTime &mdt, const std::chrono::minutes &initial_save, const std::string &initial_abbrev)
DATE_API std::string remote_version()
std::chrono::duration< int, detail::ratio_multiply< std::ratio< 24 >, std::chrono::hours::period > > days
void set_install(const std::string &s)
sys_time< std::chrono::seconds > sys_seconds
static const Rule * find_first_std_rule(const std::pair< const Rule *, const Rule * > &eqr)
sys_time< days > sys_days
static std::unique_ptr< tzdb > init_tzdb()
const time_zone * current_zone()
static std::chrono::seconds parse_signed_time(std::istream &in)
static std::string extract_tz_name(char const *rp)
date::month_day month_day_
union date::detail::zonelet::U u
std::chrono::seconds gmtoff_
bool operator()(const std::string &nm, const Rule &x) const
bool operator()(const Rule &x, const std::string &nm) const
enum date::local_info::@25 result
std::chrono::minutes save
std::chrono::seconds offset
static void push_front(tzdb_list &db_list, tzdb *tzdb) NOEXCEPT
std::vector< time_zone > zones
std::vector< time_zone_link > links
std::vector< detail::Rule > rules
std::vector< leap_second > leap_seconds
const time_zone * locate_zone(const std::string &tz_name) const
const time_zone * current_zone() const
static std::string expand_path(std::string path)
static CONSTDATA char folder_delimiter
USE_SHELL_API.
static std::string get_download_folder()
date::month_weekday_last month_weekday_last_
date::month_day month_day_
U & operator=(const date::month_day &x)
std::chrono::minutes save_