| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| #include "vec/runtime/vdatetime_value.h" |
| |
| #include <cctz/civil_time.h> |
| #include <cctz/time_zone.h> |
| #include <glog/logging.h> |
| |
| #include <cctype> |
| #include <cstddef> |
| #include <cstdlib> |
| #include <cstring> |
| #include <ctime> |
| // IWYU pragma: no_include <bits/chrono.h> |
| #include <algorithm> |
| #include <chrono> // IWYU pragma: keep |
| // IWYU pragma: no_include <bits/std_abs.h> |
| #include <cmath> |
| #include <cstdint> |
| #include <string_view> |
| |
| #include "common/compiler_util.h" |
| #include "common/config.h" |
| #include "common/exception.h" |
| #include "common/status.h" |
| #include "util/timezone_utils.h" |
| #include "vec/common/int_exp.h" |
| |
| namespace doris { |
| |
| static const char* s_ab_month_name[] = {"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
| "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", nullptr}; |
| |
| static const char* s_ab_day_name[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", nullptr}; |
| |
| uint8_t mysql_week_mode(uint32_t mode) { |
| mode &= 7; |
| if (!(mode & WEEK_MONDAY_FIRST)) { |
| mode ^= WEEK_FIRST_WEEKDAY; |
| } |
| return mode; |
| } |
| |
| static bool check_space(char ch) { |
| // \t, \n, \v, \f, \r are 9~13, respectively. |
| return UNLIKELY(ch == ' ' || (ch >= 9 && ch <= 13)); |
| } |
| |
| static bool check_date_punct(char ch) { |
| return UNLIKELY(!(isdigit(ch) || isalpha(ch))); |
| } |
| |
| static bool time_zone_begins(const char* ptr, const char* end) { |
| return *ptr == '+' || (*ptr == '-' && ptr + 3 < end && *(ptr + 3) == ':') || |
| (isalpha(*ptr) && *ptr != 'T'); |
| } |
| |
| bool VecDateTimeValue::check_range(uint32_t year, uint32_t month, uint32_t day, uint32_t hour, |
| uint32_t minute, uint32_t second, uint16_t type) { |
| bool time = hour > (type == TIME_TIME ? TIME_MAX_HOUR : 23) || minute > 59 || second > 59; |
| if (type == TIME_TIME) { |
| return time; |
| } else { |
| return time || check_date(year, month, day); |
| } |
| } |
| |
| bool VecDateTimeValue::check_date(uint32_t year, uint32_t month, uint32_t day) { |
| if (month == 2 && day == 29 && doris::is_leap(year)) { |
| return false; |
| } |
| if (year > 9999 || month == 0 || month > 12 || day > S_DAYS_IN_MONTH[month] || day == 0) { |
| return true; |
| } |
| return false; |
| } |
| |
| // The interval format is that with no delimiters |
| // YYYY-MM-DD HH-MM-DD.FFFFFF AM in default format |
| // 0 1 2 3 4 5 6 7 |
| bool VecDateTimeValue::from_date_str(const char* date_str, size_t len) { |
| return from_date_str_base(date_str, len, nullptr); |
| } |
| //parse timezone to get offset |
| bool VecDateTimeValue::from_date_str(const char* date_str, size_t len, |
| const cctz::time_zone& local_time_zone) { |
| return from_date_str_base(date_str, len, &local_time_zone); |
| } |
| |
| bool VecDateTimeValue::from_date_str_base(const char* date_str, int len, |
| const cctz::time_zone* local_time_zone) { |
| const char* ptr = date_str; |
| const char* end = date_str + len; |
| // ONLY 2, 6 can follow by a space |
| const static int allow_space_mask = 4 | 64; |
| const static int MAX_DATE_PARTS = 8; |
| uint32_t date_val[MAX_DATE_PARTS]; |
| int32_t date_len[MAX_DATE_PARTS]; |
| |
| _neg = false; |
| // Skip space character |
| while (ptr < end && check_space(*ptr)) { |
| ptr++; |
| } |
| if (ptr == end || !isdigit(*ptr)) { |
| return false; |
| } |
| // Fix year length |
| const char* pos = ptr; |
| while (pos < end && (isdigit(*pos) || *pos == 'T')) { |
| pos++; |
| } |
| int year_len = 4; |
| int digits = pos - ptr; |
| bool is_interval_format = false; |
| bool has_bar = false; |
| |
| // Compatible with MySQL. |
| // For YYYYMMDD/YYYYMMDDHHMMSS is 4 digits years |
| if (pos == end || *pos == '.') { |
| if (digits == 4 || digits == 8 || digits >= 14) { |
| year_len = 4; |
| } else { |
| year_len = 2; |
| } |
| is_interval_format = true; |
| } |
| |
| int field_idx = 0; |
| int field_len = year_len; |
| long sec_offset = 0; |
| while (ptr < end && isdigit(*ptr) && field_idx < MAX_DATE_PARTS - 1) { |
| const char* start = ptr; |
| int temp_val = 0; |
| bool scan_to_delim = (!is_interval_format) && (field_idx != 6); |
| while (ptr < end && isdigit(*ptr) && (scan_to_delim || field_len--)) { |
| temp_val = temp_val * 10 + (*ptr++ - '0'); |
| } |
| // Impossible |
| if (temp_val > 999999L) { |
| return false; |
| } |
| date_val[field_idx] = temp_val; |
| date_len[field_idx] = ptr - start; |
| field_len = 2; |
| |
| if (ptr == end) { |
| field_idx++; |
| break; |
| } |
| |
| // timezone |
| if (UNLIKELY((field_idx > 2 || |
| !has_bar) /*dont treat xxxx-xx-xx:xx:xx as xxxx-xx(-xx:xx:xx)*/ |
| && time_zone_begins(ptr, end))) { |
| if (local_time_zone == nullptr) { |
| return false; |
| } |
| auto get_tz_offset = [&](const std::string& str_tz, |
| const cctz::time_zone* local_time_zone) -> long { |
| cctz::time_zone given_tz {}; |
| if (!TimezoneUtils::find_cctz_time_zone(str_tz, given_tz)) { |
| throw Exception {ErrorCode::INVALID_ARGUMENT, ""}; |
| } |
| auto given = cctz::convert(cctz::civil_second {}, given_tz); |
| auto local = cctz::convert(cctz::civil_second {}, *local_time_zone); |
| // these two values is absolute time. so they are negative. need to use (-local) - (-given) |
| return std::chrono::duration_cast<std::chrono::seconds>(given - local).count(); |
| }; |
| try { |
| sec_offset = get_tz_offset(std::string {ptr, end}, |
| local_time_zone); // use the whole remain string |
| } catch ([[maybe_unused]] Exception& e) { |
| return false; // invalid format |
| } |
| field_idx++; |
| break; |
| } |
| |
| if (field_idx == 2 && *ptr == 'T') { |
| // YYYYMMDDTHHMMDD, skip 'T' and continue |
| ptr++; |
| field_idx++; |
| continue; |
| } |
| |
| // Second part |
| if (field_idx == 5) { |
| if (*ptr == '.') { |
| ptr++; |
| field_len = 6; |
| } else if (isdigit(*ptr)) { |
| field_idx++; |
| break; |
| } |
| field_idx++; |
| continue; |
| } |
| // escape separator |
| while (ptr < end && (check_date_punct(*ptr) || check_space(*ptr))) { |
| if (check_space(*ptr)) { |
| if (((1 << field_idx) & allow_space_mask) == 0) { |
| return false; |
| } |
| } |
| if (*ptr == '-') { |
| has_bar = true; |
| } |
| ptr++; |
| } |
| field_idx++; |
| } |
| int num_field = field_idx; |
| if (num_field <= 3) { |
| _type = TIME_DATE; |
| } else { |
| _type = TIME_DATETIME; |
| } |
| if (!is_interval_format) { |
| year_len = date_len[0]; |
| } |
| for (; field_idx < MAX_DATE_PARTS; ++field_idx) { |
| date_len[field_idx] = 0; |
| date_val[field_idx] = 0; |
| } |
| |
| if (year_len == 2) { |
| if (date_val[0] < YY_PART_YEAR) { |
| date_val[0] += 2000; |
| } else { |
| date_val[0] += 1900; |
| } |
| } |
| |
| if (num_field < 3) { |
| return false; |
| } |
| if (!check_range_and_set_time(date_val[0], date_val[1], date_val[2], date_val[3], date_val[4], |
| date_val[5], _type)) { |
| return false; |
| } |
| return sec_offset ? date_add_interval<TimeUnit::SECOND>( |
| TimeInterval {TimeUnit::SECOND, sec_offset, false}) |
| : true; |
| } |
| |
| // [0, 101) invalid |
| // [101, (YY_PART_YEAR - 1) * 10000 + 1231] for two digits year 2000 ~ 2069 |
| // [(YY_PART_YEAR - 1) * 10000 + 1231, YY_PART_YEAR * 10000L + 101) invalid |
| // [YY_PART_YEAR * 10000L + 101, 991231] for two digits year 1970 ~1999 |
| // (991231, 10000101) invalid, because support 1000-01-01 |
| // [10000101, 99991231] for four digits year date value. |
| // (99991231, 101000000) invalid, NOTE below this is datetime vale hh:mm:ss must exist. |
| // [101000000, (YY_PART_YEAR - 1)##1231235959] two digits year datetime value |
| // ((YY_PART_YEAR - 1)##1231235959, YY_PART_YEAR##0101000000) invalid |
| // ((YY_PART_YEAR)##1231235959, 99991231235959] two digits year datetime value 1970 ~ 1999 |
| // (999991231235959, ~) valid |
| int64_t VecDateTimeValue::standardize_timevalue(int64_t value) { |
| _type = TIME_DATE; |
| if (value <= 0) { |
| return 0; |
| } |
| if (value >= 10000101000000L) { |
| // 9999-99-99 99:99:99 |
| if (value > 99999999999999L) { |
| return 0; |
| } |
| |
| // between 1000-01-01 00:00:00L and 9999-99-99 99:99:99 |
| // all digits exist. |
| _type = TIME_DATETIME; |
| return value; |
| } |
| // 2000-01-01 |
| if (value < 101) { |
| return 0; |
| } |
| // two digits year. 2000 ~ 2069 |
| if (value <= (YY_PART_YEAR - 1) * 10000L + 1231L) { |
| return (value + 20000000L) * 1000000L; |
| } |
| // two digits year, invalid date |
| if (value < YY_PART_YEAR * 10000L + 101) { |
| return 0; |
| } |
| // two digits year. 1970 ~ 1999 |
| if (value <= 991231L) { |
| return (value + 19000000L) * 1000000L; |
| } |
| // TODO(zhaochun): Don't allow year betwen 1000-01-01 |
| if (value < 10000101) { |
| return 0; |
| } |
| // four digits years without hour. |
| if (value <= 99991231L) { |
| return value * 1000000L; |
| } |
| // below 0000-01-01 |
| if (value < 101000000) { |
| return 0; |
| } |
| |
| // below is with datetime, must have hh:mm:ss |
| _type = TIME_DATETIME; |
| // 2000 ~ 2069 |
| if (value <= (YY_PART_YEAR - 1) * 10000000000L + 1231235959L) { |
| return value + 20000000000000L; |
| } |
| if (value < YY_PART_YEAR * 10000000000L + 101000000L) { |
| return 0; |
| } |
| // 1970 ~ 1999 |
| if (value <= 991231235959L) { |
| return value + 19000000000000L; |
| } |
| return value; |
| } |
| |
| bool VecDateTimeValue::from_date_int64(int64_t value) { |
| _neg = false; |
| value = standardize_timevalue(value); |
| if (value <= 0) { |
| return false; |
| } |
| uint64_t date = value / 1000000; |
| uint64_t time = value % 1000000; |
| |
| auto [year, month, day, hour, minute, second] = std::tuple {0, 0, 0, 0, 0, 0}; |
| year = date / 10000; |
| date %= 10000; |
| month = date / 100; |
| day = date % 100; |
| hour = time / 10000; |
| time %= 10000; |
| minute = time / 100; |
| second = time % 100; |
| |
| return check_range_and_set_time(year, month, day, hour, minute, second, _type); |
| } |
| |
| void VecDateTimeValue::set_zero(int type) { |
| memset(this, 0, sizeof(*this)); |
| _type = type; |
| } |
| |
| void VecDateTimeValue::set_type(int type) { |
| _type = type; |
| if (type == TIME_DATE) { |
| _hour = 0; |
| _minute = 0; |
| _second = 0; |
| } |
| } |
| |
| void VecDateTimeValue::set_max_time(bool neg) { |
| set_zero(TIME_TIME); |
| _hour = static_cast<uint8_t>(TIME_MAX_HOUR); |
| _minute = TIME_MAX_MINUTE; |
| _second = TIME_MAX_SECOND; |
| _neg = neg; |
| } |
| |
| bool VecDateTimeValue::from_time_int64(int64_t value) { |
| _type = TIME_TIME; |
| if (value > TIME_MAX_VALUE) { |
| // 0001-01-01 00:00:00 to convert to a datetime |
| if (value > 10000000000L) { |
| if (from_date_int64(value)) { |
| return true; |
| } |
| } |
| set_max_time(false); |
| return false; |
| } else if (value < -1 * TIME_MAX_VALUE) { |
| set_max_time(true); |
| return false; |
| } |
| if (value < 0) { |
| _neg = 1; |
| value = -value; |
| } |
| _hour = value / 10000; |
| value %= 10000; |
| _minute = value / 100; |
| if (_minute > TIME_MAX_MINUTE) { |
| return false; |
| } |
| _second = value % 100; |
| return _second <= TIME_MAX_SECOND; |
| } |
| |
| char* VecDateTimeValue::append_date_buffer(char* to) const { |
| uint32_t temp; |
| // Year |
| temp = _year / 100; |
| *to++ = (char)('0' + (temp / 10)); |
| *to++ = (char)('0' + (temp % 10)); |
| temp = _year % 100; |
| *to++ = (char)('0' + (temp / 10)); |
| *to++ = (char)('0' + (temp % 10)); |
| *to++ = '-'; |
| // Month |
| *to++ = (char)('0' + (_month / 10)); |
| *to++ = (char)('0' + (_month % 10)); |
| *to++ = '-'; |
| // Day |
| *to++ = (char)('0' + (_day / 10)); |
| *to++ = (char)('0' + (_day % 10)); |
| return to; |
| } |
| |
| char* VecDateTimeValue::append_time_buffer(char* to) const { |
| if (_neg) { |
| *to++ = '-'; |
| } |
| // Hour |
| uint32_t temp = _hour; |
| if (temp >= 100) { |
| *to++ = (char)('0' + (temp / 100)); |
| temp %= 100; |
| } |
| *to++ = (char)('0' + (temp / 10)); |
| *to++ = (char)('0' + (temp % 10)); |
| *to++ = ':'; |
| // Minute |
| *to++ = (char)('0' + (_minute / 10)); |
| *to++ = (char)('0' + (_minute % 10)); |
| *to++ = ':'; |
| /* Second */ |
| *to++ = (char)('0' + (_second / 10)); |
| *to++ = (char)('0' + (_second % 10)); |
| return to; |
| } |
| |
| char* VecDateTimeValue::to_datetime_buffer(char* to) const { |
| to = append_date_buffer(to); |
| *to++ = ' '; |
| return append_time_buffer(to); |
| } |
| |
| char* VecDateTimeValue::to_date_buffer(char* to) const { |
| return append_date_buffer(to); |
| } |
| |
| char* VecDateTimeValue::to_time_buffer(char* to) const { |
| return append_time_buffer(to); |
| } |
| |
| int32_t VecDateTimeValue::to_buffer(char* buffer) const { |
| switch (_type) { |
| case TIME_TIME: |
| return to_time_buffer(buffer) - buffer; |
| case TIME_DATE: |
| return to_date_buffer(buffer) - buffer; |
| case TIME_DATETIME: |
| return to_datetime_buffer(buffer) - buffer; |
| default: |
| break; |
| } |
| return 0; |
| } |
| |
| char* VecDateTimeValue::to_string(char* to) const { |
| int len = to_buffer(to); |
| *(to + len) = '\0'; |
| return to + len + 1; |
| } |
| |
| int64_t VecDateTimeValue::to_datetime_int64() const { |
| return (_year * 10000L + _month * 100 + _day) * 1000000L + _hour * 10000 + _minute * 100 + |
| _second; |
| } |
| |
| int64_t VecDateTimeValue::to_date_int64() const { |
| return _year * 10000 + _month * 100 + _day; |
| } |
| |
| int64_t VecDateTimeValue::to_time_int64() const { |
| int sign = _neg == 0 ? 1 : -1; |
| return sign * (_hour * 10000 + _minute * 100 + _second); |
| } |
| |
| int64_t VecDateTimeValue::to_int64() const { |
| switch (_type) { |
| case TIME_TIME: |
| return to_time_int64(); |
| case TIME_DATE: |
| return to_date_int64(); |
| case TIME_DATETIME: |
| return to_datetime_int64(); |
| default: |
| return 0; |
| } |
| } |
| |
| bool VecDateTimeValue::get_date_from_daynr(uint64_t daynr) { |
| if (daynr <= 0 || daynr > DATE_MAX_DAYNR) { |
| return false; |
| } |
| |
| auto [year, month, day] = std::tuple {0, 0, 0}; |
| year = daynr / 365; |
| uint32_t days_befor_year = 0; |
| while (daynr < (days_befor_year = doris::calc_daynr(year, 1, 1))) { |
| year--; |
| } |
| uint32_t days_of_year = daynr - days_befor_year + 1; |
| int leap_day = 0; |
| if (doris::is_leap(year)) { |
| if (days_of_year > 31 + 28) { |
| days_of_year--; |
| if (days_of_year == 31 + 28) { |
| leap_day = 1; |
| } |
| } |
| } |
| month = 1; |
| while (days_of_year > S_DAYS_IN_MONTH[month]) { |
| days_of_year -= S_DAYS_IN_MONTH[month]; |
| month++; |
| } |
| day = days_of_year + leap_day; |
| |
| if (check_range(year, month, day, 0, 0, 0, _type)) { |
| return false; |
| } |
| unchecked_set_time(year, month, day, _hour, _minute, _second); |
| return true; |
| } |
| |
| bool VecDateTimeValue::from_date_daynr(uint64_t daynr) { |
| _neg = false; |
| if (!get_date_from_daynr(daynr)) { |
| return false; |
| } |
| _hour = 0; |
| _minute = 0; |
| _second = 0; |
| _type = TIME_DATE; |
| return true; |
| } |
| |
| /// @return: tail |
| static char* int_to_str(uint64_t val, char* to) { |
| char buf[64]; |
| char* ptr = buf; |
| // Use do/while for 0 value |
| do { |
| *ptr++ = '0' + (val % 10); |
| val /= 10; |
| } while (val); |
| |
| while (ptr > buf) { |
| *to++ = *--ptr; |
| } |
| return to; |
| } |
| |
| static char* append_string(const char* from, char* to) { |
| while (*from) { |
| *to++ = *from++; |
| } |
| return to; |
| } |
| |
| static char* append_with_prefix(const char* str, int str_len, char prefix, int target_len, |
| char* to) { |
| // full_len is the lower bound. if less, use prefix to pad. if greater, accept all. |
| int diff = target_len - str_len; |
| // use prefix to pad |
| while (diff-- > 0) { // won't be INT_MIN. it's ok |
| *to++ = prefix; |
| } |
| |
| memcpy(to, str, str_len); |
| return to + str_len; |
| } |
| |
| int VecDateTimeValue::compute_format_len(const char* format, size_t len) { |
| int size = 0; |
| const char* ptr = format; |
| const char* end = format + len; |
| |
| while (ptr < end) { |
| if (*ptr != '%' || (ptr + 1) < end) { |
| size++; |
| ptr++; |
| continue; |
| } |
| switch (*++ptr) { |
| case 'M': |
| case 'W': |
| size += 10; |
| break; |
| case 'D': |
| case 'Y': |
| case 'x': |
| case 'X': |
| size += 4; |
| break; |
| case 'a': |
| case 'b': |
| size += 10; |
| break; |
| case 'j': |
| size += 3; |
| break; |
| case 'u': |
| case 'U': |
| case 'v': |
| case 'V': |
| case 'y': |
| case 'm': |
| case 'd': |
| case 'h': |
| case 'i': |
| case 'I': |
| case 'l': |
| case 'p': |
| case 'S': |
| case 's': |
| case 'c': |
| case 'e': |
| size += 2; |
| break; |
| case 'k': |
| case 'H': |
| size += 7; |
| break; |
| case 'r': |
| size += 11; |
| break; |
| case 'T': |
| size += 8; |
| break; |
| case 'f': |
| size += 6; |
| break; |
| case 'w': |
| case '%': |
| default: |
| size++; |
| break; |
| } |
| } |
| return size; |
| } |
| |
| static const char digits100[201] = |
| "00010203040506070809" |
| "10111213141516171819" |
| "20212223242526272829" |
| "30313233343536373839" |
| "40414243444546474849" |
| "50515253545556575859" |
| "60616263646566676869" |
| "70717273747576777879" |
| "80818283848586878889" |
| "90919293949596979899"; |
| |
| char* write_two_digits_to_string(int number, char* dst) { |
| memcpy(dst, &digits100[number * 2], 2); |
| return dst + 2; |
| } |
| |
| char* write_four_digits_to_string(int number, char* dst) { |
| memcpy(dst, &digits100[(number / 100) * 2], 2); |
| memcpy(dst + 2, &digits100[(number % 100) * 2], 2); |
| return dst + 4; |
| } |
| |
| bool VecDateTimeValue::to_format_string_conservative(const char* format, size_t len, char* to, |
| size_t max_valid_length) const { |
| if (check_range(_year, _month, _day, _hour, _minute, _second, _type)) { |
| return false; |
| } |
| char* const begin = to; // to check written bytes |
| char buf[64]; |
| char* cursor = buf; |
| char* pos = nullptr; |
| const char* ptr = format; |
| const char* end = format + len; |
| char ch = '\0'; |
| |
| while (ptr < end) { |
| if (to - begin + SAFE_FORMAT_STRING_MARGIN > max_valid_length) [[unlikely]] { |
| return false; |
| } |
| if (*ptr != '%' || (ptr + 1) == end) { |
| *to++ = *ptr++; |
| continue; |
| } |
| // Skip '%' |
| ptr++; |
| switch (ch = *ptr++) { |
| case 'y': |
| // Year, numeric (two digits) |
| to = write_two_digits_to_string(_year % 100, to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'Y': |
| // Year, numeric, four digits |
| to = write_four_digits_to_string(_year, to); |
| cursor += 4; |
| pos = cursor; |
| break; |
| case 'd': |
| // Day of month (00...31) |
| to = write_two_digits_to_string(_day, to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'H': |
| to = write_two_digits_to_string(_hour, to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'i': |
| // Minutes, numeric (00..59) |
| to = write_two_digits_to_string(_minute, to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'm': |
| to = write_two_digits_to_string(_month, to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'h': |
| case 'I': |
| // Hour (01..12) |
| to = write_two_digits_to_string((_hour % 24 + 11) % 12 + 1, to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 's': |
| case 'S': |
| // Seconds (00..59) |
| to = write_two_digits_to_string(_second, to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'a': |
| // Abbreviated weekday name |
| if (_type == TIME_TIME || (_year == 0 && _month == 0)) { |
| return false; |
| } |
| to = append_string(s_ab_day_name[weekday()], to); |
| break; |
| case 'b': |
| // Abbreviated month name |
| if (_month == 0) { |
| return false; |
| } |
| to = append_string(s_ab_month_name[_month], to); |
| break; |
| case 'c': |
| // Month, numeric (0...12) |
| pos = int_to_str(_month, cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 1, to); |
| break; |
| case 'D': |
| // Day of the month with English suffix (0th, 1st, ...) |
| pos = int_to_str(_day, cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 1, to); |
| if (_day >= 10 && _day <= 19) { |
| to = append_string("th", to); |
| } else { |
| switch (_day % 10) { |
| case 1: |
| to = append_string("st", to); |
| break; |
| case 2: |
| to = append_string("nd", to); |
| break; |
| case 3: |
| to = append_string("rd", to); |
| break; |
| default: |
| to = append_string("th", to); |
| break; |
| } |
| } |
| break; |
| case 'e': |
| // Day of the month, numeric (0..31) |
| pos = int_to_str(_day, cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 1, to); |
| break; |
| case 'f': |
| // Microseconds (000000..999999) |
| pos = int_to_str(0, cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 6, to); |
| break; |
| case 'j': |
| // Day of year (001..366) |
| pos = int_to_str(daynr() - doris::calc_daynr(_year, 1, 1) + 1, cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 3, to); |
| break; |
| case 'k': |
| // Hour (0..23) |
| pos = int_to_str(_hour, cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 1, to); |
| break; |
| case 'l': |
| // Hour (1..12) |
| pos = int_to_str((_hour % 24 + 11) % 12 + 1, cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 1, to); |
| break; |
| case 'M': |
| // Month name (January..December) |
| if (_month == 0) { |
| return false; |
| } |
| to = append_string(s_month_name[_month], to); |
| break; |
| case 'p': |
| // AM or PM |
| if ((_hour % 24) >= 12) { |
| to = append_string("PM", to); |
| } else { |
| to = append_string("AM", to); |
| } |
| break; |
| case 'r': |
| // Time, 12-hour (hh:mm:ss followed by AM or PM) |
| *to++ = (char)('0' + (((_hour + 11) % 12 + 1) / 10)); |
| *to++ = (char)('0' + (((_hour + 11) % 12 + 1) % 10)); |
| *to++ = ':'; |
| // Minute |
| *to++ = (char)('0' + (_minute / 10)); |
| *to++ = (char)('0' + (_minute % 10)); |
| *to++ = ':'; |
| /* Second */ |
| *to++ = (char)('0' + (_second / 10)); |
| *to++ = (char)('0' + (_second % 10)); |
| if ((_hour % 24) >= 12) { |
| to = append_string(" PM", to); |
| } else { |
| to = append_string(" AM", to); |
| } |
| break; |
| case 'T': |
| // Time, 24-hour (hh:mm:ss) |
| *to++ = (char)('0' + ((_hour % 24) / 10)); |
| *to++ = (char)('0' + ((_hour % 24) % 10)); |
| *to++ = ':'; |
| // Minute |
| *to++ = (char)('0' + (_minute / 10)); |
| *to++ = (char)('0' + (_minute % 10)); |
| *to++ = ':'; |
| /* Second */ |
| *to++ = (char)('0' + (_second / 10)); |
| *to++ = (char)('0' + (_second % 10)); |
| break; |
| case 'u': |
| // Week (00..53), where Monday is the first day of the week; |
| // WEEK() mode 1 |
| if (_type == TIME_TIME) { |
| return false; |
| } |
| to = write_two_digits_to_string(week(mysql_week_mode(1)), to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'U': |
| // Week (00..53), where Sunday is the first day of the week; |
| // WEEK() mode 0 |
| if (_type == TIME_TIME) { |
| return false; |
| } |
| to = write_two_digits_to_string(week(mysql_week_mode(0)), to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'v': |
| // Week (01..53), where Monday is the first day of the week; |
| // WEEK() mode 3; used with %x |
| if (_type == TIME_TIME) { |
| return false; |
| } |
| to = write_two_digits_to_string(week(mysql_week_mode(3)), to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'V': |
| // Week (01..53), where Sunday is the first day of the week; |
| // WEEK() mode 2; used with %X |
| if (_type == TIME_TIME) { |
| return false; |
| } |
| to = write_two_digits_to_string(week(mysql_week_mode(2)), to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'w': |
| // Day of the week (0=Sunday..6=Saturday) |
| if (_type == TIME_TIME || (_month == 0 && _year == 0)) { |
| return false; |
| } |
| pos = int_to_str(doris::calc_weekday(daynr(), true), cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 1, to); |
| break; |
| case 'W': |
| // Weekday name (Sunday..Saturday) |
| to = append_string(s_day_name[weekday()], to); |
| break; |
| case 'x': { |
| // Year for the week, where Monday is the first day of the week, |
| // numeric, four digits; used with %v |
| if (_type == TIME_TIME) { |
| return false; |
| } |
| uint32_t year = 0; |
| calc_week(*this, mysql_week_mode(3), &year, true); |
| to = write_four_digits_to_string(year, to); |
| cursor += 4; |
| pos = cursor; |
| break; |
| } |
| case 'X': { |
| // Year for the week where Sunday is the first day of the week, |
| // numeric, four digits; used with %V |
| if (_type == TIME_TIME) { |
| return false; |
| } |
| uint32_t year = 0; |
| calc_week(*this, mysql_week_mode(2), &year); |
| to = write_four_digits_to_string(year, to); |
| cursor += 4; |
| pos = cursor; |
| break; |
| } |
| default: |
| // put it literal |
| *to++ = ch; |
| break; |
| } |
| } |
| *to++ = '\0'; |
| return true; |
| } |
| |
| uint8_t VecDateTimeValue::calc_week(const VecDateTimeValue& value, uint8_t mode, uint32_t* year, |
| bool disable_lut) { |
| // mode=3 is used for week_of_year() |
| if (config::enable_time_lut && !disable_lut && mode == 3 && value._year >= 1950 && |
| value._year < 2030) { |
| return doris::TimeLUT::GetImplement() |
| ->week_of_year_table[value._year - doris::LUT_START_YEAR][value._month - 1] |
| [value._day - 1]; |
| } |
| // mode=4 is used for week() |
| if (config::enable_time_lut && !disable_lut && mode == 4 && value._year >= 1950 && |
| value._year < 2030) { |
| return doris::TimeLUT::GetImplement() |
| ->week_table[value._year - doris::LUT_START_YEAR][value._month - 1][value._day - 1]; |
| } |
| // not covered by pre calculated dates, calculate at runtime |
| bool monday_first = mode & WEEK_MONDAY_FIRST; |
| bool week_year = mode & WEEK_YEAR; |
| bool first_weekday = mode & WEEK_FIRST_WEEKDAY; |
| uint64_t day_nr = value.daynr(); |
| uint64_t daynr_first_day = doris::calc_daynr(value._year, 1, 1); |
| uint8_t weekday_first_day = doris::calc_weekday(daynr_first_day, !monday_first); |
| |
| int days = 0; |
| *year = value._year; |
| |
| // Check weather the first days of this year belongs to last year |
| if (value._month == 1 && value._day <= (7 - weekday_first_day)) { |
| if (!week_year && ((first_weekday && weekday_first_day != 0) || |
| (!first_weekday && weekday_first_day > 3))) { |
| return 0; |
| } |
| (*year)--; |
| week_year = true; |
| daynr_first_day -= (days = doris::calc_days_in_year(*year)); |
| weekday_first_day = (weekday_first_day + 53 * 7 - days) % 7; |
| } |
| |
| // How many days since first week |
| if ((first_weekday && weekday_first_day != 0) || (!first_weekday && weekday_first_day > 3)) { |
| // days in new year belongs to last year. |
| days = day_nr - (daynr_first_day + (7 - weekday_first_day)); |
| } else { |
| // days in new year belongs to this year. |
| days = day_nr - (daynr_first_day - weekday_first_day); |
| } |
| |
| if (week_year && days >= 52 * 7) { |
| weekday_first_day = (weekday_first_day + doris::calc_days_in_year(*year)) % 7; |
| if ((first_weekday && weekday_first_day == 0) || |
| (!first_weekday && weekday_first_day <= 3)) { |
| // Belong to next year. |
| (*year)++; |
| return 1; |
| } |
| } |
| |
| return days / 7 + 1; |
| } |
| |
| uint8_t VecDateTimeValue::week(uint8_t mode) const { |
| uint32_t year = 0; |
| return calc_week(*this, mode, &year); |
| } |
| |
| uint32_t VecDateTimeValue::year_week(uint8_t mode) const { |
| // mode=4 is used for yearweek() |
| if (config::enable_time_lut && mode == 4 && _year >= 1950 && _year < 2030) { |
| return doris::TimeLUT::GetImplement()->year_week_table[_year - 1950][_month - 1][_day - 1]; |
| } |
| |
| // not covered by year_week_table, calculate at runtime |
| uint32_t year = 0; |
| // The range of the week in the year_week is 1-53, so the mode WEEK_YEAR is always true. |
| uint8_t week = calc_week(*this, mode | 2, &year, true); |
| // When the mode WEEK_FIRST_WEEKDAY is not set, |
| // the week in which the last three days of the year fall may belong to the following year. |
| if (week == 53 && day() >= 29 && !(mode & 4)) { |
| uint8_t monday_first = mode & WEEK_MONDAY_FIRST; |
| uint64_t daynr_of_last_day = doris::calc_daynr(_year, 12, 31); |
| uint8_t weekday_of_last_day = doris::calc_weekday(daynr_of_last_day, !monday_first); |
| |
| if (weekday_of_last_day - monday_first < 2) { |
| ++year; |
| week = 1; |
| } |
| } |
| return year * 100 + week; |
| } |
| |
| template <typename T> |
| bool VecDateTimeValue::operator>=(const DateV2Value<T>& other) const { |
| int64_t ts1; |
| int64_t ts2; |
| this->unix_timestamp(&ts1, TimezoneUtils::default_time_zone); |
| other.unix_timestamp(&ts2, TimezoneUtils::default_time_zone); |
| return ts1 >= ts2; |
| } |
| |
| template <typename T> |
| bool VecDateTimeValue::operator<=(const DateV2Value<T>& other) const { |
| int64_t ts1; |
| int64_t ts2; |
| this->unix_timestamp(&ts1, TimezoneUtils::default_time_zone); |
| other.unix_timestamp(&ts2, TimezoneUtils::default_time_zone); |
| return ts1 <= ts2; |
| } |
| |
| template <typename T> |
| bool VecDateTimeValue::operator>(const DateV2Value<T>& other) const { |
| int64_t ts1; |
| int64_t ts2; |
| this->unix_timestamp(&ts1, TimezoneUtils::default_time_zone); |
| other.unix_timestamp(&ts2, TimezoneUtils::default_time_zone); |
| return ts1 > ts2; |
| } |
| |
| template <typename T> |
| bool VecDateTimeValue::operator<(const DateV2Value<T>& other) const { |
| int64_t ts1; |
| int64_t ts2; |
| this->unix_timestamp(&ts1, TimezoneUtils::default_time_zone); |
| other.unix_timestamp(&ts2, TimezoneUtils::default_time_zone); |
| return ts1 < ts2; |
| } |
| |
| template <typename T> |
| bool VecDateTimeValue::operator==(const DateV2Value<T>& other) const { |
| int64_t ts1; |
| int64_t ts2; |
| this->unix_timestamp(&ts1, TimezoneUtils::default_time_zone); |
| other.unix_timestamp(&ts2, TimezoneUtils::default_time_zone); |
| return ts1 == ts2; |
| } |
| |
| // TODO(zhaochun): Think endptr is NULL |
| // Return true if convert to a integer success. Otherwise false. |
| static bool str_to_int64(const char* ptr, const char** endptr, int64_t* ret) { |
| const static uint64_t MAX_NEGATIVE_NUMBER = 0x8000000000000000; |
| const static uint64_t ULONGLONG_MAX = ~0; |
| const static uint64_t LFACTOR2 = 100000000000ULL; |
| const char* end = *endptr; |
| uint64_t cutoff_1 = 0; |
| uint64_t cutoff_2 = 0; |
| uint64_t cutoff_3 = 0; |
| // Skip space |
| while (ptr < end && (*ptr == ' ' || *ptr == '\t')) { |
| ptr++; |
| } |
| if (ptr >= end) { |
| return false; |
| } |
| // Sign |
| bool neg = false; |
| if (*ptr == '-') { |
| neg = true; |
| ptr++; |
| cutoff_1 = MAX_NEGATIVE_NUMBER / LFACTOR2; |
| cutoff_2 = (MAX_NEGATIVE_NUMBER % LFACTOR2) / 100; |
| cutoff_3 = (MAX_NEGATIVE_NUMBER % LFACTOR2) % 100; |
| } else { |
| if (*ptr == '+') { |
| ptr++; |
| } |
| cutoff_1 = ULONGLONG_MAX / LFACTOR2; |
| cutoff_2 = (ULONGLONG_MAX % LFACTOR2) / 100; |
| cutoff_3 = (ULONGLONG_MAX % LFACTOR2) % 100; |
| } |
| if (ptr >= end) { |
| return false; |
| } |
| // a valid input should at least contains one digit |
| if (!isdigit(*ptr)) { |
| return false; |
| } |
| // Skip '0' |
| while (ptr < end && *ptr == '0') { |
| ptr++; |
| } |
| const char* n_end = ptr + 9; |
| if (n_end > end) { |
| n_end = end; |
| } |
| uint64_t value_1 = 0; |
| while (ptr < n_end && isdigit(*ptr)) { |
| value_1 *= 10; |
| value_1 += *ptr++ - '0'; |
| } |
| if (ptr == end || !isdigit(*ptr)) { |
| *endptr = ptr; |
| *ret = neg ? -value_1 : value_1; |
| return true; |
| } |
| // TODO |
| uint64_t value_2 = 0; |
| uint64_t value_3 = 0; |
| |
| // Check overflow. |
| return value_1 <= cutoff_1 && |
| (value_1 != cutoff_1 || |
| (value_2 <= cutoff_2 && (value_2 != cutoff_2 || value_3 <= cutoff_3))); |
| } |
| |
| static int min(int a, int b) { |
| return a < b ? a : b; |
| } |
| |
| static int find_in_lib(const char* lib[], const char* str, const char* end) { |
| int pos = 0; |
| int find_count = 0; |
| int find_pos = 0; |
| for (; lib[pos] != nullptr; ++pos) { |
| const char* i = str; |
| const char* j = lib[pos]; |
| while (i < end && *j) { |
| if (toupper(*i) != toupper(*j)) { |
| break; |
| } |
| ++i; |
| ++j; |
| } |
| if (i == end) { |
| if (*j == '\0') { |
| return pos; |
| } else { |
| find_count++; |
| find_pos = pos; |
| } |
| } |
| } |
| return find_count == 1 ? find_pos : -1; |
| } |
| |
| static int check_word(const char* lib[], const char* str, const char* end, const char** endptr) { |
| const char* ptr = str; |
| while (ptr < end && isalpha(*ptr)) { |
| ptr++; |
| } |
| int pos = find_in_lib(lib, str, ptr); |
| if (pos >= 0) { |
| *endptr = ptr; |
| } |
| return pos; |
| } |
| |
| // this method is exactly same as fromDateFormatStr() in DateLiteral.java in FE |
| // change this method should also change that. |
| bool VecDateTimeValue::from_date_format_str(const char* format, int format_len, const char* value, |
| int64_t value_len, const char** sub_val_end) { |
| if (value_len <= 0) [[unlikely]] { |
| return false; |
| } |
| const char* ptr = format; |
| const char* end = format + format_len; |
| const char* val = value; |
| const char* val_end = value + value_len; |
| |
| bool already_set_time_part = false; // skip time part in the end's setting. |
| |
| uint32_t part_used = 0; |
| constexpr int YEAR_PART = 1U << 0; |
| constexpr int MONTH_PART = 1U << 1; |
| constexpr int DAY_PART = 1U << 2; |
| constexpr int NORMAL_DATE_PART = YEAR_PART | MONTH_PART | DAY_PART; |
| constexpr int WEEKDAY_PART = 1U << 3; |
| constexpr int YEARDAY_PART = 1U << 4; |
| constexpr int WEEK_NUM_PART = 1U << 5; |
| constexpr int SPECIAL_DATE_PART = WEEKDAY_PART | YEARDAY_PART | WEEK_NUM_PART; |
| [[maybe_unused]] constexpr int DATE_PART = NORMAL_DATE_PART | SPECIAL_DATE_PART; |
| constexpr int HOUR_PART = 1U << 6; |
| constexpr int MINUTE_PART = 1U << 7; |
| constexpr int SECOND_PART = 1U << 8; |
| constexpr int TIME_PART = HOUR_PART | MINUTE_PART | SECOND_PART; |
| |
| int half_day = 0; // 0 for am/none, 12 for pm. |
| int weekday = -1; |
| int yearday = -1; |
| int week_num = -1; // week idx in one year |
| |
| bool strict_week_number = false; |
| bool sunday_first = false; |
| bool strict_week_number_year_type = false; |
| int strict_week_number_year = -1; |
| bool hour_system_12 = false; |
| |
| auto [year, month, day, hour, minute, second] = std::tuple {0, 0, 0, 0, 0, 0}; |
| while (ptr < end && val < val_end) { |
| // Skip space character |
| while (val < val_end && check_space(*val)) { |
| val++; |
| } |
| // Check switch |
| if (*ptr == '%' && ptr + 1 < end) { |
| const char* tmp = nullptr; |
| int64_t int_value = 0; |
| ptr++; |
| switch (*ptr++) { |
| // Year |
| case 'y': |
| // Year, numeric (two digits) |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| int_value += int_value >= 70 ? 1900 : 2000; |
| year = int_value; |
| val = tmp; |
| part_used |= YEAR_PART; |
| break; |
| case 'Y': |
| // Year, numeric, four digits |
| tmp = val + min(4, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| if (tmp - val <= 2) { |
| int_value += int_value >= 70 ? 1900 : 2000; |
| } |
| year = int_value; |
| val = tmp; |
| part_used |= YEAR_PART; |
| break; |
| // Month |
| case 'm': |
| case 'c': |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| month = int_value; |
| val = tmp; |
| part_used |= MONTH_PART; |
| break; |
| case 'M': |
| int_value = check_word(const_cast<const char**>(s_month_name), val, val_end, &val); |
| if (int_value < 0) { |
| return false; |
| } |
| month = int_value; |
| part_used |= MONTH_PART; |
| break; |
| case 'b': |
| int_value = check_word(s_ab_month_name, val, val_end, &val); |
| if (int_value < 0) { |
| return false; |
| } |
| month = int_value; |
| part_used |= MONTH_PART; |
| break; |
| // Day |
| case 'd': |
| case 'e': |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| day = int_value; |
| val = tmp; |
| part_used |= DAY_PART; |
| break; |
| case 'D': |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| day = int_value; |
| val = tmp + min(2, val_end - tmp); |
| part_used |= DAY_PART; |
| break; |
| // Hour |
| case 'h': |
| case 'I': |
| case 'l': |
| hour_system_12 = true; |
| part_used |= HOUR_PART; |
| // Fall through |
| case 'k': |
| case 'H': |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| hour = int_value; |
| val = tmp; |
| part_used |= HOUR_PART; |
| break; |
| // Minute |
| case 'i': |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| minute = int_value; |
| val = tmp; |
| part_used |= MINUTE_PART; |
| break; |
| // Second |
| case 's': |
| case 'S': |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| second = int_value; |
| val = tmp; |
| part_used |= SECOND_PART; |
| break; |
| // Micro second |
| case 'f': |
| // _microsecond is removed, but need to eat this val |
| tmp = val + min(6, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| val = tmp; |
| break; |
| // AM/PM, only meaningful for 12-hour system. |
| case 'p': |
| if ((val_end - val) < 2 || toupper(*(val + 1)) != 'M' || !hour_system_12) { |
| return false; |
| } |
| if (toupper(*val) == 'P') { |
| // PM |
| half_day = 12; |
| } |
| val += 2; |
| break; |
| // Weekday |
| case 'W': |
| int_value = check_word(const_cast<const char**>(s_day_name), val, val_end, &val); |
| if (int_value < 0) { |
| return false; |
| } |
| int_value++; |
| weekday = int_value; |
| part_used |= WEEKDAY_PART; |
| break; |
| case 'a': |
| int_value = check_word(s_ab_day_name, val, val_end, &val); |
| if (int_value < 0) { |
| return false; |
| } |
| int_value++; |
| weekday = int_value; |
| part_used |= WEEKDAY_PART; |
| break; |
| case 'w': |
| tmp = val + min(1, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| if (int_value >= 7) { |
| return false; |
| } |
| if (int_value == 0) { |
| int_value = 7; |
| } |
| weekday = int_value; |
| val = tmp; |
| part_used |= WEEKDAY_PART; |
| break; |
| case 'j': |
| tmp = val + min(3, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| yearday = int_value; |
| val = tmp; |
| part_used |= YEARDAY_PART; |
| break; |
| case 'u': |
| case 'v': |
| case 'U': |
| case 'V': |
| sunday_first = (*(ptr - 1) == 'U' || *(ptr - 1) == 'V'); |
| // Used to check if there is %x or %X |
| strict_week_number = (*(ptr - 1) == 'V' || *(ptr - 1) == 'v'); |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| week_num = int_value; |
| if (week_num > 53 || (strict_week_number && week_num == 0)) { |
| return false; |
| } |
| val = tmp; |
| part_used |= WEEK_NUM_PART; |
| break; |
| // strict week number, must be used with %V or %v |
| case 'x': |
| case 'X': |
| strict_week_number_year_type = (*(ptr - 1) == 'X'); |
| tmp = val + min(4, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| strict_week_number_year = int_value; |
| val = tmp; |
| part_used |= WEEK_NUM_PART; |
| break; |
| case 'r': { |
| VecDateTimeValue tmp_val; |
| if (!tmp_val.from_date_format_str("%I:%i:%S %p", 11, val, val_end - val, &tmp)) { |
| return false; |
| } |
| this->_hour = tmp_val._hour; |
| this->_minute = tmp_val._minute; |
| this->_second = tmp_val._second; |
| val = tmp; |
| part_used |= TIME_PART; |
| already_set_time_part = true; |
| break; |
| } |
| case 'T': { |
| VecDateTimeValue tmp_val; |
| if (!tmp_val.from_date_format_str("%H:%i:%S", 8, val, val_end - val, &tmp)) { |
| return false; |
| } |
| this->_hour = tmp_val._hour; |
| this->_minute = tmp_val._minute; |
| this->_second = tmp_val._second; |
| part_used |= TIME_PART; |
| already_set_time_part = true; |
| val = tmp; |
| break; |
| } |
| case '.': |
| while (val < val_end && ispunct(*val)) { |
| val++; |
| } |
| break; |
| case '@': |
| while (val < val_end && isalpha(*val)) { |
| val++; |
| } |
| break; |
| case '#': |
| while (val < val_end && isdigit(*val)) { |
| val++; |
| } |
| break; |
| case '%': // %%, escape the % |
| if ('%' != *val) { |
| return false; |
| } |
| val++; |
| break; |
| default: |
| return false; |
| } |
| } else if (!check_space(*ptr)) { |
| if (*ptr != *val) { |
| return false; |
| } |
| ptr++; |
| val++; |
| } else { |
| ptr++; |
| } |
| } |
| |
| // for compatible with mysql, like something have %H:%i:%s format but no relative content... |
| while (ptr < end) { |
| if (*ptr == '%' && ptr + 1 < end) { |
| ptr++; |
| switch (*ptr++) { |
| case 'H': |
| case 'h': |
| case 'I': |
| case 'i': |
| case 'k': |
| case 'l': |
| case 'r': |
| case 's': |
| case 'f': |
| case 'S': |
| case 'p': |
| case 'T': |
| part_used |= TIME_PART; |
| break; |
| default: |
| break; |
| } |
| } else { |
| ptr++; |
| } |
| } |
| |
| if (!part_used) { |
| return false; |
| } |
| |
| if (hour_system_12) { |
| if (hour > 12 || hour < 1) { |
| return false; |
| } |
| hour = (hour % 12) + half_day; |
| } |
| if (sub_val_end) { |
| *sub_val_end = val; |
| } |
| |
| // Compute timestamp type |
| if (part_used & DATE_PART) { |
| if (part_used & TIME_PART) { |
| _type = TIME_DATETIME; |
| } else { |
| _type = TIME_DATE; |
| } |
| } else { |
| _type = TIME_TIME; |
| } |
| |
| _neg = false; |
| |
| // Year day |
| if (yearday > 0) { |
| uint64_t days = doris::calc_daynr(year, 1, 1) + yearday - 1; |
| if (!get_date_from_daynr(days)) { |
| return false; |
| } |
| } |
| // weekday |
| if (week_num >= 0 && weekday > 0) { |
| // Check |
| if ((strict_week_number && |
| (strict_week_number_year < 0 || strict_week_number_year_type != sunday_first)) || |
| (!strict_week_number && strict_week_number_year >= 0)) { |
| return false; |
| } |
| uint64_t days = |
| doris::calc_daynr(strict_week_number ? strict_week_number_year : year, 1, 1); |
| |
| uint8_t weekday_b = doris::calc_weekday(days, sunday_first); |
| |
| if (sunday_first) { |
| days += ((weekday_b == 0) ? 0 : 7) - weekday_b + (week_num - 1) * 7 + weekday % 7; |
| } else { |
| days += ((weekday_b <= 3) ? 0 : 7) - weekday_b + (week_num - 1) * 7 + weekday - 1; |
| } |
| if (!get_date_from_daynr(days)) { |
| return false; |
| } |
| } |
| // 1. already_set_date_part means _year, _month, _day be set, so we only set time part |
| // 2. already_set_time_part means _hour, _minute, _second, _microsecond be set, |
| // so we only need to set date part |
| // 3. if both are true, means all part of date_time be set, no need check_range_and_set_time |
| bool already_set_date_part = yearday > 0 || (week_num >= 0 && weekday > 0); |
| if (already_set_date_part && already_set_time_part) { |
| return true; |
| } |
| // complete default month/day |
| if (!(part_used & ~NORMAL_DATE_PART)) { // Ymd part only |
| if (!(part_used & DAY_PART)) { |
| day = 1; |
| if (!(part_used & MONTH_PART)) { |
| month = 1; |
| } |
| } |
| } |
| |
| if (already_set_date_part) { |
| return check_range_and_set_time(_year, _month, _day, hour, minute, second, _type); |
| } |
| if (already_set_time_part) { |
| return check_range_and_set_time(year, month, day, _hour, _minute, _second, _type); |
| } |
| |
| return check_range_and_set_time(year, month, day, hour, minute, second, _type); |
| } |
| |
| template <TimeUnit unit, bool need_check> |
| bool VecDateTimeValue::date_add_interval(const TimeInterval& interval) { |
| if constexpr (need_check) { |
| if (!is_valid_date()) [[unlikely]] { |
| return false; |
| } |
| } |
| |
| int sign = interval.is_neg ? -1 : 1; |
| |
| if constexpr ((unit == SECOND) || (unit == MINUTE) || (unit == HOUR) || |
| (unit == SECOND_MICROSECOND) || (unit == MINUTE_MICROSECOND) || |
| (unit == MINUTE_SECOND) || (unit == HOUR_MICROSECOND) || (unit == HOUR_SECOND) || |
| (unit == HOUR_MINUTE) || (unit == DAY_MICROSECOND) || (unit == DAY_SECOND) || |
| (unit == DAY_MINUTE) || (unit == DAY_HOUR)) { |
| // This may change the day information |
| |
| int64_t seconds = (_day - 1) * 86400L + _hour * 3600L + _minute * 60 + _second + |
| sign * (interval.day * 86400 + interval.hour * 3600 + |
| interval.minute * 60 + interval.second); |
| int64_t days = seconds / 86400; |
| seconds %= 86400L; |
| if (seconds < 0) { |
| seconds += 86400L; |
| days--; |
| } |
| _second = seconds % 60; |
| _minute = (seconds / 60) % 60; |
| _hour = seconds / 3600; |
| int64_t day_nr = doris::calc_daynr(_year, _month, 1) + days; |
| if (!get_date_from_daynr(day_nr)) { |
| return false; |
| } |
| if (_second || _minute || _hour) { |
| _type = TIME_DATETIME; |
| } |
| } else if constexpr ((unit == DAY) || (unit == WEEK)) { |
| // This only change day information, not change second information |
| int64_t day_nr = daynr() + interval.day * sign; |
| if (!get_date_from_daynr(day_nr)) { |
| return false; |
| } |
| } else if constexpr (unit == YEAR) { |
| // This only change year information |
| _year += sign * interval.year; |
| if (_year > 9999) { |
| return false; |
| } |
| if (_month == 2 && _day == 29 && !doris::is_leap(_year)) { |
| _day = 28; |
| } |
| } else if constexpr (unit == QUARTER || unit == MONTH || unit == YEAR_MONTH) { |
| // This will change month and year information, maybe date. |
| int64_t months = _year * 12 + _month - 1 + sign * (12 * interval.year + interval.month); |
| _year = months / 12; |
| if (months < 0) { |
| return false; |
| } |
| if (_year > MAX_YEAR) { |
| return false; |
| } |
| _month = (months % 12) + 1; |
| if (_day > S_DAYS_IN_MONTH[_month]) { |
| _day = S_DAYS_IN_MONTH[_month]; |
| if (_month == 2 && doris::is_leap(_year)) { |
| _day++; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| template <TimeUnit unit> |
| bool VecDateTimeValue::date_set_interval(const TimeInterval& interval) { |
| static_assert( |
| (unit == YEAR) || (unit == MONTH) || (unit == DAY) || (unit == HOUR) || |
| (unit == MINUTE) || (unit == SECOND), |
| "date_set_interval function now only support YEAR MONTH DAY HOUR MINUTE SECOND type"); |
| if constexpr ((unit == SECOND) || (unit == MINUTE) || (unit == HOUR)) { |
| // This may change the day information |
| int64_t seconds = interval.day * 86400L + interval.hour * 3600 + interval.minute * 60 + |
| interval.second; |
| int64_t days = seconds / 86400; |
| seconds %= 86400L; |
| _second = seconds % 60; |
| _minute = (seconds / 60) % 60; |
| _hour = seconds / 3600; |
| int64_t day_nr = doris::calc_daynr(_year, _month, 1) + days; |
| if (!get_date_from_daynr(day_nr)) { |
| return false; |
| } |
| if (_second || _minute || _hour) { |
| _type = TIME_DATETIME; |
| } |
| } else if constexpr ((unit == DAY)) { |
| // This only change day information, not change second information |
| int64_t day_nr = interval.day; |
| if (!get_date_from_daynr(day_nr)) { |
| return false; |
| } |
| } else if constexpr (unit == YEAR) { |
| // This only change year information |
| _year = interval.year; |
| _day = 1; |
| _month = 1; |
| } else if constexpr (unit == MONTH) { |
| // This will change month and year information, maybe date. |
| int64_t months = 12 * interval.year + interval.month; |
| _year = months / 12; |
| _day = 1; |
| _month = (months % 12) + 1; |
| } |
| return true; |
| } |
| |
| bool VecDateTimeValue::unix_timestamp(int64_t* timestamp, const std::string& timezone) const { |
| cctz::time_zone ctz; |
| if (!TimezoneUtils::find_cctz_time_zone(timezone, ctz)) { |
| return false; |
| } |
| return unix_timestamp(timestamp, ctz); |
| } |
| |
| bool VecDateTimeValue::unix_timestamp(int64_t* timestamp, const cctz::time_zone& ctz) const { |
| const auto tp = |
| cctz::convert(cctz::civil_second(_year, _month, _day, _hour, _minute, _second), ctz); |
| *timestamp = tp.time_since_epoch().count(); |
| return true; |
| } |
| |
| bool VecDateTimeValue::from_unixtime(int64_t timestamp, const std::string& timezone) { |
| cctz::time_zone ctz; |
| if (!TimezoneUtils::find_cctz_time_zone(timezone, ctz)) { |
| return false; |
| } |
| from_unixtime(timestamp, ctz); |
| return true; |
| } |
| |
| void VecDateTimeValue::from_unixtime(int64_t timestamp, const cctz::time_zone& ctz) { |
| static const cctz::time_point<cctz::sys_seconds> epoch = |
| std::chrono::time_point_cast<cctz::sys_seconds>( |
| std::chrono::system_clock::from_time_t(0)); |
| cctz::time_point<cctz::sys_seconds> t = epoch + cctz::seconds(timestamp); |
| |
| const auto tp = cctz::convert(t, ctz); |
| |
| // there's no overflow check since it's hot path |
| |
| _neg = 0; |
| _type = TIME_DATETIME; |
| _year = tp.year(); |
| _month = tp.month(); |
| _day = tp.day(); |
| _hour = tp.hour(); |
| _minute = tp.minute(); |
| _second = tp.second(); |
| } |
| |
| const char* VecDateTimeValue::month_name() const { |
| if (_month < 1 || _month > 12) { |
| return nullptr; |
| } |
| return s_month_name[_month]; |
| } |
| |
| const char* VecDateTimeValue::day_name() const { |
| int day = weekday(); |
| if (day < 0 || day >= 7) { |
| return nullptr; |
| } |
| return s_day_name[day]; |
| } |
| |
| VecDateTimeValue VecDateTimeValue::local_time() { |
| VecDateTimeValue value; |
| value.from_unixtime(time(nullptr), TimezoneUtils::default_time_zone); |
| return value; |
| } |
| |
| void VecDateTimeValue::unchecked_set_time(uint32_t year, uint32_t month, uint32_t day, |
| uint32_t hour, uint32_t minute, uint32_t second) { |
| _year = year; |
| _month = month; |
| _day = day; |
| _hour = hour; |
| _minute = minute; |
| _second = second; |
| } |
| |
| template <TimeUnit unit> |
| bool VecDateTimeValue::datetime_trunc() { |
| if (!is_valid_date()) [[unlikely]] { |
| return false; |
| } |
| switch (unit) { |
| case SECOND: { |
| break; |
| } |
| case MINUTE: { |
| _second = 0; |
| break; |
| } |
| case HOUR: { |
| _second = 0; |
| _minute = 0; |
| break; |
| } |
| case DAY: { |
| _second = 0; |
| _minute = 0; |
| _hour = 0; |
| break; |
| } |
| case WEEK: { |
| _second = 0; |
| _minute = 0; |
| _hour = 0; |
| TimeInterval interval(DAY, weekday(), true); |
| date_add_interval<DAY>(interval); |
| break; |
| } |
| case MONTH: { |
| _second = 0; |
| _minute = 0; |
| _hour = 0; |
| _day = 1; |
| break; |
| } |
| case QUARTER: { |
| _second = 0; |
| _minute = 0; |
| _hour = 0; |
| _day = 1; |
| if (_month <= 3) { |
| _month = 1; |
| } else if (_month <= 6) { |
| _month = 4; |
| } else if (_month <= 9) { |
| _month = 7; |
| } else { |
| _month = 10; |
| } |
| break; |
| } |
| case YEAR: { |
| _second = 0; |
| _minute = 0; |
| _hour = 0; |
| _day = 1; |
| _month = 1; |
| break; |
| } |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| template <typename T> |
| void VecDateTimeValue::create_from_date_v2(DateV2Value<T>& value, TimeType type) { |
| if constexpr (std::is_same_v<T, DateV2ValueType>) { |
| this->unchecked_set_time(value.year(), value.month(), value.day(), 0, 0, 0); |
| } else { |
| this->unchecked_set_time(value.year(), value.month(), value.day(), value.hour(), |
| value.minute(), value.second()); |
| } |
| this->set_type(type); |
| this->_neg = 0; |
| } |
| |
| template <typename T> |
| void VecDateTimeValue::create_from_date_v2(DateV2Value<T>&& value, TimeType type) { |
| DateV2Value<T> v = value; |
| create_from_date_v2(v, type); |
| } |
| |
| std::ostream& operator<<(std::ostream& os, const VecDateTimeValue& value) { |
| char buf[64]; |
| value.to_string(buf); |
| return os << buf; |
| } |
| |
| // NOTE: |
| // only support DATE - DATE (no support DATETIME - DATETIME) |
| std::size_t operator-(const VecDateTimeValue& v1, const VecDateTimeValue& v2) { |
| return v1.daynr() - v2.daynr(); |
| } |
| |
| template <typename T> |
| std::size_t operator-(const DateV2Value<T>& v1, const VecDateTimeValue& v2) { |
| return v1.daynr() - v2.daynr(); |
| } |
| |
| template <typename T> |
| std::size_t operator-(const VecDateTimeValue& v1, const DateV2Value<T>& v2) { |
| return v1.daynr() - v2.daynr(); |
| } |
| |
| std::size_t hash_value(VecDateTimeValue const& value) { |
| return HashUtil::hash(&value, sizeof(VecDateTimeValue), 0); |
| } |
| |
| template <typename T> |
| void DateV2Value<T>::format_datetime(uint32_t* date_val, bool* carry_bits) const { |
| // ms |
| DCHECK(date_val[6] < 1000000L); |
| // hour, minute, second |
| for (size_t i = 5; i > 2; i--) { |
| if (date_val[i] == MAX_TIME_PART_VALUE[i - 3] + 1 && carry_bits[i + 1]) { |
| date_val[i] = 0; |
| date_val[i - 1] += 1; |
| carry_bits[i] = true; |
| } |
| } |
| // day |
| if (date_val[1] == 2 && doris::is_leap(date_val[0])) { |
| if (date_val[2] == 30 && carry_bits[3]) { |
| date_val[2] = 1; |
| date_val[1] += 1; |
| carry_bits[2] = true; |
| } |
| } else if (date_val[2] == S_DAYS_IN_MONTH[date_val[1]] + 1 && carry_bits[3]) { |
| date_val[2] = 1; |
| date_val[1] += 1; |
| carry_bits[2] = true; |
| } |
| // month |
| if (date_val[1] == 13 && carry_bits[2]) { |
| date_val[1] = 1; |
| date_val[0] += 1; |
| } |
| } |
| |
| // The interval format is that with no delimiters |
| // YYYY-MM-DD HH-MM-DD.FFFFFF AM in default format |
| // 0 1 2 3 4 5 6 7 |
| template <typename T> |
| bool DateV2Value<T>::from_date_str(const char* date_str, int len, int scale /* = -1*/, |
| bool convert_zero) { |
| return from_date_str_base(date_str, len, scale, nullptr, convert_zero); |
| } |
| template <typename T> |
| bool DateV2Value<T>::from_date_str(const char* date_str, int len, |
| const cctz::time_zone& local_time_zone, int scale /* = -1*/, |
| bool convert_zero) { |
| return from_date_str_base(date_str, len, scale, &local_time_zone, convert_zero); |
| } |
| // if local_time_zone is null, only be able to parse time without timezone |
| template <typename T> |
| bool DateV2Value<T>::from_date_str_base(const char* date_str, int len, int scale, |
| const cctz::time_zone* local_time_zone, bool convert_zero) { |
| const char* ptr = date_str; |
| const char* end = date_str + len; |
| // ONLY 2, 6 can follow by a space |
| const static int allow_space_mask = 4 | 64; |
| uint32_t date_val[MAX_DATE_PARTS] = {0}; |
| int32_t date_len[MAX_DATE_PARTS] = {0}; |
| |
| // Skip space character |
| while (ptr < end && check_space(*ptr)) { |
| ptr++; |
| } |
| if (ptr == end || !isdigit(*ptr)) { |
| return false; |
| } |
| // Fix year length |
| const char* pos = ptr; |
| while (pos < end && (isdigit(*pos) || *pos == 'T')) { |
| pos++; |
| } |
| int year_len = 4; |
| int digits = pos - ptr; |
| bool is_interval_format = false; |
| bool has_bar = false; |
| |
| // Compatible with MySQL. |
| // For YYYYMMDD/YYYYMMDDHHMMSS is 4 digits years |
| if (pos == end || *pos == '.' || |
| time_zone_begins(pos, end)) { // no delimeter until ./Asia/Z/GMT... |
| if (digits == 4 || digits == 8 || digits >= 14) { |
| year_len = 4; |
| } else { |
| year_len = 2; |
| } |
| is_interval_format = true; |
| } |
| |
| int field_idx = 0; |
| int field_len = year_len; |
| long sec_offset = 0; |
| bool need_use_timezone = false; |
| |
| while (ptr < end && isdigit(*ptr) && field_idx < MAX_DATE_PARTS) { |
| const char* start = ptr; |
| int temp_val = 0; |
| bool scan_to_delim = (!is_interval_format) && (field_idx != 6); |
| while (ptr < end && isdigit(*ptr) && (scan_to_delim || field_len--)) { // field_len <= 7 |
| temp_val = temp_val * 10 + (*ptr - '0'); |
| ptr++; |
| } |
| |
| if (ptr == start) { |
| return false; |
| } |
| |
| if (field_idx == 6) { |
| if constexpr (is_datetime) { |
| // round of microseconds |
| // 1. normalize to 7 digits for rounding |
| // 2. rounding |
| // 3. nomalize to 6 digits for storage |
| if (scale >= 0) { |
| // do normalization |
| const auto ms_digit_count = ptr - start; |
| const auto normalizer = int_exp10(std::abs(7 - ms_digit_count)); |
| temp_val *= normalizer; |
| |
| // check round |
| const auto rounder = int_exp10(std::abs(7 - scale)); |
| const auto reminder = temp_val % rounder; |
| temp_val -= reminder; |
| |
| if (reminder >= 5 * normalizer) { |
| temp_val += rounder; |
| } |
| |
| // truncate to 6 digits |
| if (temp_val == int_exp10(7)) { |
| temp_val = 0; |
| sec_offset += 1; |
| } else { |
| temp_val /= 10; |
| } |
| } |
| |
| // move ptr to start of timezone or end |
| while (ptr < end && isdigit(*ptr)) { |
| ptr++; |
| } |
| } else { |
| // Microsecond |
| const auto ms_part = ptr - start; |
| temp_val *= int_exp10(std::max(0L, 6 - ms_part)); |
| } |
| } |
| |
| // Impossible |
| if (temp_val > 999999L) { |
| return false; |
| } |
| |
| date_val[field_idx] = temp_val; |
| |
| if (field_idx == 6) { |
| // select cast("2020-01-01 12:00:00.12345" as Datetime(4)) |
| // ptr - start will be 5, but scale is 4 |
| date_len[field_idx] = std::min(static_cast<int>(ptr - start), scale); |
| } else { |
| date_len[field_idx] = ptr - start; |
| } |
| |
| field_len = 2; |
| |
| if (ptr == end) { |
| field_idx++; |
| break; |
| } |
| |
| // timezone |
| if (UNLIKELY((field_idx > 2 || |
| !has_bar) /*dont treat xxxx-xx-xx:xx:xx as xxxx-xx(-xx:xx:xx)*/ |
| && time_zone_begins(ptr, end))) { |
| if (local_time_zone == nullptr) { |
| return false; |
| } |
| need_use_timezone = true; |
| field_idx++; |
| break; |
| } |
| |
| if (field_idx == 2 && *ptr == 'T') { |
| // YYYYMMDDTHHMMDD, skip 'T' and continue |
| ptr++; |
| field_idx++; |
| continue; |
| } |
| |
| // Second part |
| if (field_idx == 5) { |
| if (*ptr == '.') { |
| ptr++; |
| // for datetime, we need to discard the fraction part |
| // that beyond the scale + 1, and scale + 1 digit will |
| // be used to round the fraction part |
| if constexpr (is_datetime) { |
| field_len = std::min(7, scale + 1); |
| } else { |
| field_len = 6; |
| } |
| } else if (isdigit(*ptr)) { |
| field_idx++; |
| break; |
| } |
| field_idx++; |
| continue; |
| } |
| // escape separator |
| while (ptr < end && (check_date_punct(*ptr) || check_space(*ptr))) { |
| if (check_space(*ptr)) { |
| if (((1 << field_idx) & allow_space_mask) == 0) { |
| return false; |
| } |
| } |
| if (*ptr == '-') { |
| has_bar = true; |
| } |
| ptr++; |
| } |
| field_idx++; |
| } |
| |
| int num_field = field_idx; |
| if (!is_interval_format) { |
| year_len = date_len[0]; |
| } |
| for (; field_idx < MAX_DATE_PARTS; ++field_idx) { |
| date_val[field_idx] = 0; |
| } |
| |
| if (year_len == 2) { |
| if (date_val[0] < YY_PART_YEAR) { |
| date_val[0] += 2000; |
| } else { |
| date_val[0] += 1900; |
| } |
| } |
| |
| if (num_field < 3) { |
| return false; |
| } |
| if (is_invalid(date_val[0], date_val[1], date_val[2], 0, 0, 0, 0)) { |
| if (date_val[0] == 0 && date_val[1] == 0 && date_val[2] == 0 && convert_zero) { |
| date_val[1] = 1; |
| date_val[2] = 1; |
| } else { |
| return false; |
| } |
| } |
| |
| if (need_use_timezone) { |
| cctz::time_zone given_tz {}; |
| if (!TimezoneUtils::find_cctz_time_zone(std::string {ptr, end}, given_tz)) { |
| return false; // invalid format |
| } |
| auto given = cctz::convert(cctz::civil_second {}, given_tz); |
| auto local = cctz::convert(cctz::civil_second {}, *local_time_zone); |
| // these two values is absolute time. so they are negative. need to use (-local) - (-given) |
| sec_offset = std::chrono::duration_cast<std::chrono::seconds>(given - local).count(); |
| } |
| |
| // In check_range_and_set_time, for Date type the time part will be truncated. So if the timezone offset should make |
| // rounding to date part, it would be lost. To avoid this, we use a Datetime type to do these calc. It will save the |
| // time part and apply the offset. Then convert to Date type back. |
| // see https://github.com/apache/doris/pull/33553 for more details. |
| if constexpr (!is_datetime) { |
| if (sec_offset) { |
| DateV2Value<DateTimeV2ValueType> tmp; |
| if (!tmp.check_range_and_set_time(date_val[0], date_val[1], date_val[2], date_val[3], |
| date_val[4], date_val[5], date_val[6])) { |
| return false; |
| } |
| if (!tmp.date_add_interval<TimeUnit::SECOND>( |
| TimeInterval {TimeUnit::SECOND, sec_offset, false})) { |
| return false; |
| } |
| this->assign_from(tmp); |
| return true; |
| } |
| } |
| |
| if (!check_range_and_set_time(date_val[0], date_val[1], date_val[2], date_val[3], date_val[4], |
| date_val[5], date_val[6])) { |
| return false; |
| } |
| |
| return sec_offset ? date_add_interval<TimeUnit::SECOND>( |
| TimeInterval {TimeUnit::SECOND, sec_offset, false}) |
| : true; |
| } |
| |
| template <typename T> |
| void DateV2Value<T>::set_zero() { |
| int_val_ = 0; |
| } |
| |
| // this method is exactly same as fromDateFormatStr() in DateLiteral.java in FE |
| // change this method should also change that. |
| template <typename T> |
| bool DateV2Value<T>::from_date_format_str(const char* format, int format_len, const char* value, |
| int64_t value_len, const char** sub_val_end) { |
| if (value_len <= 0) [[unlikely]] { |
| return false; |
| } |
| const char* ptr = format; |
| const char* end = format + format_len; |
| const char* val = value; |
| const char* val_end = value + value_len; |
| |
| bool already_set_time_part = false; // skip time part in the end's setting. |
| |
| uint32_t part_used = 0; |
| constexpr int YEAR_PART = 1U << 0; |
| constexpr int MONTH_PART = 1U << 1; |
| constexpr int DAY_PART = 1U << 2; |
| constexpr int NORMAL_DATE_PART = YEAR_PART | MONTH_PART | DAY_PART; |
| constexpr int WEEKDAY_PART = 1U << 3; |
| constexpr int YEARDAY_PART = 1U << 4; |
| constexpr int WEEK_NUM_PART = 1U << 5; |
| constexpr int SPECIAL_DATE_PART = WEEKDAY_PART | YEARDAY_PART | WEEK_NUM_PART; |
| [[maybe_unused]] constexpr int DATE_PART = NORMAL_DATE_PART | SPECIAL_DATE_PART; |
| constexpr int HOUR_PART = 1U << 6; |
| constexpr int MINUTE_PART = 1U << 7; |
| constexpr int SECOND_PART = 1U << 8; |
| constexpr int FRAC_PART = 1U << 9; |
| constexpr int TIME_PART = HOUR_PART | MINUTE_PART | SECOND_PART | FRAC_PART; |
| |
| int half_day = 0; // 0 for am/none, 12 for pm. |
| int weekday = -1; |
| int yearday = -1; |
| int week_num = -1; |
| |
| bool strict_week_number = false; |
| bool sunday_first = false; |
| bool strict_week_number_year_type = false; |
| int strict_week_number_year = -1; |
| bool hour_system_12 = false; |
| |
| auto [year, month, day, hour, minute, second, microsecond] = std::tuple {0, 0, 0, 0, 0, 0, 0}; |
| while (ptr < end && val < val_end) { |
| // Skip space character |
| while (val < val_end && check_space(*val)) { |
| val++; |
| } |
| // Check switch |
| if (*ptr == '%' && ptr + 1 < end) { |
| const char* tmp = nullptr; |
| int64_t int_value = 0; |
| ptr++; |
| switch (*ptr++) { |
| // Year |
| case 'y': |
| // Year, numeric (two digits) |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| int_value += int_value >= 70 ? 1900 : 2000; |
| year = int_value; |
| val = tmp; |
| part_used |= YEAR_PART; |
| break; |
| case 'Y': |
| // Year, numeric, four digits |
| tmp = val + min(4, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| if (tmp - val <= 2) { |
| int_value += int_value >= 70 ? 1900 : 2000; |
| } |
| year = int_value; |
| val = tmp; |
| part_used |= YEAR_PART; |
| break; |
| // Month |
| case 'm': |
| case 'c': |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| month = int_value; |
| val = tmp; |
| part_used |= MONTH_PART; |
| break; |
| case 'M': |
| int_value = check_word(const_cast<const char**>(s_month_name), val, val_end, &val); |
| if (int_value < 0) { |
| return false; |
| } |
| month = int_value; |
| part_used |= MONTH_PART; |
| break; |
| case 'b': |
| int_value = check_word(s_ab_month_name, val, val_end, &val); |
| if (int_value < 0) { |
| return false; |
| } |
| month = int_value; |
| part_used |= MONTH_PART; |
| break; |
| // Day |
| case 'd': |
| case 'e': |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| day = int_value; |
| val = tmp; |
| part_used |= DAY_PART; |
| break; |
| case 'D': |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| day = int_value; |
| val = tmp + min(2, val_end - tmp); |
| part_used |= DAY_PART; |
| break; |
| // Hour |
| case 'h': |
| case 'I': |
| case 'l': |
| hour_system_12 = true; |
| part_used |= HOUR_PART; |
| // Fall through |
| case 'k': |
| case 'H': |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| hour = int_value; |
| val = tmp; |
| part_used |= HOUR_PART; |
| break; |
| // Minute |
| case 'i': |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| minute = int_value; |
| val = tmp; |
| part_used |= MINUTE_PART; |
| break; |
| // Second |
| case 's': |
| case 'S': |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| second = int_value; |
| val = tmp; |
| part_used |= SECOND_PART; |
| break; |
| // Micro second |
| case 'f': |
| tmp = val; |
| // when there's still something to the end, fix the scale of ms. |
| while (tmp < val_end && isdigit(*tmp)) { |
| tmp++; |
| } |
| |
| if (tmp - val > 6) { |
| const char* tmp2 = val + 6; |
| if (!str_to_int64(val, &tmp2, &int_value)) { |
| return false; |
| } |
| } else { |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| } |
| if constexpr (is_datetime) { |
| microsecond = int_value * int_exp10(6 - min(6, tmp - val)); |
| part_used |= FRAC_PART; |
| } |
| val = tmp; |
| break; |
| // AM/PM |
| case 'p': |
| if ((val_end - val) < 2 || toupper(*(val + 1)) != 'M' || !hour_system_12) { |
| return false; |
| } |
| if (toupper(*val) == 'P') { |
| // PM |
| half_day = 12; |
| } |
| val += 2; |
| break; |
| // Weekday |
| case 'W': |
| int_value = check_word(const_cast<const char**>(s_day_name), val, val_end, &val); |
| if (int_value < 0) { |
| return false; |
| } |
| int_value++; |
| weekday = int_value; |
| part_used |= WEEKDAY_PART; |
| break; |
| case 'a': |
| int_value = check_word(s_ab_day_name, val, val_end, &val); |
| if (int_value < 0) { |
| return false; |
| } |
| int_value++; |
| weekday = int_value; |
| part_used |= WEEKDAY_PART; |
| break; |
| case 'w': |
| tmp = val + min(1, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| if (int_value >= 7) { |
| return false; |
| } |
| if (int_value == 0) { |
| int_value = 7; |
| } |
| weekday = int_value; |
| val = tmp; |
| part_used |= WEEKDAY_PART; |
| break; |
| case 'j': |
| tmp = val + min(3, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| yearday = int_value; |
| val = tmp; |
| part_used |= YEARDAY_PART; |
| break; |
| case 'u': |
| case 'v': |
| case 'U': |
| case 'V': |
| sunday_first = (*(ptr - 1) == 'U' || *(ptr - 1) == 'V'); |
| // Used to check if there is %x or %X |
| strict_week_number = (*(ptr - 1) == 'V' || *(ptr - 1) == 'v'); |
| tmp = val + min(2, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| week_num = int_value; |
| if (week_num > 53 || (strict_week_number && week_num == 0)) { |
| return false; |
| } |
| val = tmp; |
| part_used |= WEEK_NUM_PART; |
| break; |
| // strict week number, must be used with %V or %v |
| case 'x': |
| case 'X': |
| strict_week_number_year_type = (*(ptr - 1) == 'X'); |
| tmp = val + min(4, val_end - val); |
| if (!str_to_int64(val, &tmp, &int_value)) { |
| return false; |
| } |
| strict_week_number_year = int_value; |
| val = tmp; |
| part_used |= WEEK_NUM_PART; |
| break; |
| case 'r': { |
| if constexpr (is_datetime) { |
| DateV2Value<DateTimeV2ValueType> tmp_val; |
| if (!tmp_val.from_date_format_str("%I:%i:%S %p", 11, val, val_end - val, |
| &tmp)) { |
| return false; |
| } |
| this->date_v2_value_.hour_ = tmp_val.hour(); |
| this->date_v2_value_.minute_ = tmp_val.minute(); |
| this->date_v2_value_.second_ = tmp_val.second(); |
| val = tmp; |
| part_used |= TIME_PART; |
| already_set_time_part = true; |
| break; |
| } else { |
| return false; |
| } |
| } |
| case 'T': { |
| if constexpr (is_datetime) { |
| DateV2Value<DateTimeV2ValueType> tmp_val; |
| if (!tmp_val.from_date_format_str("%H:%i:%S", 8, val, val_end - val, &tmp)) { |
| return false; |
| } |
| this->date_v2_value_.hour_ = tmp_val.hour(); |
| this->date_v2_value_.minute_ = tmp_val.minute(); |
| this->date_v2_value_.second_ = tmp_val.second(); |
| part_used |= TIME_PART; |
| already_set_time_part = true; |
| val = tmp; |
| break; |
| } else { |
| return false; |
| } |
| } |
| case '.': |
| while (val < val_end && ispunct(*val)) { |
| val++; |
| } |
| break; |
| case '@': |
| while (val < val_end && isalpha(*val)) { |
| val++; |
| } |
| break; |
| case '#': |
| while (val < val_end && isdigit(*val)) { |
| val++; |
| } |
| break; |
| case '%': // %%, escape the % |
| if ('%' != *val) { |
| return false; |
| } |
| val++; |
| break; |
| default: |
| return false; |
| } |
| } else if (!isspace(*ptr)) { |
| if (*ptr != *val) { |
| return false; |
| } |
| ptr++; |
| val++; |
| } else { |
| ptr++; |
| } |
| } |
| |
| // for compatible with mysql, like something have %H:%i:%s format but no relative content... |
| while (ptr < end) { |
| if (*ptr == '%' && ptr + 1 < end) { |
| ptr++; |
| switch (*ptr++) { |
| case 'H': |
| case 'h': |
| case 'I': |
| case 'i': |
| case 'k': |
| case 'l': |
| case 'r': |
| case 's': |
| case 'f': |
| case 'S': |
| case 'p': |
| case 'T': |
| part_used |= TIME_PART; |
| break; |
| default: |
| break; |
| } |
| } else { |
| ptr++; |
| } |
| } |
| |
| if (!part_used) { |
| return false; |
| } |
| |
| if (hour_system_12) { |
| if (hour > 12 || hour < 1) { |
| return false; |
| } |
| hour = (hour % 12) + half_day; |
| } |
| if (sub_val_end) { |
| *sub_val_end = val; |
| } |
| |
| // Compute timestamp type |
| if (part_used & FRAC_PART) { |
| if constexpr (!is_datetime) { |
| return false; |
| } |
| } else if (part_used & TIME_PART) { |
| if constexpr (!is_datetime) { |
| return false; |
| } |
| } |
| |
| // Year day |
| if (yearday > 0) { |
| uint64_t days = doris::calc_daynr(year, 1, 1) + yearday - 1; |
| if (!get_date_from_daynr(days)) { |
| return false; |
| } |
| } |
| // weekday |
| if (week_num >= 0 && weekday > 0) { |
| // Check |
| if ((strict_week_number && |
| (strict_week_number_year < 0 || strict_week_number_year_type != sunday_first)) || |
| (!strict_week_number && strict_week_number_year >= 0)) { |
| return false; |
| } |
| uint64_t days = |
| doris::calc_daynr(strict_week_number ? strict_week_number_year : year, 1, 1); |
| |
| uint8_t weekday_b = doris::calc_weekday(days, sunday_first); |
| |
| if (sunday_first) { |
| days += ((weekday_b == 0) ? 0 : 7) - weekday_b + (week_num - 1) * 7 + weekday % 7; |
| } else { |
| days += ((weekday_b <= 3) ? 0 : 7) - weekday_b + (week_num - 1) * 7 + weekday - 1; |
| } |
| if (!get_date_from_daynr(days)) { |
| return false; |
| } |
| } |
| // 1. already_set_date_part means _year, _month, _day be set, so we only set time part |
| // 2. already_set_time_part means _hour, _minute, _second, _microsecond be set, |
| // so we only need to set date part |
| // 3. if both are true, means all part of date_time be set, no need check_range_and_set_time |
| bool already_set_date_part = yearday > 0 || (week_num >= 0 && weekday > 0); |
| if (already_set_date_part && already_set_time_part) { |
| return true; |
| } |
| if (already_set_date_part) { |
| if constexpr (is_datetime) { |
| return check_range_and_set_time(date_v2_value_.year_, date_v2_value_.month_, |
| date_v2_value_.day_, hour, minute, second, microsecond); |
| } else { |
| return check_range_and_set_time(date_v2_value_.year_, date_v2_value_.month_, |
| date_v2_value_.day_, 0, 0, 0, 0); |
| } |
| } |
| // complete default month/day |
| if (!(part_used & ~NORMAL_DATE_PART)) { // Ymd part only |
| if (!(part_used & DAY_PART)) { |
| day = 1; |
| if (!(part_used & MONTH_PART)) { |
| month = 1; |
| } |
| } |
| } |
| |
| if (already_set_time_part) { |
| if constexpr (is_datetime) { |
| return check_range_and_set_time(year, month, day, date_v2_value_.hour_, |
| date_v2_value_.minute_, date_v2_value_.second_, |
| microsecond); |
| } else { |
| return check_range_and_set_time(year, month, day, 0, 0, 0, 0); |
| } |
| } |
| if constexpr (is_datetime) { |
| return check_range_and_set_time(year, month, day, hour, minute, second, microsecond, |
| !(part_used & ~TIME_PART)); |
| } else { |
| return check_range_and_set_time(year, month, day, 0, 0, 0, 0); |
| } |
| } |
| |
| template <typename T> |
| int32_t DateV2Value<T>::to_buffer(char* buffer, int scale) const { |
| // if this is an invalid date, write nothing(instead of 0000-00-00) to output string, or else |
| // it will cause problem for null DataTypeDateV2 value in cast function, |
| // e.g. cast(cast(null_date as char) as date) |
| if (!is_valid_date()) [[unlikely]] { |
| return 0; |
| } |
| char* start = buffer; |
| uint32_t temp; |
| // Year |
| temp = date_v2_value_.year_ / 100; |
| *buffer++ = (char)('0' + (temp / 10)); |
| *buffer++ = (char)('0' + (temp % 10)); |
| temp = date_v2_value_.year_ % 100; |
| *buffer++ = (char)('0' + (temp / 10)); |
| *buffer++ = (char)('0' + (temp % 10)); |
| *buffer++ = '-'; |
| // Month |
| *buffer++ = (char)('0' + (date_v2_value_.month_ / 10)); |
| *buffer++ = (char)('0' + (date_v2_value_.month_ % 10)); |
| *buffer++ = '-'; |
| // Day |
| *buffer++ = (char)('0' + (date_v2_value_.day_ / 10)); |
| *buffer++ = (char)('0' + (date_v2_value_.day_ % 10)); |
| if constexpr (is_datetime) { |
| *buffer++ = ' '; |
| // Hour |
| temp = date_v2_value_.hour_; |
| if (temp >= 100) { |
| *buffer++ = (char)('0' + (temp / 100)); |
| temp %= 100; |
| } |
| *buffer++ = (char)('0' + (temp / 10)); |
| *buffer++ = (char)('0' + (temp % 10)); |
| *buffer++ = ':'; |
| // Minute |
| *buffer++ = (char)('0' + (date_v2_value_.minute_ / 10)); |
| *buffer++ = (char)('0' + (date_v2_value_.minute_ % 10)); |
| *buffer++ = ':'; |
| /* Second */ |
| *buffer++ = (char)('0' + (date_v2_value_.second_ / 10)); |
| *buffer++ = (char)('0' + (date_v2_value_.second_ % 10)); |
| if (scale < 0 && date_v2_value_.microsecond_ > 0) { |
| *buffer++ = '.'; |
| /* Microsecond */ |
| uint32_t ms = date_v2_value_.microsecond_; |
| int ms_width = scale == -1 ? 6 : std::min(6, scale); |
| for (int i = 0; i < ms_width; i++) { |
| *buffer++ = (char)('0' + (ms / int_exp10(5 - i))); |
| ms %= (uint32_t)int_exp10(5 - i); |
| } |
| } else if (scale > 0) { |
| *buffer++ = '.'; |
| /* Microsecond */ |
| uint32_t ms = date_v2_value_.microsecond_; |
| int ms_width = std::min(6, scale); |
| for (int i = 0; i < ms_width; i++) { |
| *buffer++ = (char)('0' + (ms / int_exp10(5 - i))); |
| ms %= (uint32_t)int_exp10(5 - i); |
| } |
| } |
| } |
| return buffer - start; |
| } |
| |
| template <typename T> |
| char* DateV2Value<T>::to_string(char* to, int scale) const { |
| int len = to_buffer(to, scale); |
| *(to + len) = '\0'; |
| return to + len + 1; |
| } |
| |
| // [1900-01-01, 2039-12-31] |
| std::array<DateV2Value<DateV2ValueType>, date_day_offset_dict::DICT_DAYS> |
| date_day_offset_dict::DATE_DAY_OFFSET_ITEMS; |
| |
| // [1900-01-01, 2039-12-31] |
| std::array<std::array<std::array<int, 31>, 12>, 140> date_day_offset_dict::DATE_DAY_OFFSET_DICT; |
| |
| bool date_day_offset_dict::DATE_DAY_OFFSET_ITEMS_INIT = false; |
| |
| date_day_offset_dict date_day_offset_dict::instance = date_day_offset_dict(); |
| |
| date_day_offset_dict::date_day_offset_dict() { |
| DateV2Value<DateV2ValueType> d; |
| // Init days before epoch. |
| d.unchecked_set_time(1969, 12, 31, 0, 0, 0, 0); |
| for (int i = 0; i < DAY_BEFORE_EPOCH; ++i) { |
| DATE_DAY_OFFSET_ITEMS[DAY_BEFORE_EPOCH - i - 1] = d; |
| DATE_DAY_OFFSET_DICT[d.year() - START_YEAR][d.month() - 1][d.day() - 1] = |
| calc_daynr(d.year(), d.month(), d.day()); |
| d -= 1; |
| } |
| // Init epoch day. |
| d.unchecked_set_time(1970, 1, 1, 0, 0, 0, 0); |
| DATE_DAY_OFFSET_ITEMS[DAY_BEFORE_EPOCH] = d; |
| DATE_DAY_OFFSET_DICT[d.year() - START_YEAR][d.month() - 1][d.day() - 1] = |
| calc_daynr(d.year(), d.month(), d.day()); |
| d += 1; |
| |
| // Init days after epoch. |
| for (int i = 0; i < DAY_AFTER_EPOCH; ++i) { |
| DATE_DAY_OFFSET_ITEMS[DAY_BEFORE_EPOCH + 1 + i] = d; |
| DATE_DAY_OFFSET_DICT[d.year() - START_YEAR][d.month() - 1][d.day() - 1] = |
| calc_daynr(d.year(), d.month(), d.day()); |
| d += 1; |
| } |
| |
| DATE_DAY_OFFSET_ITEMS_INIT = true; |
| } |
| |
| template <typename T> |
| uint8_t DateV2Value<T>::week(uint8_t mode) const { |
| uint16_t year = 0; |
| return calc_week(this->daynr(), this->year(), this->month(), this->day(), mode, &year); |
| } |
| |
| template <typename T> |
| uint32_t DateV2Value<T>::year_week(uint8_t mode) const { |
| if (config::enable_time_lut && mode == 4 && this->year() >= 1950 && this->year() < 2030) { |
| return doris::TimeLUT::GetImplement() |
| ->year_week_table[this->year() - 1950][this->month() - 1][this->day() - 1]; |
| } |
| uint16_t year = 0; |
| // The range of the week in the year_week is 1-53, so the mode WEEK_YEAR is always true. |
| uint8_t week = calc_week(this->daynr(), this->year(), this->month(), this->day(), mode | 2, |
| &year, true); |
| // When the mode WEEK_FIRST_WEEKDAY is not set, |
| // the week in which the last three days of the year fall may belong to the following year. |
| if (week == 53 && day() >= 29 && !(mode & 4)) { |
| uint8_t monday_first = mode & WEEK_MONDAY_FIRST; |
| uint64_t daynr_of_last_day = doris::calc_daynr(this->year(), 12, 31); |
| uint8_t weekday_of_last_day = doris::calc_weekday(daynr_of_last_day, !monday_first); |
| |
| if (weekday_of_last_day - monday_first < 2) { |
| ++year; |
| week = 1; |
| } |
| } |
| return year * 100 + week; |
| } |
| |
| template <typename T> |
| bool DateV2Value<T>::get_date_from_daynr(uint64_t daynr) { |
| if (daynr <= 0 || daynr > DATE_MAX_DAYNR) { |
| return false; |
| } |
| auto [year, month, day] = std::tuple {0, 0, 0}; |
| |
| if (date_day_offset_dict::can_speed_up_daynr_to_date(daynr) && |
| LIKELY(date_day_offset_dict::get_dict_init())) { |
| auto dt = date_day_offset_dict::get()[date_day_offset_dict::get_offset_by_daynr(daynr)]; |
| year = dt.year(); |
| month = dt.month(); |
| day = dt.day(); |
| } else { |
| year = daynr / 365; |
| uint32_t days_befor_year = 0; |
| while (daynr < (days_befor_year = doris::calc_daynr(year, 1, 1))) { |
| year--; |
| } |
| uint32_t days_of_year = daynr - days_befor_year + 1; |
| int leap_day = 0; |
| if (doris::is_leap(year)) { |
| if (days_of_year > 31 + 28) { |
| days_of_year--; |
| if (days_of_year == 31 + 28) { |
| leap_day = 1; |
| } |
| } |
| } |
| month = 1; |
| while (days_of_year > S_DAYS_IN_MONTH[month]) { |
| days_of_year -= S_DAYS_IN_MONTH[month]; |
| month++; |
| } |
| day = days_of_year + leap_day; |
| |
| if (is_invalid(year, month, day, this->hour(), this->minute(), this->second(), |
| this->microsecond())) { |
| return false; |
| } |
| } |
| |
| unchecked_set_time(year, month, day, this->hour(), this->minute(), this->second(), |
| this->microsecond()); |
| return true; |
| } |
| |
| template <typename T> |
| template <TimeUnit unit, typename TO> |
| bool DateV2Value<T>::date_add_interval(const TimeInterval& interval, DateV2Value<TO>& to_value) { |
| if (!is_valid_date()) [[unlikely]] { |
| return false; |
| } |
| |
| int sign = interval.is_neg ? -1 : 1; |
| |
| if constexpr ((unit == MICROSECOND) || (unit == MILLISECOND) || (unit == SECOND) || |
| (unit == MINUTE) || (unit == HOUR) || (unit == SECOND_MICROSECOND) || |
| (unit == MINUTE_MICROSECOND) || (unit == MINUTE_SECOND) || |
| (unit == HOUR_MICROSECOND) || (unit == HOUR_SECOND) || (unit == HOUR_MINUTE) || |
| (unit == DAY_MICROSECOND) || (unit == DAY_SECOND) || (unit == DAY_MINUTE) || |
| (unit == DAY_HOUR) || (unit == DAY) || (unit == WEEK)) { |
| // This may change the day information |
| constexpr int64_t microseconds_in_one_second = 1000000L; |
| int64_t microseconds = this->microsecond() + sign * interval.microsecond + |
| sign * interval.millisecond * 1000L; |
| int64_t extra_second = microseconds / microseconds_in_one_second; |
| microseconds -= extra_second * microseconds_in_one_second; |
| |
| int64_t seconds = (this->day() - 1) * 86400L + this->hour() * 3600L + this->minute() * 60 + |
| this->second() + |
| sign * (interval.day * 86400 + interval.hour * 3600 + |
| interval.minute * 60 + interval.second) + |
| extra_second; |
| if (microseconds < 0) { |
| seconds--; |
| microseconds += microseconds_in_one_second; |
| } |
| int64_t days = seconds / 86400; |
| seconds %= 86400L; |
| if (seconds < 0) { |
| seconds += 86400L; |
| days--; |
| } |
| int64_t day_nr = doris::calc_daynr(this->year(), this->month(), 1) + days; |
| if (!to_value.get_date_from_daynr(day_nr)) { |
| return false; |
| } |
| PROPAGATE_FALSE(to_value.check_range_and_set_time( |
| 0, 0, 0, seconds / 3600, (seconds / 60) % 60, seconds % 60, microseconds, true)); |
| } else if constexpr (unit == YEAR) { |
| // This only change year information |
| PROPAGATE_FALSE(to_value.template set_time_unit<TimeUnit::YEAR>(date_v2_value_.year_ + |
| interval.year)); |
| if (date_v2_value_.month_ == 2 && date_v2_value_.day_ == 29 && |
| !doris::is_leap(to_value.year())) { |
| // add year. so if from Leap Year to Equal Year, use last day of Feb(29 to 28) |
| PROPAGATE_FALSE(to_value.template set_time_unit<TimeUnit::DAY>(28)); |
| } |
| } else if constexpr (unit == QUARTER || unit == MONTH || unit == YEAR_MONTH) { |
| // This will change month and year information, maybe date. |
| int64_t months = date_v2_value_.year_ * 12 + date_v2_value_.month_ - 1 + |
| 12 * interval.year + interval.month; |
| if (months < 0) { |
| return false; |
| } |
| PROPAGATE_FALSE(to_value.template set_time_unit<TimeUnit::YEAR>(months / 12)); |
| PROPAGATE_FALSE(to_value.template set_time_unit<TimeUnit::MONTH>((months % 12) + 1)); |
| if (date_v2_value_.day_ > S_DAYS_IN_MONTH[to_value.month()]) { |
| date_v2_value_.day_ = S_DAYS_IN_MONTH[to_value.month()]; |
| if (to_value.month() == 2 && doris::is_leap(to_value.year())) { |
| PROPAGATE_FALSE( |
| to_value.template set_time_unit<TimeUnit::DAY>(date_v2_value_.day_ + 1)); |
| } |
| } |
| } |
| return true; |
| } |
| |
| template <typename T> |
| template <TimeUnit unit, bool need_check> |
| bool DateV2Value<T>::date_add_interval(const TimeInterval& interval) { |
| if constexpr (need_check) { |
| if (!is_valid_date()) [[unlikely]] { |
| return false; |
| } |
| } |
| |
| int sign = interval.is_neg ? -1 : 1; |
| |
| if constexpr ((unit == MICROSECOND) || (unit == MILLISECOND) || (unit == SECOND) || |
| (unit == MINUTE) || (unit == HOUR) || (unit == SECOND_MICROSECOND) || |
| (unit == MINUTE_MICROSECOND) || (unit == MINUTE_SECOND) || |
| (unit == HOUR_MICROSECOND) || (unit == HOUR_SECOND) || (unit == HOUR_MINUTE) || |
| (unit == DAY_MICROSECOND) || (unit == DAY_SECOND) || (unit == DAY_MINUTE) || |
| (unit == DAY_HOUR) || (unit == DAY) || (unit == WEEK)) { |
| // This may change the day information |
| constexpr int64_t microseconds_in_one_second = 1000000L; |
| int64_t microseconds = this->microsecond() + sign * interval.microsecond + |
| sign * interval.millisecond * 1000L; |
| int64_t extra_second = microseconds / microseconds_in_one_second; |
| microseconds -= extra_second * microseconds_in_one_second; |
| |
| int64_t seconds = (this->day() - 1) * 86400L + this->hour() * 3600L + this->minute() * 60 + |
| this->second() + |
| sign * (interval.day * 86400 + interval.hour * 3600 + |
| interval.minute * 60 + interval.second) + |
| extra_second; |
| if (microseconds < 0) { |
| seconds--; |
| microseconds += microseconds_in_one_second; |
| } |
| int64_t days = seconds / 86400; |
| seconds %= 86400L; |
| if (seconds < 0) { |
| seconds += 86400L; |
| days--; |
| } |
| int64_t day_nr = doris::calc_daynr(this->year(), this->month(), 1) + days; |
| if (!this->get_date_from_daynr(day_nr)) { |
| return false; |
| } |
| if constexpr (is_datetime) { |
| if constexpr (need_check) { |
| PROPAGATE_FALSE(this->check_range_and_set_time(0, 0, 0, seconds / 3600, |
| (seconds / 60) % 60, seconds % 60, |
| microseconds, true)); |
| } else { |
| this->unchecked_set_time(seconds / 3600, (seconds / 60) % 60, seconds % 60, |
| microseconds); |
| } |
| } |
| } else if constexpr (unit == YEAR) { |
| // This only change year information |
| PROPAGATE_FALSE( |
| this->template set_time_unit<TimeUnit::YEAR>(date_v2_value_.year_ + interval.year)); |
| if (date_v2_value_.month_ == 2 && date_v2_value_.day_ == 29 && |
| !doris::is_leap(this->year())) { |
| // add year. so if from Leap Year to Equal Year, use last day of Feb(29 to 28) |
| PROPAGATE_FALSE(this->template set_time_unit<TimeUnit::DAY>(28)); |
| } |
| } else if constexpr (unit == QUARTER || unit == MONTH || unit == YEAR_MONTH) { |
| // This will change month and year information, maybe date. |
| int64_t months = date_v2_value_.year_ * 12 + date_v2_value_.month_ - 1 + |
| 12 * interval.year + interval.month; |
| if (months < 0) { |
| return false; |
| } |
| PROPAGATE_FALSE(this->template set_time_unit<TimeUnit::YEAR>(months / 12)); |
| PROPAGATE_FALSE(this->template set_time_unit<TimeUnit::MONTH>((months % 12) + 1)); |
| if (date_v2_value_.day_ > S_DAYS_IN_MONTH[this->month()]) { |
| date_v2_value_.day_ = S_DAYS_IN_MONTH[this->month()]; |
| if (this->month() == 2 && doris::is_leap(this->year())) { |
| PROPAGATE_FALSE( |
| this->template set_time_unit<TimeUnit::DAY>(date_v2_value_.day_ + 1)); |
| } |
| } |
| } |
| return true; |
| } |
| |
| template <typename T> |
| template <TimeUnit unit> |
| bool DateV2Value<T>::date_set_interval(const TimeInterval& interval) { |
| static_assert( |
| (unit == YEAR) || (unit == MONTH) || (unit == DAY) || (unit == HOUR) || |
| (unit == MINUTE) || (unit == SECOND), |
| "date_set_interval function now only support YEAR MONTH DAY HOUR MINUTE SECOND type"); |
| if constexpr ((unit == SECOND) || (unit == MINUTE) || (unit == HOUR) || (unit == DAY)) { |
| set_zero(); |
| // This may change the day information |
| int64_t seconds = (interval.day * 86400 + interval.hour * 3600 + interval.minute * 60 + |
| interval.second); |
| int64_t days = seconds / 86400; |
| seconds %= 86400L; |
| if (!this->get_date_from_daynr(days)) { |
| return false; |
| } |
| if constexpr (is_datetime) { |
| PROPAGATE_FALSE(this->check_range_and_set_time( |
| 0, 0, 0, seconds / 3600, (seconds / 60) % 60, seconds % 60, 0, true)); |
| } |
| } else if constexpr (unit == YEAR) { |
| this->unchecked_set_time(0, 1, 1, 0, 0, 0, 0); |
| PROPAGATE_FALSE(this->template set_time_unit<TimeUnit::YEAR>(interval.year)); |
| } else if constexpr (unit == MONTH) { |
| // This will change month and year information, maybe date. |
| this->unchecked_set_time(0, 1, 1, 0, 0, 0, 0); |
| int64_t months = 12 * interval.year + interval.month; |
| PROPAGATE_FALSE(this->template set_time_unit<TimeUnit::YEAR>(months / 12)); |
| PROPAGATE_FALSE(this->template set_time_unit<TimeUnit::MONTH>((months % 12) + 1)); |
| } |
| return true; |
| } |
| |
| template <typename T> |
| template <TimeUnit unit> |
| bool DateV2Value<T>::datetime_trunc() { |
| if constexpr (is_datetime) { |
| if (!is_valid_date()) [[unlikely]] { |
| return false; |
| } |
| switch (unit) { |
| case SECOND: { |
| date_v2_value_.microsecond_ = 0; |
| break; |
| } |
| case MINUTE: { |
| date_v2_value_.microsecond_ = 0; |
| date_v2_value_.second_ = 0; |
| break; |
| } |
| case HOUR: { |
| date_v2_value_.microsecond_ = 0; |
| date_v2_value_.second_ = 0; |
| date_v2_value_.minute_ = 0; |
| break; |
| } |
| case DAY: { |
| date_v2_value_.microsecond_ = 0; |
| date_v2_value_.second_ = 0; |
| date_v2_value_.minute_ = 0; |
| date_v2_value_.hour_ = 0; |
| break; |
| } |
| case WEEK: { |
| date_v2_value_.microsecond_ = 0; |
| date_v2_value_.second_ = 0; |
| date_v2_value_.minute_ = 0; |
| date_v2_value_.hour_ = 0; |
| TimeInterval interval(DAY, weekday(), true); |
| date_add_interval<DAY>(interval); |
| break; |
| } |
| case MONTH: { |
| date_v2_value_.microsecond_ = 0; |
| date_v2_value_.second_ = 0; |
| date_v2_value_.minute_ = 0; |
| date_v2_value_.hour_ = 0; |
| date_v2_value_.day_ = 1; |
| break; |
| } |
| case QUARTER: { |
| date_v2_value_.microsecond_ = 0; |
| date_v2_value_.second_ = 0; |
| date_v2_value_.minute_ = 0; |
| date_v2_value_.hour_ = 0; |
| date_v2_value_.day_ = 1; |
| if (date_v2_value_.month_ <= 3) { |
| date_v2_value_.month_ = 1; |
| } else if (date_v2_value_.month_ <= 6) { |
| date_v2_value_.month_ = 4; |
| } else if (date_v2_value_.month_ <= 9) { |
| date_v2_value_.month_ = 7; |
| } else { |
| date_v2_value_.month_ = 10; |
| } |
| break; |
| } |
| case YEAR: { |
| date_v2_value_.microsecond_ = 0; |
| date_v2_value_.second_ = 0; |
| date_v2_value_.minute_ = 0; |
| date_v2_value_.hour_ = 0; |
| date_v2_value_.day_ = 1; |
| date_v2_value_.month_ = 1; |
| break; |
| } |
| default: |
| return false; |
| } |
| } else { // is_datev2 |
| if (!is_valid_date()) [[unlikely]] { |
| return false; |
| } |
| switch (unit) { |
| case SECOND: |
| case MINUTE: |
| case HOUR: |
| case DAY: |
| break; |
| case WEEK: { |
| TimeInterval interval(DAY, weekday(), true); |
| date_add_interval<DAY>(interval); |
| break; |
| } |
| case MONTH: { |
| date_v2_value_.day_ = 1; |
| break; |
| } |
| case QUARTER: { |
| date_v2_value_.day_ = 1; |
| if (date_v2_value_.month_ <= 3) { |
| date_v2_value_.month_ = 1; |
| } else if (date_v2_value_.month_ <= 6) { |
| date_v2_value_.month_ = 4; |
| } else if (date_v2_value_.month_ <= 9) { |
| date_v2_value_.month_ = 7; |
| } else { |
| date_v2_value_.month_ = 10; |
| } |
| break; |
| } |
| case YEAR: { |
| date_v2_value_.day_ = 1; |
| date_v2_value_.month_ = 1; |
| break; |
| } |
| default: |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| template <typename T> |
| bool DateV2Value<T>::unix_timestamp(int64_t* timestamp, const std::string& timezone) const { |
| cctz::time_zone ctz; |
| if (!TimezoneUtils::find_cctz_time_zone(timezone, ctz)) { |
| return false; |
| } |
| return unix_timestamp(timestamp, ctz); |
| } |
| |
| template <typename T> |
| bool DateV2Value<T>::unix_timestamp(int64_t* timestamp, const cctz::time_zone& ctz) const { |
| if constexpr (is_datetime) { |
| const auto tp = |
| cctz::convert(cctz::civil_second(date_v2_value_.year_, date_v2_value_.month_, |
| date_v2_value_.day_, date_v2_value_.hour_, |
| date_v2_value_.minute_, date_v2_value_.second_), |
| ctz); |
| *timestamp = tp.time_since_epoch().count(); |
| return true; |
| } else { |
| const auto tp = |
| cctz::convert(cctz::civil_second(date_v2_value_.year_, date_v2_value_.month_, |
| date_v2_value_.day_, 0, 0, 0), |
| ctz); |
| *timestamp = tp.time_since_epoch().count(); |
| return true; |
| } |
| } |
| |
| template <typename T> |
| bool DateV2Value<T>::unix_timestamp(std::pair<int64_t, int64_t>* timestamp, |
| const std::string& timezone) const { |
| cctz::time_zone ctz; |
| if (!TimezoneUtils::find_cctz_time_zone(timezone, ctz)) { |
| return false; |
| } |
| return unix_timestamp(timestamp, ctz); |
| } |
| |
| template <typename T> |
| bool DateV2Value<T>::unix_timestamp(std::pair<int64_t, int64_t>* timestamp, |
| const cctz::time_zone& ctz) const { |
| DCHECK(is_datetime) << "Function unix_timestamp with double_t timestamp only support " |
| "datetimev2 value type."; |
| if constexpr (is_datetime) { |
| const auto tp = |
| cctz::convert(cctz::civil_second(date_v2_value_.year_, date_v2_value_.month_, |
| date_v2_value_.day_, date_v2_value_.hour_, |
| date_v2_value_.minute_, date_v2_value_.second_), |
| ctz); |
| timestamp->first = tp.time_since_epoch().count(); |
| timestamp->second = date_v2_value_.microsecond_; |
| } else { // just make compiler happy |
| } |
| return true; |
| } |
| |
| template <typename T> |
| bool DateV2Value<T>::from_unixtime(int64_t timestamp, const std::string& timezone) { |
| cctz::time_zone ctz; |
| if (!TimezoneUtils::find_cctz_time_zone(timezone, ctz)) { |
| return false; |
| } |
| from_unixtime(timestamp, ctz); |
| return true; |
| } |
| |
| template <typename T> |
| void DateV2Value<T>::from_unixtime(int64_t timestamp, const cctz::time_zone& ctz) { |
| static const cctz::time_point<cctz::sys_seconds> epoch = |
| std::chrono::time_point_cast<cctz::sys_seconds>( |
| std::chrono::system_clock::from_time_t(0)); |
| cctz::time_point<cctz::sys_seconds> t = epoch + cctz::seconds(timestamp); |
| const auto tp = cctz::convert(t, ctz); |
| |
| // there's no overflow check since it's hot path |
| |
| unchecked_set_time(tp.year(), tp.month(), tp.day(), tp.hour(), tp.minute(), tp.second(), 0); |
| } |
| |
| template <typename T> |
| bool DateV2Value<T>::from_unixtime(std::pair<int64_t, int64_t> timestamp, |
| const std::string& timezone) { |
| cctz::time_zone ctz; |
| if (!TimezoneUtils::find_cctz_time_zone(timezone, ctz)) { |
| return false; |
| } |
| from_unixtime(timestamp, ctz); |
| return true; |
| } |
| |
| template <typename T> |
| void DateV2Value<T>::from_unixtime(std::pair<int64_t, int64_t> timestamp, |
| const cctz::time_zone& ctz) { |
| static const cctz::time_point<cctz::sys_seconds> epoch = |
| std::chrono::time_point_cast<cctz::sys_seconds>( |
| std::chrono::system_clock::from_time_t(0)); |
| cctz::time_point<cctz::sys_seconds> t = epoch + cctz::seconds(timestamp.first); |
| |
| const auto tp = cctz::convert(t, ctz); |
| |
| unchecked_set_time(tp.year(), tp.month(), tp.day(), tp.hour(), tp.minute(), tp.second(), |
| timestamp.second); |
| } |
| |
| template <typename T> |
| bool DateV2Value<T>::from_unixtime(int64_t timestamp, int32_t nano_seconds, |
| const std::string& timezone, const int scale) { |
| cctz::time_zone ctz; |
| if (!TimezoneUtils::find_cctz_time_zone(timezone, ctz)) { |
| return false; |
| } |
| from_unixtime(timestamp, nano_seconds, ctz, scale); |
| return true; |
| } |
| |
| template <typename T> |
| void DateV2Value<T>::from_unixtime(int64_t timestamp, int32_t nano_seconds, |
| const cctz::time_zone& ctz, int scale) { |
| static const cctz::time_point<cctz::sys_seconds> epoch = |
| std::chrono::time_point_cast<cctz::sys_seconds>( |
| std::chrono::system_clock::from_time_t(0)); |
| cctz::time_point<cctz::sys_seconds> t = epoch + cctz::seconds(timestamp); |
| |
| const auto tp = cctz::convert(t, ctz); |
| |
| if (scale > 6) [[unlikely]] { |
| scale = 6; |
| } |
| |
| unchecked_set_time(tp.year(), tp.month(), tp.day(), tp.hour(), tp.minute(), tp.second(), |
| nano_seconds / int_exp10(9 - scale) * int_exp10(6 - scale)); |
| } |
| |
| template <typename T> |
| const char* DateV2Value<T>::month_name() const { |
| if (date_v2_value_.month_ < 1 || date_v2_value_.month_ > 12) { |
| return nullptr; |
| } |
| return s_month_name[date_v2_value_.month_]; |
| } |
| |
| template <typename T> |
| const char* DateV2Value<T>::day_name() const { |
| int day = weekday(); |
| if (day < 0 || day >= 7) { |
| return nullptr; |
| } |
| return s_day_name[day]; |
| } |
| |
| template <typename T> |
| void DateV2Value<T>::unchecked_set_time(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, |
| uint8_t minute, uint16_t second, uint32_t microsecond) { |
| date_v2_value_.year_ = year; |
| date_v2_value_.month_ = month; |
| date_v2_value_.day_ = day; |
| if constexpr (is_datetime) { |
| date_v2_value_.hour_ = hour; |
| date_v2_value_.minute_ = minute; |
| date_v2_value_.second_ = second; |
| date_v2_value_.microsecond_ = microsecond; |
| } |
| } |
| |
| template <typename T> |
| void DateV2Value<T>::unchecked_set_time(uint8_t hour, uint8_t minute, uint16_t second, |
| uint32_t microsecond) { |
| if constexpr (is_datetime) { |
| date_v2_value_.hour_ = hour; |
| date_v2_value_.minute_ = minute; |
| date_v2_value_.second_ = second; |
| date_v2_value_.microsecond_ = microsecond; |
| } else { |
| throw Exception(Status::FatalError("Invalid operation 'set_time' for date!")); |
| } |
| } |
| |
| template <typename T> |
| void DateV2Value<T>::set_microsecond(uint64_t microsecond) { |
| if constexpr (is_datetime) { |
| date_v2_value_.microsecond_ = microsecond; |
| } else { |
| throw Exception(Status::FatalError("Invalid operation 'set_microsecond' for date!")); |
| } |
| } |
| |
| template <typename T> |
| bool DateV2Value<T>::to_format_string_conservative(const char* format, size_t len, char* to, |
| size_t max_valid_length) const { |
| if (is_invalid(year(), month(), day(), hour(), minute(), second(), microsecond())) { |
| return false; |
| } |
| char* const begin = to; // to check written bytes |
| char buf[64]; |
| char* pos = nullptr; |
| char* cursor = buf; |
| const char* ptr = format; |
| const char* end = format + len; |
| char ch = '\0'; |
| |
| while (ptr < end) { |
| if (to - begin + SAFE_FORMAT_STRING_MARGIN > max_valid_length) [[unlikely]] { |
| return false; |
| } |
| if (*ptr != '%' || (ptr + 1) == end) { |
| *to++ = *ptr++; |
| continue; |
| } |
| // Skip '%' |
| ptr++; |
| switch (ch = *ptr++) { |
| case 'y': |
| // Year, numeric (two digits) |
| to = write_two_digits_to_string(this->year() % 100, to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'Y': |
| // Year, numeric, four digits |
| to = write_four_digits_to_string(this->year(), to); |
| cursor += 4; |
| pos = cursor; |
| break; |
| case 'd': |
| // Day of month (00...31) |
| to = write_two_digits_to_string(this->day(), to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'H': |
| to = write_two_digits_to_string(this->hour(), to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'i': |
| // Minutes, numeric (00..59) |
| to = write_two_digits_to_string(this->minute(), to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'm': |
| to = write_two_digits_to_string(this->month(), to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'h': |
| case 'I': |
| // Hour (01..12) |
| to = write_two_digits_to_string((this->hour() % 24 + 11) % 12 + 1, to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 's': |
| case 'S': |
| // Seconds (00..59) |
| to = write_two_digits_to_string(this->second(), to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'a': |
| // Abbreviated weekday name |
| if (this->year() == 0 && this->month() == 0) { |
| return false; |
| } |
| to = append_string(s_ab_day_name[weekday()], to); |
| break; |
| case 'b': |
| // Abbreviated month name |
| if (this->month() == 0) { |
| return false; |
| } |
| to = append_string(s_ab_month_name[this->month()], to); |
| break; |
| case 'c': |
| // Month, numeric (0...12) |
| pos = int_to_str(this->month(), cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 1, to); |
| break; |
| case 'D': |
| // Day of the month with English suffix (0th, 1st, ...) |
| pos = int_to_str(this->day(), cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 1, to); |
| if (this->day() >= 10 && this->day() <= 19) { |
| to = append_string("th", to); |
| } else { |
| switch (this->day() % 10) { |
| case 1: |
| to = append_string("st", to); |
| break; |
| case 2: |
| to = append_string("nd", to); |
| break; |
| case 3: |
| to = append_string("rd", to); |
| break; |
| default: |
| to = append_string("th", to); |
| break; |
| } |
| } |
| break; |
| case 'e': |
| // Day of the month, numeric (0..31) |
| pos = int_to_str(this->day(), cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 1, to); |
| break; |
| case 'f': |
| // Microseconds (000000..999999) |
| pos = int_to_str(this->microsecond(), cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 6, to); |
| break; |
| case 'j': |
| // Day of year (001..366) |
| pos = int_to_str(daynr() - doris::calc_daynr(this->year(), 1, 1) + 1, cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 3, to); |
| break; |
| case 'k': |
| // Hour (0..23) |
| pos = int_to_str(this->hour(), cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 1, to); |
| break; |
| case 'l': |
| // Hour (1..12) |
| pos = int_to_str((this->hour() % 24 + 11) % 12 + 1, cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 1, to); |
| break; |
| case 'M': |
| // Month name (January..December) |
| if (this->month() == 0) { |
| return false; |
| } |
| to = append_string(s_month_name[this->month()], to); |
| break; |
| case 'p': |
| // AM or PM |
| if ((this->hour() % 24) >= 12) { |
| to = append_string("PM", to); |
| } else { |
| to = append_string("AM", to); |
| } |
| break; |
| case 'r': { |
| // Time, 12-hour (hh:mm:ss followed by AM or PM) |
| *to++ = (char)('0' + (((this->hour() + 11) % 12 + 1) / 10)); |
| *to++ = (char)('0' + (((this->hour() + 11) % 12 + 1) % 10)); |
| *to++ = ':'; |
| // Minute |
| *to++ = (char)('0' + (this->minute() / 10)); |
| *to++ = (char)('0' + (this->minute() % 10)); |
| *to++ = ':'; |
| /* Second */ |
| *to++ = (char)('0' + (this->second() / 10)); |
| *to++ = (char)('0' + (this->second() % 10)); |
| if ((this->hour() % 24) >= 12) { |
| to = append_string(" PM", to); |
| } else { |
| to = append_string(" AM", to); |
| } |
| break; |
| } |
| case 'T': { |
| // Time, 24-hour (hh:mm:ss) |
| *to++ = (char)('0' + ((this->hour() % 24) / 10)); |
| *to++ = (char)('0' + ((this->hour() % 24) % 10)); |
| *to++ = ':'; |
| // Minute |
| *to++ = (char)('0' + (this->minute() / 10)); |
| *to++ = (char)('0' + (this->minute() % 10)); |
| *to++ = ':'; |
| /* Second */ |
| *to++ = (char)('0' + (this->second() / 10)); |
| *to++ = (char)('0' + (this->second() % 10)); |
| break; |
| } |
| case 'u': |
| // Week (00..53), where Monday is the first day of the week; |
| // WEEK() mode 1 |
| to = write_two_digits_to_string(week(mysql_week_mode(1)), to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'U': |
| // Week (00..53), where Sunday is the first day of the week; |
| // WEEK() mode 0 |
| to = write_two_digits_to_string(week(mysql_week_mode(0)), to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'v': |
| // Week (01..53), where Monday is the first day of the week; |
| // WEEK() mode 3; used with %x |
| to = write_two_digits_to_string(week(mysql_week_mode(3)), to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'V': |
| // Week (01..53), where Sunday is the first day of the week; |
| // WEEK() mode 2; used with %X |
| to = write_two_digits_to_string(week(mysql_week_mode(2)), to); |
| cursor += 2; |
| pos = cursor; |
| break; |
| case 'w': |
| // Day of the week (0=Sunday..6=Saturday) |
| if (this->month() == 0 && this->year() == 0) { |
| return false; |
| } |
| pos = int_to_str(doris::calc_weekday(daynr(), true), cursor); |
| to = append_with_prefix(cursor, pos - cursor, '0', 1, to); |
| break; |
| case 'W': |
| // Weekday name (Sunday..Saturday) |
| to = append_string(s_day_name[weekday()], to); |
| break; |
| case 'x': { |
| // Year for the week, where Monday is the first day of the week, |
| // numeric, four digits; used with %v |
| uint16_t year = 0; |
| calc_week(this->daynr(), this->year(), this->month(), this->day(), mysql_week_mode(3), |
| &year, true); |
| to = write_four_digits_to_string(year, to); |
| cursor += 4; |
| pos = cursor; |
| break; |
| } |
| case 'X': { |
| // Year for the week where Sunday is the first day of the week, |
| // numeric, four digits; used with %V |
| uint16_t year = 0; |
| calc_week(this->daynr(), this->year(), this->month(), this->day(), mysql_week_mode(2), |
| &year); |
| to = write_four_digits_to_string(year, to); |
| cursor += 4; |
| pos = cursor; |
| break; |
| } |
| default: |
| // put it literal |
| *to++ = ch; |
| break; |
| } |
| } |
| *to++ = '\0'; |
| return true; |
| } |
| |
| template <typename T> |
| int64_t DateV2Value<T>::standardize_timevalue(int64_t value) { |
| if (value <= 0) { |
| return 0; |
| } |
| if (value >= 10000101000000L) { |
| // 9999-99-99 99:99:99 |
| if (value > 99999999999999L) { |
| return 0; |
| } |
| |
| // between 1000-01-01 00:00:00L and 9999-99-99 99:99:99 |
| // all digits exist. |
| return value; |
| } |
| // 2000-01-01 |
| if (value < 101) { |
| return 0; |
| } |
| // two digits year. 2000 ~ 2069 |
| if (value <= (YY_PART_YEAR - 1) * 10000L + 1231L) { |
| return (value + 20000000L) * 1000000L; |
| } |
| // two digits year, invalid date |
| if (value < YY_PART_YEAR * 10000L + 101) { |
| return 0; |
| } |
| // two digits year. 1970 ~ 1999 |
| if (value <= 991231L) { |
| return (value + 19000000L) * 1000000L; |
| } |
| if (value < 10000101) { |
| return 0; |
| } |
| // four digits years without hour. |
| if (value <= 99991231L) { |
| return value * 1000000L; |
| } |
| // below 0000-01-01 |
| if (value < 101000000) { |
| return 0; |
| } |
| |
| // below is with datetime, must have hh:mm:ss |
| // 2000 ~ 2069 |
| if (value <= (YY_PART_YEAR - 1) * 10000000000L + 1231235959L) { |
| return value + 20000000000000L; |
| } |
| if (value < YY_PART_YEAR * 10000000000L + 101000000L) { |
| return 0; |
| } |
| // 1970 ~ 1999 |
| if (value <= 991231235959L) { |
| return value + 19000000000000L; |
| } |
| return value; |
| } |
| |
| template <typename T> |
| bool DateV2Value<T>::from_date_int64(int64_t value) { |
| value = standardize_timevalue(value); |
| if (value <= 0) { |
| return false; |
| } |
| uint64_t date = value / 1000000; |
| |
| auto [year, month, day, hour, minute, second] = std::tuple {0, 0, 0, 0, 0, 0}; |
| year = date / 10000; |
| date %= 10000; |
| month = date / 100; |
| day = date % 100; |
| |
| if constexpr (is_datetime) { |
| uint64_t time = value % 1000000; |
| hour = time / 10000; |
| time %= 10000; |
| minute = time / 100; |
| second = time % 100; |
| return check_range_and_set_time(year, month, day, hour, minute, second, 0); |
| } else { |
| return check_range_and_set_time(year, month, day, 0, 0, 0, 0); |
| } |
| } |
| |
| // An ISO week-numbering year (also called ISO year informally) has 52 or 53 full weeks. That is 364 or 371 days instead of the usual 365 or 366 days. These 53-week years occur on all years that have Thursday as 1 January and on leap years that start on Wednesday. The extra week is sometimes referred to as a leap week, although ISO 8601 does not use this term. https://en.wikipedia.org/wiki/ISO_week_date |
| template <typename T> |
| uint16_t DateV2Value<T>::year_of_week() const { |
| constexpr uint8_t THURSDAY = 3; |
| |
| if (date_v2_value_.month_ == 1) { |
| constexpr uint8_t MAX_DISTANCE_WITH_THURSDAY = 6 - THURSDAY; |
| if (date_v2_value_.day_ <= MAX_DISTANCE_WITH_THURSDAY) { |
| auto weekday = calc_weekday(daynr(), false); |
| // if the current day is after Thursday and Thursday is in the previous year, return the previous year |
| return date_v2_value_.year_ - |
| (weekday > THURSDAY && weekday - THURSDAY > date_v2_value_.day_ - 1); |
| } |
| } else if (date_v2_value_.month_ == 12) { |
| constexpr uint8_t MAX_DISTANCE_WITH_THURSDAY = THURSDAY - 0; |
| if (S_DAYS_IN_MONTH[12] - date_v2_value_.day_ <= MAX_DISTANCE_WITH_THURSDAY) { |
| auto weekday = calc_weekday(daynr(), false); |
| // if the current day is before Thursday and Thursday is in the next year, return the next year |
| return date_v2_value_.year_ + |
| (weekday < THURSDAY && |
| (THURSDAY - weekday) > S_DAYS_IN_MONTH[12] - date_v2_value_.day_); |
| } |
| } |
| return date_v2_value_.year_; |
| } |
| |
| template <typename T> |
| uint8_t DateV2Value<T>::calc_week(const uint32_t& day_nr, const uint16_t& year, |
| const uint8_t& month, const uint8_t& day, uint8_t mode, |
| uint16_t* to_year, bool disable_lut) { |
| if (config::enable_time_lut && !disable_lut && mode == 3 && year >= 1950 && year < 2030) { |
| return doris::TimeLUT::GetImplement() |
| ->week_of_year_table[year - doris::LUT_START_YEAR][month - 1][day - 1]; |
| } |
| // mode=4 is used for week() |
| if (config::enable_time_lut && !disable_lut && mode == 4 && year >= 1950 && year < 2030) { |
| return doris::TimeLUT::GetImplement() |
| ->week_table[year - doris::LUT_START_YEAR][month - 1][day - 1]; |
| } |
| bool monday_first = mode & WEEK_MONDAY_FIRST; |
| bool week_year = mode & WEEK_YEAR; |
| bool first_weekday = mode & WEEK_FIRST_WEEKDAY; |
| uint64_t daynr_first_day = doris::calc_daynr(year, 1, 1); |
| uint8_t weekday_first_day = doris::calc_weekday(daynr_first_day, !monday_first); |
| |
| int days = 0; |
| *to_year = year; |
| |
| // Check weather the first days of this year belongs to last year |
| if (month == 1 && day <= (7 - weekday_first_day)) { |
| if (!week_year && ((first_weekday && weekday_first_day != 0) || |
| (!first_weekday && weekday_first_day > 3))) { |
| return 0; |
| } |
| (*to_year)--; |
| week_year = true; |
| daynr_first_day -= (days = doris::calc_days_in_year(*to_year)); |
| weekday_first_day = (weekday_first_day + 53 * 7 - days) % 7; |
| } |
| |
| // How many days since first week |
| if ((first_weekday && weekday_first_day != 0) || (!first_weekday && weekday_first_day > 3)) { |
| // days in new year belongs to last year. |
| days = day_nr - (daynr_first_day + (7 - weekday_first_day)); |
| } else { |
| // days in new year belongs to this year. |
| days = day_nr - (daynr_first_day - weekday_first_day); |
| } |
| |
| if (week_year && days >= 52 * 7) { |
| weekday_first_day = (weekday_first_day + doris::calc_days_in_year(*to_year)) % 7; |
| if ((first_weekday && weekday_first_day == 0) || |
| (!first_weekday && weekday_first_day <= 3)) { |
| // Belong to next year. |
| (*to_year)++; |
| return 1; |
| } |
| } |
| |
| return days / 7 + 1; |
| } |
| |
| template <typename T> |
| std::ostream& operator<<(std::ostream& os, const DateV2Value<T>& value) { |
| char buf[30]; |
| value.to_string(buf); |
| return os << buf; |
| } |
| |
| // NOTE: |
| // only support DATE - DATE (no support DATETIME - DATETIME) |
| template <typename T0, typename T1> |
| std::size_t operator-(const DateV2Value<T0>& v1, const DateV2Value<T1>& v2) { |
| return v1.daynr() - v2.daynr(); |
| } |
| |
| template <typename T> |
| std::size_t hash_value(DateV2Value<T> const& value) { |
| return HashUtil::hash(&value, sizeof(DateV2Value<T>), 0); |
| } |
| |
| template class DateV2Value<DateV2ValueType>; |
| template class DateV2Value<DateTimeV2ValueType>; |
| |
| template std::size_t hash_value<DateV2ValueType>(DateV2Value<DateV2ValueType> const& value); |
| template std::size_t hash_value<DateTimeV2ValueType>(DateV2Value<DateTimeV2ValueType> const& value); |
| |
| template std::ostream& operator<<(std::ostream& os, const DateV2Value<DateV2ValueType>& value); |
| template std::ostream& operator<<(std::ostream& os, const DateV2Value<DateTimeV2ValueType>& value); |
| |
| template std::size_t operator-(const VecDateTimeValue& v1, const DateV2Value<DateV2ValueType>& v2); |
| template std::size_t operator-(const VecDateTimeValue& v1, |
| const DateV2Value<DateTimeV2ValueType>& v2); |
| |
| template std::size_t operator-(const DateV2Value<DateV2ValueType>& v1, const VecDateTimeValue& v2); |
| template std::size_t operator-(const DateV2Value<DateTimeV2ValueType>& v1, |
| const VecDateTimeValue& v2); |
| |
| template std::size_t operator-(const DateV2Value<DateV2ValueType>& v1, |
| const DateV2Value<DateV2ValueType>& v2); |
| template std::size_t operator-(const DateV2Value<DateV2ValueType>& v1, |
| const DateV2Value<DateTimeV2ValueType>& v2); |
| template std::size_t operator-(const DateV2Value<DateTimeV2ValueType>& v1, |
| const DateV2Value<DateV2ValueType>& v2); |
| template std::size_t operator-(const DateV2Value<DateTimeV2ValueType>& v1, |
| const DateV2Value<DateTimeV2ValueType>& v2); |
| |
| template void VecDateTimeValue::create_from_date_v2<DateV2ValueType>( |
| DateV2Value<DateV2ValueType>& value, TimeType type); |
| template void VecDateTimeValue::create_from_date_v2<DateV2ValueType>( |
| DateV2Value<DateV2ValueType>&& value, TimeType type); |
| template void VecDateTimeValue::create_from_date_v2<DateTimeV2ValueType>( |
| DateV2Value<DateTimeV2ValueType>& value, TimeType type); |
| template void VecDateTimeValue::create_from_date_v2<DateTimeV2ValueType>( |
| DateV2Value<DateTimeV2ValueType>&& value, TimeType type); |
| |
| template int64_t VecDateTimeValue::datetime_diff_in_seconds<DateV2Value<DateV2ValueType>>( |
| const DateV2Value<DateV2ValueType>& rhs) const; |
| template int64_t VecDateTimeValue::datetime_diff_in_seconds<DateV2Value<DateTimeV2ValueType>>( |
| const DateV2Value<DateTimeV2ValueType>& rhs) const; |
| |
| #define DELARE_DATE_ADD_INTERVAL(DateValueType1, DateValueType2) \ |
| template bool \ |
| DateV2Value<DateValueType1>::date_add_interval<TimeUnit::MICROSECOND, DateValueType2>( \ |
| TimeInterval const&, DateV2Value<DateValueType2>&); \ |
| template bool \ |
| DateV2Value<DateValueType1>::date_add_interval<TimeUnit::MILLISECOND, DateValueType2>( \ |
| TimeInterval const&, DateV2Value<DateValueType2>&); \ |
| template bool \ |
| DateV2Value<DateValueType1>::date_add_interval<TimeUnit::SECOND, DateValueType2>( \ |
| TimeInterval const&, DateV2Value<DateValueType2>&); \ |
| template bool \ |
| DateV2Value<DateValueType1>::date_add_interval<TimeUnit::MINUTE, DateValueType2>( \ |
| TimeInterval const&, DateV2Value<DateValueType2>&); \ |
| template bool DateV2Value<DateValueType1>::date_add_interval<TimeUnit::HOUR, DateValueType2>( \ |
| TimeInterval const&, DateV2Value<DateValueType2>&); \ |
| template bool DateV2Value<DateValueType1>::date_add_interval<TimeUnit::DAY, DateValueType2>( \ |
| TimeInterval const&, DateV2Value<DateValueType2>&); \ |
| template bool DateV2Value<DateValueType1>::date_add_interval<TimeUnit::MONTH, DateValueType2>( \ |
| TimeInterval const&, DateV2Value<DateValueType2>&); \ |
| template bool DateV2Value<DateValueType1>::date_add_interval<TimeUnit::YEAR, DateValueType2>( \ |
| TimeInterval const&, DateV2Value<DateValueType2>&); \ |
| template bool \ |
| DateV2Value<DateValueType1>::date_add_interval<TimeUnit::QUARTER, DateValueType2>( \ |
| TimeInterval const&, DateV2Value<DateValueType2>&); \ |
| template bool DateV2Value<DateValueType1>::date_add_interval<TimeUnit::WEEK, DateValueType2>( \ |
| TimeInterval const&, DateV2Value<DateValueType2>&); |
| |
| DELARE_DATE_ADD_INTERVAL(DateV2ValueType, DateV2ValueType) |
| DELARE_DATE_ADD_INTERVAL(DateV2ValueType, DateTimeV2ValueType) |
| DELARE_DATE_ADD_INTERVAL(DateTimeV2ValueType, DateV2ValueType) |
| DELARE_DATE_ADD_INTERVAL(DateTimeV2ValueType, DateTimeV2ValueType) |
| |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::SECOND>(const TimeInterval& interval); |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::MINUTE>(const TimeInterval& interval); |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::HOUR>(const TimeInterval& interval); |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::DAY>(const TimeInterval& interval); |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::MONTH>(const TimeInterval& interval); |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::YEAR>(const TimeInterval& interval); |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::QUARTER>(const TimeInterval& interval); |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::WEEK>(const TimeInterval& interval); |
| |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::SECOND, false>( |
| const TimeInterval& interval); |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::MINUTE, false>( |
| const TimeInterval& interval); |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::HOUR, false>( |
| const TimeInterval& interval); |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::DAY, false>( |
| const TimeInterval& interval); |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::MONTH, false>( |
| const TimeInterval& interval); |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::YEAR, false>( |
| const TimeInterval& interval); |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::QUARTER, false>( |
| const TimeInterval& interval); |
| template bool VecDateTimeValue::date_add_interval<TimeUnit::WEEK, false>( |
| const TimeInterval& interval); |
| |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::MICROSECOND>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::MILLISECOND>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::SECOND>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::MINUTE>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::HOUR>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::DAY>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::MONTH>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::YEAR>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::QUARTER>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::WEEK>( |
| const TimeInterval& interval); |
| |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::MICROSECOND>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::MILLISECOND>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::SECOND>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::MINUTE>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::HOUR>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::DAY>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::MONTH>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::YEAR>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::QUARTER>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::WEEK>( |
| const TimeInterval& interval); |
| |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::MICROSECOND, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::SECOND, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::MINUTE, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::HOUR, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::DAY, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::MONTH, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::YEAR, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::QUARTER, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_add_interval<TimeUnit::WEEK, false>( |
| const TimeInterval& interval); |
| |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::MICROSECOND, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::SECOND, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::MINUTE, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::HOUR, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::DAY, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::MONTH, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::YEAR, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::QUARTER, false>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_add_interval<TimeUnit::WEEK, false>( |
| const TimeInterval& interval); |
| |
| template bool VecDateTimeValue::date_set_interval<TimeUnit::SECOND>(const TimeInterval& interval); |
| template bool VecDateTimeValue::date_set_interval<TimeUnit::MINUTE>(const TimeInterval& interval); |
| template bool VecDateTimeValue::date_set_interval<TimeUnit::HOUR>(const TimeInterval& interval); |
| template bool VecDateTimeValue::date_set_interval<TimeUnit::DAY>(const TimeInterval& interval); |
| template bool VecDateTimeValue::date_set_interval<TimeUnit::MONTH>(const TimeInterval& interval); |
| template bool VecDateTimeValue::date_set_interval<TimeUnit::YEAR>(const TimeInterval& interval); |
| |
| template bool DateV2Value<DateV2ValueType>::date_set_interval<TimeUnit::SECOND>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_set_interval<TimeUnit::MINUTE>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_set_interval<TimeUnit::HOUR>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_set_interval<TimeUnit::DAY>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_set_interval<TimeUnit::MONTH>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateV2ValueType>::date_set_interval<TimeUnit::YEAR>( |
| const TimeInterval& interval); |
| |
| template bool DateV2Value<DateTimeV2ValueType>::date_set_interval<TimeUnit::SECOND>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_set_interval<TimeUnit::MINUTE>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_set_interval<TimeUnit::HOUR>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_set_interval<TimeUnit::DAY>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_set_interval<TimeUnit::MONTH>( |
| const TimeInterval& interval); |
| template bool DateV2Value<DateTimeV2ValueType>::date_set_interval<TimeUnit::YEAR>( |
| const TimeInterval& interval); |
| |
| template bool VecDateTimeValue::datetime_trunc<TimeUnit::SECOND>(); |
| template bool VecDateTimeValue::datetime_trunc<TimeUnit::MINUTE>(); |
| template bool VecDateTimeValue::datetime_trunc<TimeUnit::HOUR>(); |
| template bool VecDateTimeValue::datetime_trunc<TimeUnit::DAY>(); |
| template bool VecDateTimeValue::datetime_trunc<TimeUnit::MONTH>(); |
| template bool VecDateTimeValue::datetime_trunc<TimeUnit::YEAR>(); |
| template bool VecDateTimeValue::datetime_trunc<TimeUnit::QUARTER>(); |
| template bool VecDateTimeValue::datetime_trunc<TimeUnit::WEEK>(); |
| |
| template bool DateV2Value<DateV2ValueType>::datetime_trunc<TimeUnit::SECOND>(); |
| template bool DateV2Value<DateV2ValueType>::datetime_trunc<TimeUnit::MINUTE>(); |
| template bool DateV2Value<DateV2ValueType>::datetime_trunc<TimeUnit::HOUR>(); |
| template bool DateV2Value<DateV2ValueType>::datetime_trunc<TimeUnit::DAY>(); |
| template bool DateV2Value<DateV2ValueType>::datetime_trunc<TimeUnit::MONTH>(); |
| template bool DateV2Value<DateV2ValueType>::datetime_trunc<TimeUnit::YEAR>(); |
| template bool DateV2Value<DateV2ValueType>::datetime_trunc<TimeUnit::QUARTER>(); |
| template bool DateV2Value<DateV2ValueType>::datetime_trunc<TimeUnit::WEEK>(); |
| |
| template bool DateV2Value<DateTimeV2ValueType>::datetime_trunc<TimeUnit::MICROSECOND>(); |
| template bool DateV2Value<DateTimeV2ValueType>::datetime_trunc<TimeUnit::SECOND>(); |
| template bool DateV2Value<DateTimeV2ValueType>::datetime_trunc<TimeUnit::MINUTE>(); |
| template bool DateV2Value<DateTimeV2ValueType>::datetime_trunc<TimeUnit::HOUR>(); |
| template bool DateV2Value<DateTimeV2ValueType>::datetime_trunc<TimeUnit::DAY>(); |
| template bool DateV2Value<DateTimeV2ValueType>::datetime_trunc<TimeUnit::MONTH>(); |
| template bool DateV2Value<DateTimeV2ValueType>::datetime_trunc<TimeUnit::YEAR>(); |
| template bool DateV2Value<DateTimeV2ValueType>::datetime_trunc<TimeUnit::QUARTER>(); |
| template bool DateV2Value<DateTimeV2ValueType>::datetime_trunc<TimeUnit::WEEK>(); |
| |
| } // namespace doris |