mirror of
https://github.com/HowardHinnant/date.git
synced 2024-12-27 00:14:07 +08:00
Implement USE_OS_TZDB for Android
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
This commit is contained in:
parent
ac0c58d5da
commit
3e43210885
@ -139,6 +139,11 @@ namespace date
|
|||||||
|
|
||||||
enum class choose {earliest, latest};
|
enum class choose {earliest, latest};
|
||||||
|
|
||||||
|
#if defined(ANDROID) || defined(__ANDROID__)
|
||||||
|
struct tzdb;
|
||||||
|
static std::unique_ptr<tzdb> init_tzdb();
|
||||||
|
#endif // defined(ANDROID) || defined(__ANDROID__)
|
||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
struct undocumented;
|
struct undocumented;
|
||||||
@ -821,6 +826,10 @@ public:
|
|||||||
|
|
||||||
#if !USE_OS_TZDB
|
#if !USE_OS_TZDB
|
||||||
DATE_API void add(const std::string& s);
|
DATE_API void add(const std::string& s);
|
||||||
|
#else
|
||||||
|
#if defined(ANDROID) || defined(__ANDROID__)
|
||||||
|
friend std::unique_ptr<tzdb> init_tzdb();
|
||||||
|
#endif // defined(ANDROID) || defined(__ANDROID__)
|
||||||
#endif // !USE_OS_TZDB
|
#endif // !USE_OS_TZDB
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -844,6 +853,9 @@ private:
|
|||||||
DATE_API void
|
DATE_API void
|
||||||
load_data(std::istream& inf, std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt,
|
load_data(std::istream& inf, std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt,
|
||||||
std::int32_t tzh_typecnt, std::int32_t tzh_charcnt);
|
std::int32_t tzh_typecnt, std::int32_t tzh_charcnt);
|
||||||
|
# if defined(ANDROID) || defined(__ANDROID__)
|
||||||
|
void parse_from_android_tzdata(std::ifstream& inf, const std::size_t off);
|
||||||
|
# endif // defined(ANDROID) || defined(__ANDROID__)
|
||||||
#else // !USE_OS_TZDB
|
#else // !USE_OS_TZDB
|
||||||
DATE_API sys_info get_info_impl(sys_seconds tp, int tz_int) const;
|
DATE_API sys_info get_info_impl(sys_seconds tp, int tz_int) const;
|
||||||
DATE_API void adjust_infos(const std::vector<detail::Rule>& rules);
|
DATE_API void adjust_infos(const std::vector<detail::Rule>& rules);
|
||||||
|
168
src/tz.cpp
168
src/tz.cpp
@ -93,8 +93,25 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(ANDROID) || defined(__ANDROID__)
|
#if defined(ANDROID) || defined(__ANDROID__)
|
||||||
#include <sys/system_properties.h>
|
# include <sys/system_properties.h>
|
||||||
#endif
|
# if USE_OS_TZDB
|
||||||
|
# define MISSING_LEAP_SECONDS 1
|
||||||
|
// from https://android.googlesource.com/platform/bionic/+/master/libc/tzcode/bionic.cpp
|
||||||
|
static constexpr size_t ANDROID_TIMEZONE_NAME_LENGTH = 40;
|
||||||
|
struct bionic_tzdata_header_t {
|
||||||
|
char tzdata_version[12];
|
||||||
|
std::int32_t index_offset;
|
||||||
|
std::int32_t data_offset;
|
||||||
|
std::int32_t final_offset;
|
||||||
|
};
|
||||||
|
struct index_entry_t {
|
||||||
|
char buf[ANDROID_TIMEZONE_NAME_LENGTH];
|
||||||
|
std::int32_t start;
|
||||||
|
std::int32_t length;
|
||||||
|
std::int32_t unused; // Was raw GMT offset; always 0 since tzdata2014f (L).
|
||||||
|
};
|
||||||
|
# endif // USE_OS_TZDB
|
||||||
|
#endif // defined(ANDROID) || defined(__ANDROID__)
|
||||||
|
|
||||||
#if USE_OS_TZDB
|
#if USE_OS_TZDB
|
||||||
# include <dirent.h>
|
# include <dirent.h>
|
||||||
@ -465,7 +482,18 @@ discover_tz_dir()
|
|||||||
{
|
{
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
# ifndef __APPLE__
|
# if defined(ANDROID) || defined(__ANDROID__)
|
||||||
|
CONSTDATA auto tz_dir_default = "/apex/com.android.tzdata/etc/tz";
|
||||||
|
CONSTDATA auto tz_dir_fallback = "/system/usr/share/zoneinfo";
|
||||||
|
|
||||||
|
// Check updatable path first
|
||||||
|
if(stat(tz_dir_default, &sb) == 0 && S_ISDIR(sb.st_mode))
|
||||||
|
return tz_dir_default;
|
||||||
|
else if(stat(tz_dir_fallback, &sb) == 0 && S_ISDIR(sb.st_mode))
|
||||||
|
return tz_dir_fallback;
|
||||||
|
else
|
||||||
|
throw runtime_error("discover_tz_dir failed to find zoneinfo\n");
|
||||||
|
# elif !defined(__APPLE__)
|
||||||
CONSTDATA auto tz_dir_default = "/usr/share/zoneinfo";
|
CONSTDATA auto tz_dir_default = "/usr/share/zoneinfo";
|
||||||
CONSTDATA auto tz_dir_buildroot = "/usr/share/zoneinfo/uclibc";
|
CONSTDATA auto tz_dir_buildroot = "/usr/share/zoneinfo/uclibc";
|
||||||
|
|
||||||
@ -523,7 +551,9 @@ get_tz_dir()
|
|||||||
static_assert(min_year <= max_year, "Configuration error");
|
static_assert(min_year <= max_year, "Configuration error");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(ANDROID) && !defined(__ANDROID__)
|
||||||
static std::unique_ptr<tzdb> init_tzdb();
|
static std::unique_ptr<tzdb> init_tzdb();
|
||||||
|
#endif // !defined(ANDROID) && !defined(__ANDROID__)
|
||||||
|
|
||||||
tzdb_list::~tzdb_list()
|
tzdb_list::~tzdb_list()
|
||||||
{
|
{
|
||||||
@ -616,7 +646,8 @@ static
|
|||||||
bool
|
bool
|
||||||
is_prefix_of(std::string const& key, std::string const& value)
|
is_prefix_of(std::string const& key, std::string const& value)
|
||||||
{
|
{
|
||||||
return key.compare(0, key.size(), value, 0, key.size()) == 0;
|
const size_t size = std::min(key.size(), value.size());
|
||||||
|
return key.compare(0, size, value, 0, size) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
@ -2185,6 +2216,9 @@ time_zone::load_data(std::istream& inf,
|
|||||||
void
|
void
|
||||||
time_zone::init_impl()
|
time_zone::init_impl()
|
||||||
{
|
{
|
||||||
|
#if defined(ANDROID) || defined(__ANDROID__)
|
||||||
|
return;
|
||||||
|
#endif // defined(ANDROID) || defined(__ANDROID__)
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
auto name = get_tz_dir() + ('/' + name_);
|
auto name = get_tz_dir() + ('/' + name_);
|
||||||
@ -2346,6 +2380,86 @@ time_zone::get_info_impl(local_seconds tp) const
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(ANDROID) || defined(__ANDROID__)
|
||||||
|
void
|
||||||
|
time_zone::parse_from_android_tzdata(std::ifstream& inf, const std::size_t off)
|
||||||
|
{
|
||||||
|
using namespace std;
|
||||||
|
using namespace std::chrono;
|
||||||
|
if (!inf.is_open())
|
||||||
|
throw std::runtime_error{"Unable to open tzdata"};
|
||||||
|
std::size_t restorepos = inf.tellg();
|
||||||
|
inf.seekg(off, inf.beg);
|
||||||
|
load_header(inf);
|
||||||
|
auto v = load_version(inf);
|
||||||
|
std::int32_t tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
|
||||||
|
tzh_timecnt, tzh_typecnt, tzh_charcnt;
|
||||||
|
skip_reserve(inf);
|
||||||
|
load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
|
||||||
|
tzh_timecnt, tzh_typecnt, tzh_charcnt);
|
||||||
|
if (v == 0)
|
||||||
|
{
|
||||||
|
load_data<int32_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt, tzh_charcnt);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt +
|
||||||
|
tzh_ttisstdcnt + tzh_ttisgmtcnt);
|
||||||
|
load_header(inf);
|
||||||
|
auto v2 = load_version(inf);
|
||||||
|
assert(v == v2);
|
||||||
|
skip_reserve(inf);
|
||||||
|
#else // defined(NDEBUG)
|
||||||
|
inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt +
|
||||||
|
tzh_ttisstdcnt + tzh_ttisgmtcnt + (4+1+15));
|
||||||
|
#endif // defined(NDEBUG)
|
||||||
|
load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
|
||||||
|
tzh_timecnt, tzh_typecnt, tzh_charcnt);
|
||||||
|
load_data<int64_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt, tzh_charcnt);
|
||||||
|
}
|
||||||
|
#if !MISSING_LEAP_SECONDS
|
||||||
|
if (tzh_leapcnt > 0)
|
||||||
|
{
|
||||||
|
auto& leap_seconds = get_tzdb_list().front().leap_seconds;
|
||||||
|
auto itr = leap_seconds.begin();
|
||||||
|
auto l = itr->date();
|
||||||
|
seconds leap_count{0};
|
||||||
|
for (auto t = std::upper_bound(transitions_.begin(), transitions_.end(), l,
|
||||||
|
[](const sys_seconds& x, const transition& ct)
|
||||||
|
{
|
||||||
|
return x < ct.timepoint;
|
||||||
|
});
|
||||||
|
t != transitions_.end(); ++t)
|
||||||
|
{
|
||||||
|
while (t->timepoint >= l)
|
||||||
|
{
|
||||||
|
++leap_count;
|
||||||
|
if (++itr == leap_seconds.end())
|
||||||
|
l = sys_days(max_year/max_day);
|
||||||
|
else
|
||||||
|
l = itr->date() + leap_count;
|
||||||
|
}
|
||||||
|
t->timepoint -= leap_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // !MISSING_LEAP_SECONDS
|
||||||
|
auto b = transitions_.begin();
|
||||||
|
auto i = transitions_.end();
|
||||||
|
if (i != b)
|
||||||
|
{
|
||||||
|
for (--i; i != b; --i)
|
||||||
|
{
|
||||||
|
if (i->info->offset == i[-1].info->offset &&
|
||||||
|
i->info->abbrev == i[-1].info->abbrev &&
|
||||||
|
i->info->is_dst == i[-1].info->is_dst)
|
||||||
|
i = transitions_.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inf.seekg(restorepos, inf.beg);
|
||||||
|
}
|
||||||
|
#endif // defined(ANDROID) || defined(__ANDROID__)
|
||||||
|
|
||||||
std::ostream&
|
std::ostream&
|
||||||
operator<<(std::ostream& os, const time_zone& z)
|
operator<<(std::ostream& os, const time_zone& z)
|
||||||
{
|
{
|
||||||
@ -2782,6 +2896,16 @@ std::string
|
|||||||
get_version()
|
get_version()
|
||||||
{
|
{
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
#if defined(ANDROID) || defined(__ANDROID__)
|
||||||
|
auto path = get_tz_dir() + string("/tzdata");
|
||||||
|
ifstream in{path};
|
||||||
|
bionic_tzdata_header_t hdr{};
|
||||||
|
if (in)
|
||||||
|
{
|
||||||
|
in.read(reinterpret_cast<char*>(&hdr), sizeof(bionic_tzdata_header_t));
|
||||||
|
return string(hdr.tzdata_version).replace(0, 6, "");
|
||||||
|
}
|
||||||
|
#else
|
||||||
auto path = get_tz_dir() + string("/+VERSION");
|
auto path = get_tz_dir() + string("/+VERSION");
|
||||||
ifstream in{path};
|
ifstream in{path};
|
||||||
string version;
|
string version;
|
||||||
@ -2797,9 +2921,11 @@ get_version()
|
|||||||
in >> version;
|
in >> version;
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
#endif // defined(ANDROID) || defined(__ANDROID__)
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(ANDROID) && !defined(__ANDROID__)
|
||||||
static
|
static
|
||||||
std::vector<leap_second>
|
std::vector<leap_second>
|
||||||
find_read_and_leap_seconds()
|
find_read_and_leap_seconds()
|
||||||
@ -2881,6 +3007,7 @@ find_read_and_leap_seconds()
|
|||||||
#endif
|
#endif
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
#endif // !defined(ANDROID) && !defined(__ANDROID__)
|
||||||
|
|
||||||
static
|
static
|
||||||
std::unique_ptr<tzdb>
|
std::unique_ptr<tzdb>
|
||||||
@ -2888,6 +3015,38 @@ init_tzdb()
|
|||||||
{
|
{
|
||||||
std::unique_ptr<tzdb> db(new tzdb);
|
std::unique_ptr<tzdb> db(new tzdb);
|
||||||
|
|
||||||
|
#if defined(ANDROID) || defined(__ANDROID__)
|
||||||
|
auto path = get_tz_dir() + std::string("/tzdata");
|
||||||
|
std::ifstream in{path};
|
||||||
|
if (!in)
|
||||||
|
throw std::runtime_error("Can not open " + path);
|
||||||
|
bionic_tzdata_header_t hdr{};
|
||||||
|
in.read(reinterpret_cast<char*>(&hdr), sizeof(bionic_tzdata_header_t));
|
||||||
|
if (!is_prefix_of(hdr.tzdata_version, "tzdata") || hdr.tzdata_version[11] != 0)
|
||||||
|
throw std::runtime_error("Malformed tzdata - invalid magic!");
|
||||||
|
maybe_reverse_bytes(hdr.index_offset);
|
||||||
|
maybe_reverse_bytes(hdr.data_offset);
|
||||||
|
maybe_reverse_bytes(hdr.final_offset);
|
||||||
|
if (hdr.index_offset > hdr.data_offset)
|
||||||
|
throw std::runtime_error("Malformed tzdata - hdr.index_offset > hdr.data_offset!");
|
||||||
|
const size_t index_size = hdr.data_offset - hdr.index_offset;
|
||||||
|
if ((index_size % sizeof(index_entry_t)) != 0)
|
||||||
|
throw std::runtime_error("Malformed tzdata - index size malformed!");
|
||||||
|
//Iterate through zone index
|
||||||
|
index_entry_t index_entry{};
|
||||||
|
for (size_t idx = 0; idx < index_size; idx += sizeof(index_entry_t)) {
|
||||||
|
in.read(reinterpret_cast<char*>(&index_entry), sizeof(index_entry_t));
|
||||||
|
maybe_reverse_bytes(index_entry.start);
|
||||||
|
maybe_reverse_bytes(index_entry.length);
|
||||||
|
time_zone timezone{std::string(index_entry.buf),
|
||||||
|
detail::undocumented{}};
|
||||||
|
timezone.parse_from_android_tzdata(in, hdr.data_offset + index_entry.start);
|
||||||
|
db->zones.emplace_back(std::move(timezone));
|
||||||
|
}
|
||||||
|
db->zones.shrink_to_fit();
|
||||||
|
std::sort(db->zones.begin(), db->zones.end());
|
||||||
|
db->version = std::string(hdr.tzdata_version).replace(0, 6, "");
|
||||||
|
#else
|
||||||
//Iterate through folders
|
//Iterate through folders
|
||||||
std::queue<std::string> subfolders;
|
std::queue<std::string> subfolders;
|
||||||
subfolders.emplace(get_tz_dir());
|
subfolders.emplace(get_tz_dir());
|
||||||
@ -2940,6 +3099,7 @@ init_tzdb()
|
|||||||
std::sort(db->zones.begin(), db->zones.end());
|
std::sort(db->zones.begin(), db->zones.end());
|
||||||
db->leap_seconds = find_read_and_leap_seconds();
|
db->leap_seconds = find_read_and_leap_seconds();
|
||||||
db->version = get_version();
|
db->version = get_version();
|
||||||
|
#endif // defined(ANDROID) || defined(__ANDROID__)
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user