forked from duckdb/duckdb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
icu-datefunc.cpp
125 lines (104 loc) · 4.02 KB
/
icu-datefunc.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include "include/icu-datefunc.hpp"
#include "duckdb/main/client_context.hpp"
#include "duckdb/common/operator/add.hpp"
#include "duckdb/common/operator/multiply.hpp"
#include "duckdb/common/types/timestamp.hpp"
namespace duckdb {
ICUDateFunc::BindData::BindData(const BindData &other)
: tz_setting(other.tz_setting), cal_setting(other.cal_setting), calendar(other.calendar->clone()) {
}
ICUDateFunc::BindData::BindData(ClientContext &context) {
Value tz_value;
if (context.TryGetCurrentSetting("TimeZone", tz_value)) {
tz_setting = tz_value.ToString();
}
auto tz = icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(icu::StringPiece(tz_setting)));
string cal_id("@calendar=");
Value cal_value;
if (context.TryGetCurrentSetting("Calendar", cal_value)) {
cal_setting = cal_value.ToString();
cal_id += cal_setting;
} else {
cal_id += "gregorian";
}
icu::Locale locale(cal_id.c_str());
UErrorCode success = U_ZERO_ERROR;
calendar.reset(icu::Calendar::createInstance(tz, locale, success));
if (U_FAILURE(success)) {
throw Exception("Unable to create ICU calendar.");
}
}
bool ICUDateFunc::BindData::Equals(const FunctionData &other_p) const {
auto &other = (const ICUDateFunc::BindData &)other_p;
return *calendar == *other.calendar;
}
unique_ptr<FunctionData> ICUDateFunc::BindData::Copy() const {
return make_unique<BindData>(*this);
}
unique_ptr<FunctionData> ICUDateFunc::Bind(ClientContext &context, ScalarFunction &bound_function,
vector<unique_ptr<Expression>> &arguments) {
return make_unique<BindData>(context);
}
void ICUDateFunc::SetTimeZone(icu::Calendar *calendar, const string_t &tz_id) {
auto tz = icu_66::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(icu::StringPiece(tz_id.GetString())));
calendar->adoptTimeZone(tz);
}
timestamp_t ICUDateFunc::GetTimeUnsafe(icu::Calendar *calendar, uint64_t micros) {
// Extract the new time
UErrorCode status = U_ZERO_ERROR;
const auto millis = int64_t(calendar->getTime(status));
if (U_FAILURE(status)) {
throw Exception("Unable to get ICU calendar time.");
}
return timestamp_t(millis * Interval::MICROS_PER_MSEC + micros);
}
timestamp_t ICUDateFunc::GetTime(icu::Calendar *calendar, uint64_t micros) {
// Extract the new time
UErrorCode status = U_ZERO_ERROR;
auto millis = int64_t(calendar->getTime(status));
if (U_FAILURE(status)) {
throw Exception("Unable to get ICU calendar time.");
}
// UDate is a double, so it can't overflow (it just loses accuracy), but converting back to µs can.
millis = MultiplyOperatorOverflowCheck::Operation<int64_t, int64_t, int64_t>(millis, Interval::MICROS_PER_MSEC);
millis = AddOperatorOverflowCheck::Operation<int64_t, int64_t, int64_t>(millis, micros);
// Now make sure the value is in range
date_t d;
dtime_t t;
Timestamp::Convert(timestamp_t(millis), d, t);
return timestamp_t(millis);
}
uint64_t ICUDateFunc::SetTime(icu::Calendar *calendar, timestamp_t date) {
int64_t millis = date.value / Interval::MICROS_PER_MSEC;
int64_t micros = date.value % Interval::MICROS_PER_MSEC;
if (micros < 0) {
--millis;
micros += Interval::MICROS_PER_MSEC;
}
const auto udate = UDate(millis);
UErrorCode status = U_ZERO_ERROR;
calendar->setTime(udate, status);
if (U_FAILURE(status)) {
throw Exception("Unable to set ICU calendar time.");
}
return uint64_t(micros);
}
int32_t ICUDateFunc::ExtractField(icu::Calendar *calendar, UCalendarDateFields field) {
UErrorCode status = U_ZERO_ERROR;
const auto result = calendar->get(field, status);
if (U_FAILURE(status)) {
throw Exception("Unable to extract ICU calendar part.");
}
return result;
}
int64_t ICUDateFunc::SubtractField(icu::Calendar *calendar, UCalendarDateFields field, timestamp_t end_date) {
const int64_t millis = end_date.value / Interval::MICROS_PER_MSEC;
const auto when = UDate(millis);
UErrorCode status = U_ZERO_ERROR;
auto sub = calendar->fieldDifference(when, field, status);
if (U_FAILURE(status)) {
throw Exception("Unable to subtract ICU calendar part.");
}
return sub;
}
} // namespace duckdb