Using cctz library [#CLICKHOUSE-2].

This commit is contained in:
Alexey Milovidov 2017-01-21 05:32:02 +03:00
parent ed60f63e26
commit 8de0740291
2 changed files with 36 additions and 124 deletions

View File

@ -4,6 +4,8 @@ SET(CONFIG_VERSION ${CMAKE_CURRENT_BINARY_DIR}/include/common/config_version.h)
SET(CONFIG_COMMON ${CMAKE_CURRENT_BINARY_DIR}/include/common/config_common.h)
include_directories (include)
include_directories (BEFORE ${CMAKE_SOURCE_DIR}/contrib/libcctz/include)
include(${CMAKE_SOURCE_DIR}/cmake/dbms_include.cmake)
if (APPLE)
@ -77,16 +79,14 @@ else ()
message (STATUS "Disabling libtcmalloc for valgrind better analysis")
endif ()
include (${ClickHouse_SOURCE_DIR}/cmake/find_glib.cmake)
find_package (Threads)
target_link_libraries (
common
pocoext
cctz
${MALLOC_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
${GLIB_LIBS}
${ICU_LIBS}
${RT_LIBRARIES})

View File

@ -1,121 +1,35 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#if __clang__
#pragma GCC diagnostic ignored "-Wdeprecated-register"
#endif
#include <glib.h>
#pragma GCC diagnostic pop
#include <civil_time.h>
#include <time_zone.h>
#include <common/DateLUTImpl.h>
#include <Poco/Exception.h>
#include <memory>
#include <chrono>
#include <cstring>
#include <iostream>
namespace details
{
namespace
{
struct GTimeZoneUnref
UInt8 getDayOfWeek(const cctz::civil_day & date)
{
void operator()(GTimeZone * tz) const
cctz::weekday day_of_week = cctz::get_weekday(date);
switch (day_of_week)
{
g_time_zone_unref(tz);
case cctz::weekday::monday: return 1;
case cctz::weekday::tuesday: return 2;
case cctz::weekday::wednesday: return 3;
case cctz::weekday::thursday: return 4;
case cctz::weekday::friday: return 5;
case cctz::weekday::saturday: return 6;
case cctz::weekday::sunday: return 7;
default:
throw Poco::Exception("Logical error: incorrect week day.");
}
};
using GTimeZonePtr = std::unique_ptr<GTimeZone, GTimeZoneUnref>;
struct GDateTimeUnref
{
void operator()(GDateTime * dt) const
{
g_date_time_unref(dt);
}
};
using GDateTimePtr = std::unique_ptr<GDateTime, GDateTimeUnref>;
GTimeZonePtr createGTimeZone(const std::string & description)
{
GTimeZone * tz = g_time_zone_new(description.c_str());
if (tz == nullptr)
throw Poco::Exception("Failed to create GTimeZone object.");
return GTimeZonePtr(tz);
}
GDateTimePtr createGDateTimeUTC(time_t timestamp)
{
GDateTime * dt = g_date_time_new_from_unix_utc(timestamp);
if (dt == nullptr)
throw Poco::Exception("Failed to create GDateTime object.");
return GDateTimePtr(dt);
}
/// Create GDateTime object, interpreting passed unix timestamp in passed timezone.
GDateTimePtr createGDateTimeLocal(const GTimeZonePtr & p_tz, time_t timestamp)
{
GDateTimePtr utc = createGDateTimeUTC(timestamp);
GDateTime * local = g_date_time_to_timezone(utc.get(), p_tz.get());
if (local == nullptr)
throw Poco::Exception("Failed to create GDateTime object.");
return GDateTimePtr(local);
}
/// Create GDateTime object, at beginning of same day in passed time zone.
GDateTimePtr createSameDayInDifferentTimeZone(const GTimeZonePtr & p_tz, const GDateTimePtr & p_dt)
{
GDateTime * dt = p_dt.get();
gint year;
gint month;
gint day;
g_date_time_get_ymd(dt, &year, &month, &day);
GDateTime * local_dt = g_date_time_new(p_tz.get(), year, month, day, 0, 0, 0);
if (local_dt == nullptr)
throw Poco::Exception("Failed to create GDateTime object.");
return GDateTimePtr(local_dt);
}
GDateTimePtr toNextDay(const GTimeZonePtr & p_tz, const GDateTimePtr & p_dt)
{
GDateTime * dt = p_dt.get();
if (dt == nullptr)
throw Poco::Exception("Null pointer.");
dt = g_date_time_add_days(dt, 1);
if (dt == nullptr)
throw Poco::Exception("Failed to create GDateTime object.");
GDateTimePtr p_next_dt = GDateTimePtr(dt);
GDateTime * next_dt = p_next_dt.get();
gint year;
gint month;
gint day;
g_date_time_get_ymd(next_dt, &year, &month, &day);
dt = g_date_time_new(p_tz.get(), year, month, day, 0, 0, 0);
if (dt == nullptr)
throw Poco::Exception("Failed to create GDateTime object.");
return GDateTimePtr(dt);
}
}
}
@ -125,28 +39,29 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_)
size_t i = 0;
time_t start_of_day = DATE_LUT_MIN;
details::GTimeZonePtr p_tz = details::createGTimeZone(time_zone);
details::GDateTimePtr p_dt = details::createSameDayInDifferentTimeZone(p_tz, details::createGDateTimeUTC(start_of_day));
cctz::time_zone cctz_time_zone;
if (!cctz::load_time_zone(time_zone.data(), &cctz_time_zone))
throw Poco::Exception("Cannot load time zone " + time_zone_);
cctz::time_zone::absolute_lookup start_of_epoch_lookup = cctz_time_zone.lookup(std::chrono::system_clock::from_time_t(start_of_day));
cctz::civil_day date{start_of_epoch_lookup.cs};
offset_at_start_of_epoch = start_of_epoch_lookup.offset;
do
{
if (i > DATE_LUT_MAX_DAY_NUM)
throw Poco::Exception("Cannot create DateLUTImpl: i > DATE_LUT_MAX_DAY_NUM.");
GDateTime * dt = p_dt.get();
cctz::time_zone::civil_lookup lookup = cctz_time_zone.lookup(date);
start_of_day = g_date_time_to_unix(dt);
gint year;
gint month;
gint day;
g_date_time_get_ymd(dt, &year, &month, &day);
start_of_day = std::chrono::system_clock::to_time_t(lookup.pre); /// Ambiguouty is possible.
Values & values = lut[i];
values.year = year;
values.month = month;
values.day_of_month = day;
values.day_of_week = g_date_time_get_day_of_week(dt);
values.year = date.year();
values.month = date.month();
values.day_of_month = date.day();
values.day_of_week = getDayOfWeek(date);
values.date = start_of_day;
values.time_at_offset_change = 0;
@ -160,8 +75,7 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_)
{
lut[i - 1].amount_of_offset_change = amount_of_offset_change_at_prev_day;
const auto utc_offset_at_beginning_of_day = g_date_time_get_utc_offset(
details::createGDateTimeLocal(p_tz, lut[i - 1].date).get());
const auto utc_offset_at_beginning_of_day = cctz_time_zone.lookup(std::chrono::system_clock::from_time_t(lut[i - 1].date)).offset;
/// Find a time (timestamp offset from beginning of day),
/// when UTC offset was changed. Search is performed with 15-minute granularity, assuming it is enough.
@ -169,8 +83,8 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_)
time_t time_at_offset_change = 900;
while (time_at_offset_change < 65536)
{
auto utc_offset_at_current_time = g_date_time_get_utc_offset(
details::createGDateTimeLocal(p_tz, lut[i - 1].date + time_at_offset_change).get());
auto utc_offset_at_current_time = cctz_time_zone.lookup(std::chrono::system_clock::from_time_t(
lut[i - 1].date + time_at_offset_change)).offset;
if (utc_offset_at_current_time != utc_offset_at_beginning_of_day)
break;
@ -186,7 +100,7 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_)
}
/// Going to next day.
p_dt = details::toNextDay(p_tz, p_dt);
++date;
++i;
}
while (start_of_day <= DATE_LUT_MAX);
@ -198,6 +112,4 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_)
if (lut[day].month == 1 && lut[day].day_of_month == 1)
years_lut[lut[day].year - DATE_LUT_MIN_YEAR] = day;
}
offset_at_start_of_epoch = g_time_zone_get_offset(p_tz.get(), g_time_zone_find_interval(p_tz.get(), G_TIME_TYPE_UNIVERSAL, 0));
}