forked from HowardHinnant/date
Add remote API and auto-download of tzdata.
This commit is contained in:
@@ -6,6 +6,6 @@ This is actually three separate C++11/C++14 libraries:
|
|||||||
|
|
||||||
Here are the Cppcon 2015 slides on date.h: http://schd.ws/hosted_files/cppcon2015/43/hinnant_dates.pdf
|
Here are the Cppcon 2015 slides on date.h: http://schd.ws/hosted_files/cppcon2015/43/hinnant_dates.pdf
|
||||||
|
|
||||||
2. `"tz.h"` / `"tz.cpp"` are a timezone library built on top of the `"date.h"` library. This timezone library is a complete parser of the IANA timezone database. It provides for an easy way to access all of the data in this database, using the types from `"date.h"` and `<chrono>`. The IANA database also includes data on leap seconds, and this library provides utilities to compute with that information as well. See http://howardhinnant.github.io/tz.html for more details. See the `auto_download` branch for an experimental version of this library which auto-installs the latest version of the IANA timezone database (if you are connected to the internet).
|
2. `"tz.h"` / `"tz.cpp"` are a timezone library built on top of the `"date.h"` library. This timezone library is a complete parser of the IANA timezone database. It provides for an easy way to access all of the data in this database, using the types from `"date.h"` and `<chrono>`. The IANA database also includes data on leap seconds, and this library provides utilities to compute with that information as well. See http://howardhinnant.github.io/tz.html for more details.
|
||||||
|
|
||||||
3. `"iso_week.h"` is a header-only library built on top of the `"date.h"` library which implements the ISO week date calendar. See http://howardhinnant.github.io/iso_week.html for more details.
|
3. `"iso_week.h"` is a header-only library built on top of the `"date.h"` library which implements the ISO week date calendar. See http://howardhinnant.github.io/iso_week.html for more details.
|
||||||
|
224
tz.cpp
224
tz.cpp
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) 2015 Howard Hinnant
|
// Copyright (c) 2015, 2016 Howard Hinnant
|
||||||
// Copyright (c) 2015 Ville Voutilainen
|
// Copyright (c) 2015 Ville Voutilainen
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
@@ -35,6 +35,9 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#if HAS_REMOTE_API
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#endif
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
@@ -73,6 +76,7 @@
|
|||||||
#include <io.h>
|
#include <io.h>
|
||||||
#else
|
#else
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <wordexp.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace date
|
namespace date
|
||||||
@@ -82,10 +86,24 @@ namespace date
|
|||||||
// +---------------------+
|
// +---------------------+
|
||||||
|
|
||||||
#if _WIN32 // TODO: sensible default for all platforms.
|
#if _WIN32 // TODO: sensible default for all platforms.
|
||||||
static std::string install{ "c:\\tzdata" };
|
static const std::string install{ "c:\\tzdata" };
|
||||||
#else
|
#else // !_WIN32
|
||||||
static std::string install{ "/Users/howardhinnant/Downloads/tzdata" };
|
|
||||||
#endif
|
static
|
||||||
|
std::string
|
||||||
|
expand_path(std::string path)
|
||||||
|
{
|
||||||
|
::wordexp_t w{};
|
||||||
|
::wordexp(path.c_str(), &w, 0);
|
||||||
|
assert(w.we_wordc == 1);
|
||||||
|
path = w.we_wordv[0];
|
||||||
|
::wordfree(&w);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::string install = expand_path("~/Downloads/tzdata");
|
||||||
|
|
||||||
|
#endif // !_WIN32
|
||||||
|
|
||||||
static const std::vector<std::string> files =
|
static const std::vector<std::string> files =
|
||||||
{
|
{
|
||||||
@@ -117,7 +135,7 @@ static_assert(boring_day.ok(), "Configuration error");
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Until filesystem arrives.
|
// Until filesystem arrives.
|
||||||
static const char folder_delimiter =
|
static CONSTDATA char folder_delimiter =
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
'\\';
|
'\\';
|
||||||
#else
|
#else
|
||||||
@@ -163,7 +181,7 @@ static std::string get_win32_message(DWORD error_code)
|
|||||||
assert(message_buffer.get() != nullptr);
|
assert(message_buffer.get() != nullptr);
|
||||||
return std::string(message_buffer.get());
|
return std::string(message_buffer.get());
|
||||||
}
|
}
|
||||||
#endif
|
#endif // _WIN32
|
||||||
|
|
||||||
#if TIMEZONE_MAPPING
|
#if TIMEZONE_MAPPING
|
||||||
|
|
||||||
@@ -447,7 +465,7 @@ native_to_standard_timezone_name(const std::string& native_tz_name,
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif // TIMEZONE_MAPPING
|
||||||
|
|
||||||
// Parsing helpers
|
// Parsing helpers
|
||||||
|
|
||||||
@@ -1893,6 +1911,138 @@ operator<<(std::ostream& os, const Leap& x)
|
|||||||
return os << x.date_ << " +";
|
return os << x.date_ << " +";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HAS_REMOTE_API
|
||||||
|
|
||||||
|
// CURL tools
|
||||||
|
|
||||||
|
static
|
||||||
|
int
|
||||||
|
curl_global()
|
||||||
|
{
|
||||||
|
if (::curl_global_init(CURL_GLOBAL_DEFAULT) != 0)
|
||||||
|
throw std::runtime_error("CURL global initialization failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const auto curl_delete = [](CURL* p) {::curl_easy_cleanup(p);};
|
||||||
|
|
||||||
|
static
|
||||||
|
std::unique_ptr<CURL, decltype(curl_delete)>
|
||||||
|
curl_init()
|
||||||
|
{
|
||||||
|
static const auto curl_is_now_initiailized = curl_global();
|
||||||
|
return std::unique_ptr<CURL, decltype(curl_delete)>{::curl_easy_init(), curl_delete};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
remote_version()
|
||||||
|
{
|
||||||
|
std::string version;
|
||||||
|
auto curl = curl_init();
|
||||||
|
if (curl != nullptr)
|
||||||
|
{
|
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_URL, "http://www.iana.org/time-zones");
|
||||||
|
using curl_callback = std::size_t(*)(void* contents, std::size_t size,
|
||||||
|
std::size_t nmemb, void* userp);
|
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION,
|
||||||
|
static_cast<curl_callback>(
|
||||||
|
[](void* contents, std::size_t size, std::size_t nmemb, void* userp)
|
||||||
|
-> std::size_t
|
||||||
|
{
|
||||||
|
auto& str = *static_cast<std::string*>(userp);
|
||||||
|
auto realsize = size * nmemb;
|
||||||
|
auto data = static_cast<const char*>(contents);
|
||||||
|
str.append(data, realsize);
|
||||||
|
return realsize;
|
||||||
|
}));
|
||||||
|
std::string str;
|
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &str);
|
||||||
|
auto res = curl_easy_perform(curl.get());
|
||||||
|
if (res == CURLE_OK)
|
||||||
|
{
|
||||||
|
CONSTDATA char db[] = "/time-zones/repository/releases/tzdata";
|
||||||
|
CONSTDATA auto db_size = sizeof(db) - 1;
|
||||||
|
auto p = str.find(db, 0, db_size);
|
||||||
|
if (p != std::string::npos && p + (db_size + 5) <= str.size())
|
||||||
|
version = str.substr(p + db_size, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
remote_download(const std::string& version)
|
||||||
|
{
|
||||||
|
assert(!version.empty());
|
||||||
|
auto curl = curl_init();
|
||||||
|
if (curl != nullptr)
|
||||||
|
{
|
||||||
|
auto url = "http://www.iana.org/time-zones/repository/releases/tzdata" +
|
||||||
|
version + ".tar.gz";
|
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
|
||||||
|
using curl_callback = std::size_t(*)(void* contents, std::size_t size,
|
||||||
|
std::size_t nmemb, void* userp);
|
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION,
|
||||||
|
static_cast<curl_callback>(
|
||||||
|
[](void* contents, std::size_t size, std::size_t nmemb, void* userp)
|
||||||
|
-> std::size_t
|
||||||
|
{
|
||||||
|
auto& of = *static_cast<std::ofstream*>(userp);
|
||||||
|
auto realsize = size * nmemb;
|
||||||
|
auto data = static_cast<const char*>(contents);
|
||||||
|
of.write(data, realsize);
|
||||||
|
return realsize;
|
||||||
|
}));
|
||||||
|
auto tarfile = install + version + ".tar.gz";
|
||||||
|
decltype(curl_easy_perform(curl.get())) res;
|
||||||
|
{
|
||||||
|
std::ofstream of(tarfile);
|
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &of);
|
||||||
|
res = curl_easy_perform(curl.get());
|
||||||
|
}
|
||||||
|
return res == CURLE_OK;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
remote_install(const std::string& version)
|
||||||
|
{
|
||||||
|
auto success = false;
|
||||||
|
assert(!version.empty());
|
||||||
|
auto tarfile = install + version + ".tar.gz";
|
||||||
|
if (file_exists(tarfile))
|
||||||
|
{
|
||||||
|
if (file_exists(install))
|
||||||
|
std::system(("rm -R " + install).c_str());
|
||||||
|
if (std::system(("mkdir " + install + " && "
|
||||||
|
"tar -xzf " + tarfile + " -C " + install).c_str()) == 0)
|
||||||
|
success = true;
|
||||||
|
std::system(("rm " + tarfile).c_str());
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAS_REMOTE_API
|
||||||
|
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
get_version(const std::string& path)
|
||||||
|
{
|
||||||
|
std::ifstream infile(path + "Makefile");
|
||||||
|
std::string version;
|
||||||
|
while (infile)
|
||||||
|
{
|
||||||
|
infile >> version;
|
||||||
|
if (version == "VERSION=")
|
||||||
|
{
|
||||||
|
infile >> version;
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Unable to get Timezone database version from " + path);
|
||||||
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
TZ_DB
|
TZ_DB
|
||||||
init_tzdb()
|
init_tzdb()
|
||||||
@@ -1903,6 +2053,35 @@ init_tzdb()
|
|||||||
bool continue_zone = false;
|
bool continue_zone = false;
|
||||||
TZ_DB db;
|
TZ_DB db;
|
||||||
|
|
||||||
|
#if AUTO_DOWNLOAD
|
||||||
|
if (!file_exists(install))
|
||||||
|
{
|
||||||
|
auto rv = remote_version();
|
||||||
|
if (!rv.empty() && remote_download(rv))
|
||||||
|
remote_install(rv);
|
||||||
|
if (!file_exists(install))
|
||||||
|
{
|
||||||
|
std::string msg = "Timezone database not found at \"";
|
||||||
|
msg += install;
|
||||||
|
msg += "\"";
|
||||||
|
throw std::runtime_error(msg);
|
||||||
|
}
|
||||||
|
db.version = get_version(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
db.version = get_version(path);
|
||||||
|
auto rv = remote_version();
|
||||||
|
if (!rv.empty() && db.version != rv)
|
||||||
|
{
|
||||||
|
if (remote_download(rv))
|
||||||
|
{
|
||||||
|
remote_install(rv);
|
||||||
|
db.version = get_version(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else // !AUTO_DOWNLOAD
|
||||||
if (!file_exists(install))
|
if (!file_exists(install))
|
||||||
{
|
{
|
||||||
std::string msg = "Timezone database not found at \"";
|
std::string msg = "Timezone database not found at \"";
|
||||||
@@ -1910,21 +2089,8 @@ init_tzdb()
|
|||||||
msg += "\"";
|
msg += "\"";
|
||||||
throw std::runtime_error(msg);
|
throw std::runtime_error(msg);
|
||||||
}
|
}
|
||||||
|
db.version = get_version(path);
|
||||||
{
|
#endif // !AUTO_DOWNLOAD
|
||||||
std::ifstream infile(path + "Makefile");
|
|
||||||
while (infile)
|
|
||||||
{
|
|
||||||
infile >> line;
|
|
||||||
if (line == "VERSION=")
|
|
||||||
{
|
|
||||||
infile >> db.version;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (db.version.empty())
|
|
||||||
throw std::runtime_error("Unable to get Timezone database version");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& filename : files)
|
for (const auto& filename : files)
|
||||||
{
|
{
|
||||||
@@ -2001,13 +2167,11 @@ access_tzdb()
|
|||||||
const TZ_DB&
|
const TZ_DB&
|
||||||
reload_tzdb()
|
reload_tzdb()
|
||||||
{
|
{
|
||||||
return access_tzdb() = init_tzdb();
|
#if AUTO_DOWNLOAD
|
||||||
}
|
auto const& v = access_tzdb().version;
|
||||||
|
if (!v.empty() && v == remote_version())
|
||||||
const TZ_DB&
|
return access_tzdb();
|
||||||
reload_tzdb(const std::string& new_install)
|
#endif
|
||||||
{
|
|
||||||
install = new_install;
|
|
||||||
return access_tzdb() = init_tzdb();
|
return access_tzdb() = init_tzdb();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
24
tz.h
24
tz.h
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) 2015 Howard Hinnant
|
// Copyright (c) 2015, 2016 Howard Hinnant
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -54,6 +54,21 @@ Technically any OS may use the mapping process but currently only Windows does u
|
|||||||
# define LAZY_INIT 1
|
# define LAZY_INIT 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAS_REMOTE_API
|
||||||
|
# ifndef _MSC_VER
|
||||||
|
# define HAS_REMOTE_API 1
|
||||||
|
# else
|
||||||
|
# define HAS_REMOTE_API 0
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef AUTO_DOWNLOAD
|
||||||
|
# define AUTO_DOWNLOAD HAS_REMOTE_API
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static_assert(HAS_REMOTE_API == 0 ? AUTO_DOWNLOAD == 0 : true,
|
||||||
|
"AUTO_DOWNLOAD can not be turned on without HAS_REMOTE_API");
|
||||||
|
|
||||||
#include "date.h"
|
#include "date.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -627,7 +642,12 @@ std::ostream& operator<<(std::ostream& os, const TZ_DB& db);
|
|||||||
|
|
||||||
const TZ_DB& get_tzdb();
|
const TZ_DB& get_tzdb();
|
||||||
const TZ_DB& reload_tzdb();
|
const TZ_DB& reload_tzdb();
|
||||||
const TZ_DB& reload_tzdb(const std::string& new_install);
|
|
||||||
|
#if HAS_REMOTE_API
|
||||||
|
std::string remote_version();
|
||||||
|
bool remote_download(const std::string& version);
|
||||||
|
bool remote_install(const std::string& version);
|
||||||
|
#endif
|
||||||
|
|
||||||
const Zone* locate_zone(const std::string& tz_name);
|
const Zone* locate_zone(const std::string& tz_name);
|
||||||
#ifdef TZ_TEST
|
#ifdef TZ_TEST
|
||||||
|
Reference in New Issue
Block a user