Skip to content

Commit

Permalink
Adding OffsetZone
Browse files Browse the repository at this point in the history
  • Loading branch information
rok committed Apr 12, 2022
1 parent f0b5c49 commit 7da61fb
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 35 deletions.
2 changes: 1 addition & 1 deletion cpp/src/arrow/compute/kernels/scalar_cast_string.cc
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ struct TemporalToStringCastFunctor<O, TimestampType> {
static const std::string kFormatString = "%Y-%m-%d %H:%M:%S%z";
static const std::string kUtcFormatString = "%Y-%m-%d %H:%M:%SZ";
DCHECK(!timezone.empty());
ARROW_ASSIGN_OR_RAISE(const time_zone* tz, LocateZone(timezone));
ARROW_ASSIGN_OR_RAISE(const ArrowTimeZone* tz, LocateZone(timezone));
ARROW_ASSIGN_OR_RAISE(std::locale locale, GetLocale("C"));
TimestampFormatter<Duration> formatter{
timezone == "UTC" ? kUtcFormatString : kFormatString, tz, locale};
Expand Down
3 changes: 0 additions & 3 deletions cpp/src/arrow/compute/kernels/scalar_temporal_binary.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,14 @@ using arrow_vendored::date::floor;
using arrow_vendored::date::hh_mm_ss;
using arrow_vendored::date::local_days;
using arrow_vendored::date::local_time;
using arrow_vendored::date::locate_zone;
using arrow_vendored::date::sys_days;
using arrow_vendored::date::sys_time;
using arrow_vendored::date::time_zone;
using arrow_vendored::date::trunc;
using arrow_vendored::date::weekday;
using arrow_vendored::date::weeks;
using arrow_vendored::date::year_month_day;
using arrow_vendored::date::year_month_weekday;
using arrow_vendored::date::years;
using arrow_vendored::date::zoned_time;
using arrow_vendored::date::literals::dec;
using arrow_vendored::date::literals::jan;
using arrow_vendored::date::literals::last;
Expand Down
60 changes: 41 additions & 19 deletions cpp/src/arrow/compute/kernels/scalar_temporal_unary.cc
Original file line number Diff line number Diff line change
Expand Up @@ -674,15 +674,21 @@ struct Nanosecond {

template <typename Duration>
struct IsDaylightSavings {
explicit IsDaylightSavings(const FunctionOptions* options, const time_zone* tz)
explicit IsDaylightSavings(const FunctionOptions* options, const ArrowTimeZone* tz)
: tz_(tz) {}

template <typename T, typename Arg0>
T Call(KernelContext*, Arg0 arg, Status*) const {
return tz_->get_info(sys_time<Duration>{Duration{arg}}).save.count() != 0;
if (tz_->index() == 0) {
auto tz = tz_->get<time_zone, 0>();
return tz->get_info(sys_time<Duration>{Duration{arg}}).save.count() != 0;
} else {
auto tz = tz_->get<OffsetZone, 1>();
return tz->get_info(sys_time<Duration>{Duration{arg}}).save.count() != 0;
}
}

const time_zone* tz_;
const ArrowTimeZone* tz_;
};

// ----------------------------------------------------------------------
Expand Down Expand Up @@ -1059,7 +1065,7 @@ Result<std::locale> GetLocale(const std::string& locale) {
template <typename Duration, typename InType>
struct Strftime {
const StrftimeOptions& options;
const time_zone* tz;
const ArrowTimeZone* tz;
const std::locale locale;

static Result<Strftime> Make(KernelContext* ctx, const DataType& type) {
Expand All @@ -1080,8 +1086,7 @@ struct Strftime {
options.format);
}
}

ARROW_ASSIGN_OR_RAISE(const time_zone* tz,
ARROW_ASSIGN_OR_RAISE(const ArrowTimeZone* tz,
LocateZone(timezone.empty() ? "UTC" : timezone));

ARROW_ASSIGN_OR_RAISE(std::locale locale, GetLocale(options.locale));
Expand Down Expand Up @@ -1270,25 +1275,42 @@ Result<ValueDescr> ResolveAssumeTimezoneOutput(KernelContext* ctx,

template <typename Duration>
struct AssumeTimezone {
explicit AssumeTimezone(const AssumeTimezoneOptions* options, const time_zone* tz)
explicit AssumeTimezone(const AssumeTimezoneOptions* options, const ArrowTimeZone* tz)
: options(*options), tz_(tz) {}

template <typename T, typename Arg0>
T get_local_time(Arg0 arg, const time_zone* tz) const {
return static_cast<T>(zoned_time<Duration>(tz, local_time<Duration>(Duration{arg}))
.get_sys_time()
.time_since_epoch()
.count());
T get_local_time(Arg0 arg, const ArrowTimeZone* tz) const {
if (tz->index() == 0) {
return static_cast<T>(zoned_time<Duration>(tz->get<time_zone, 0>(), local_time<Duration>(Duration{arg}))
.get_sys_time()
.time_since_epoch()
.count());
} else {
return static_cast<T>(zoned_time<Duration, const OffsetZone*>(tz->get<OffsetZone, 1>(), local_time<Duration>(Duration{arg}))
.get_sys_time()
.time_since_epoch()
.count());
}
}

template <typename T, typename Arg0>
T get_local_time(Arg0 arg, const arrow_vendored::date::choose choose,
const time_zone* tz) const {
return static_cast<T>(
zoned_time<Duration>(tz, local_time<Duration>(Duration{arg}), choose)
.get_sys_time()
.time_since_epoch()
.count());
const ArrowTimeZone* tz) const {
if (tz->index() == 0) {
return static_cast<T>(zoned_time<Duration>(tz->get<time_zone, 0>(),
local_time<Duration>(Duration{arg}),
choose)
.get_sys_time()
.time_since_epoch()
.count());
} else {
return static_cast<T>(
zoned_time<Duration, const OffsetZone*>(
tz->get<OffsetZone, 1>(), local_time<Duration>(Duration{arg}), choose)
.get_sys_time()
.time_since_epoch()
.count());
}
}

template <typename T, typename Arg0>
Expand Down Expand Up @@ -1329,7 +1351,7 @@ struct AssumeTimezone {
return 0;
}
AssumeTimezoneOptions options;
const time_zone* tz_;
const ArrowTimeZone* tz_;
};

// ----------------------------------------------------------------------
Expand Down
60 changes: 48 additions & 12 deletions cpp/src/arrow/compute/kernels/temporal_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

#include "arrow/compute/api_scalar.h"
#include "arrow/vendored/datetime.h"
#include "arrow/util/value_parsing.h"
#include "arrow/util/variant.h"

namespace arrow {

Expand All @@ -33,23 +35,36 @@ using arrow_vendored::date::floor;
using arrow_vendored::date::local_days;
using arrow_vendored::date::local_time;
using arrow_vendored::date::locate_zone;
using arrow_vendored::date::OffsetZone;
using arrow_vendored::date::sys_days;
using arrow_vendored::date::sys_time;
using arrow_vendored::date::time_zone;
using arrow_vendored::date::year_month_day;
using arrow_vendored::date::zoned_time;
using std::chrono::duration_cast;

using ArrowTimeZone = util::Variant<const time_zone*, const arrow_vendored::date::OffsetZone*>;

inline int64_t GetQuarter(const year_month_day& ymd) {
return static_cast<int64_t>((static_cast<uint32_t>(ymd.month()) - 1) / 3);
}

static inline Result<const time_zone*> LocateZone(const std::string& timezone) {
static inline Result<const ArrowTimeZone*> LocateZone(const std::string& timezone) {
ArrowTimeZone tz;
try {
return locate_zone(timezone);
std::chrono::minutes zone_offset;
const bool is_offset = arrow::internal::detail::ParseHH_MM(timezone.c_str(), &zone_offset);
if (is_offset) {
const auto offset_zone = OffsetZone(zone_offset);
tz = ArrowTimeZone{&offset_zone};
}
else {
tz = ArrowTimeZone{locate_zone(timezone)};
}
} catch (const std::runtime_error& ex) {
return Status::Invalid("Cannot locate timezone '", timezone, "': ", ex.what());
}
return &tz;
}

static inline const std::string& GetInputTimezone(const DataType& type) {
Expand Down Expand Up @@ -112,19 +127,33 @@ struct ZonedLocalizer {
using days_t = local_days;

// Timezone-localizing conversions: UTC -> local time
const time_zone* tz;
const ArrowTimeZone* tz_;

template <typename Duration>
local_time<Duration> ConvertTimePoint(int64_t t) const {
return tz->to_local(sys_time<Duration>(Duration{t}));
if (tz_->index() == 1) {
auto tz = tz_->get<time_zone, 0>();
return tz->to_local(sys_time<Duration>(Duration{t}));
} else {
auto tz = tz_->get<OffsetZone, 1>();
return tz->to_local(sys_time<Duration>(Duration{t}));
}
}

template <typename Duration>
Duration ConvertLocalToSys(Duration t, Status* st) const {
try {
return zoned_time<Duration>{tz, local_time<Duration>(t)}
.get_sys_time()
.time_since_epoch();
if (tz_->index() == 1) {
const time_zone* tz = tz_->get<time_zone, 0>();
return zoned_time<Duration>{tz, local_time<Duration>(t)}
.get_sys_time()
.time_since_epoch();
} else {
const OffsetZone* tz = tz_->get<OffsetZone, 1>();
return zoned_time<Duration, const OffsetZone*>{tz, local_time<Duration>(t)}
.get_sys_time()
.time_since_epoch();
}
} catch (const arrow_vendored::date::nonexistent_local_time& e) {
*st = Status::Invalid("Local time does not exist: ", e.what());
return Duration{0};
Expand All @@ -140,22 +169,29 @@ struct ZonedLocalizer {
template <typename Duration>
struct TimestampFormatter {
const char* format;
const time_zone* tz;
const ArrowTimeZone* tz_;
std::ostringstream bufstream;

explicit TimestampFormatter(const std::string& format, const time_zone* tz,
explicit TimestampFormatter(const std::string& format, const ArrowTimeZone* tz,
const std::locale& locale)
: format(format.c_str()), tz(tz) {
: format(format.c_str()), tz_(tz) {
bufstream.imbue(locale);
// Propagate errors as C++ exceptions (to get an actual error message)
bufstream.exceptions(std::ios::failbit | std::ios::badbit);
}

Result<std::string> operator()(int64_t arg) {
bufstream.str("");
const auto zt = zoned_time<Duration>{tz, sys_time<Duration>(Duration{arg})};
try {
arrow_vendored::date::to_stream(bufstream, format, zt);
if (tz_->index() == 0) {
auto tz = tz_->get<time_zone, 0>();
const auto zt = zoned_time<Duration>{tz, sys_time<Duration>(Duration{arg})};
arrow_vendored::date::to_stream(bufstream, format, zt);
} else {
auto tz = tz_->get<OffsetZone, 1>();
const auto zt = zoned_time<Duration, const OffsetZone*>{tz, sys_time<Duration>(Duration{arg})};
arrow_vendored::date::to_stream(bufstream, format, zt);
}
} catch (const std::runtime_error& ex) {
bufstream.clear();
return Status::Invalid("Failed formatting timestamp: ", ex.what());
Expand Down
34 changes: 34 additions & 0 deletions cpp/src/arrow/vendored/datetime/tz.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,40 @@ struct zoned_traits
{
};

class OffsetZone {
std::chrono::minutes offset_;

public:
explicit OffsetZone(std::chrono::minutes offset) : offset_{offset} {}

template <class Duration>
local_time<Duration> to_local(sys_time<Duration> tp) const {
return local_time<Duration>{(tp + offset_).time_since_epoch()};
}

template <class Duration>
sys_time<Duration> to_sys(local_time<Duration> tp, choose = choose::earliest) const {
return sys_time<Duration>{(tp - offset_).time_since_epoch()};
}

template <class Duration>
sys_info get_info(sys_time<Duration> st) const {
return {sys_seconds::min(), sys_seconds::max(), offset_, std::chrono::minutes{0},
offset_ >= std::chrono::minutes{0} ? "+" + format("%H%M", offset_)
: "-" + format("%H%M", -offset_)};
}

const OffsetZone* operator->() const { return this; }
};

template <>
struct zoned_traits<OffsetZone> {
static OffsetZone default_zone() {
using namespace std::chrono;
return OffsetZone{minutes{0}};
}
};

template <>
struct zoned_traits<const time_zone*>
{
Expand Down

0 comments on commit 7da61fb

Please sign in to comment.