// 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 #include #include #include #include #include #include // IWYU pragma: no_include #include #include // IWYU pragma: keep // IWYU pragma: no_include #include #include #include #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 constexpr int s_days_in_month[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 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 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, int len) { return from_date_str_base(date_str, len, nullptr); } //parse timezone to get offset bool VecDateTimeValue::from_date_str(const char* date_str, int 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 && isspace(*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(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 && (ispunct(*ptr) || isspace(*ptr))) { if (isspace(*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( 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(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; } 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; } 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 full_len, char* to) { int len = (str_len > full_len) ? str_len : full_len; len -= str_len; while (len-- > 0) { // push prefix; *to++ = prefix; } while (str_len-- > 0) { *to++ = *str++; } return to; } int VecDateTimeValue::compute_format_len(const char* format, int 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(const char* format, int len, char* to) const { if (check_range(_year, _month, _day, _hour, _minute, _second, _type)) { return false; } 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 (*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: *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 bool VecDateTimeValue::operator>=(const DateV2Value& 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 bool VecDateTimeValue::operator<=(const DateV2Value& 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 bool VecDateTimeValue::operator>(const DateV2Value& 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 bool VecDateTimeValue::operator<(const DateV2Value& 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 bool VecDateTimeValue::operator==(const DateV2Value& 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, int 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 && isspace(*val)) { val++; } if (val >= val_end) { break; } // 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(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(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 (!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 & 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 bool VecDateTimeValue::date_add_interval(const TimeInterval& interval) { if constexpr (need_check) { if (!is_valid_date()) { 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 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 epoch = std::chrono::time_point_cast( std::chrono::system_clock::from_time_t(0)); cctz::time_point 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::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 bool VecDateTimeValue::datetime_trunc() { if (!is_valid_date()) { 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(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 void VecDateTimeValue::create_from_date_v2(DateV2Value& value, TimeType type) { if constexpr (std::is_same_v) { this->set_time(value.year(), value.month(), value.day(), 0, 0, 0); } else { this->set_time(value.year(), value.month(), value.day(), value.hour(), value.minute(), value.second()); } this->set_type(type); this->_neg = 0; } template void VecDateTimeValue::create_from_date_v2(DateV2Value&& value, TimeType type) { DateV2Value 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 std::size_t operator-(const DateV2Value& v1, const VecDateTimeValue& v2) { return v1.daynr() - v2.daynr(); } template std::size_t operator-(const VecDateTimeValue& v1, const DateV2Value& v2) { return v1.daynr() - v2.daynr(); } std::size_t hash_value(VecDateTimeValue const& value) { return HashUtil::hash(&value, sizeof(VecDateTimeValue), 0); } template bool DateV2Value::is_invalid(uint32_t year, uint32_t month, uint32_t day, uint8_t hour, uint8_t minute, uint8_t second, uint32_t microsecond, bool only_time_part) { if (hour >= 24 || minute >= 60 || second >= 60 || microsecond > 999999) { return true; } if (only_time_part) { return false; } if (year < MIN_YEAR || year > MAX_YEAR) { return true; } if (month == 2 && day == 29 && doris::is_leap(year)) return false; if (month == 0 || month > 12 || day > s_days_in_month[month] || day == 0) { return true; } return false; } template void DateV2Value::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 bool DateV2Value::from_date_str(const char* date_str, int len, int scale /* = -1*/) { return from_date_str_base(date_str, len, scale, nullptr); } // when we parse template bool DateV2Value::from_date_str(const char* date_str, int len, const cctz::time_zone& local_time_zone, int scale /* = -1*/) { return from_date_str_base(date_str, len, scale, &local_time_zone); } template bool DateV2Value::from_date_str_base(const char* date_str, int len, int scale, 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; uint32_t date_val[MAX_DATE_PARTS] = {0}; int32_t date_len[MAX_DATE_PARTS] = {0}; // Skip space character while (ptr < end && isspace(*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; 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 (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(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; } 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(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++; // 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 && (ispunct(*ptr) || isspace(*ptr))) { if (isspace(*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)) { return false; } // 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 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( 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( TimeInterval {TimeUnit::SECOND, sec_offset, false}) : true; } template void DateV2Value::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 bool DateV2Value::from_date_format_str(const char* format, int format_len, const char* value, int 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 && isspace(*val)) { val++; } if (val >= val_end) { break; } // 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(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(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 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 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 int32_t DateV2Value::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()) { 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 char* DateV2Value::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, date_day_offset_dict::DICT_DAYS> date_day_offset_dict::DATE_DAY_OFFSET_ITEMS; // [1900-01-01, 2039-12-31] std::array, 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::get() { return instance; } bool date_day_offset_dict::get_dict_init() { return DATE_DAY_OFFSET_ITEMS_INIT; } date_day_offset_dict::date_day_offset_dict() { DateV2Value d; // Init days before epoch. d.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.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; } int date_day_offset_dict::daynr(int year, int month, int day) const { return DATE_DAY_OFFSET_DICT[year - START_YEAR][month - 1][day - 1]; } template uint32_t DateV2Value::set_date_uint32(uint32_t int_val) { union DateV2UInt32Union { DateV2Value dt; uint32_t ui32; ~DateV2UInt32Union() {} }; DateV2UInt32Union conv = {.ui32 = int_val}; if (is_invalid(conv.dt.year(), conv.dt.month(), conv.dt.day(), 0, 0, 0, 0)) { return 0; } this->set_time(conv.dt.year(), conv.dt.month(), conv.dt.day(), 0, 0, 0, 0); return int_val; } template uint64_t DateV2Value::set_datetime_uint64(uint64_t int_val) { union DateTimeV2UInt64Union { DateV2Value dt; uint64_t ui64; ~DateTimeV2UInt64Union() {} }; DateTimeV2UInt64Union conv = {.ui64 = int_val}; if (is_invalid(conv.dt.year(), conv.dt.month(), conv.dt.day(), conv.dt.hour(), conv.dt.minute(), conv.dt.second(), conv.dt.microsecond())) { return 0; } this->set_time(conv.dt.year(), conv.dt.month(), conv.dt.day(), conv.dt.hour(), conv.dt.minute(), conv.dt.second(), conv.dt.microsecond()); return int_val; } template uint8_t DateV2Value::week(uint8_t mode) const { uint16_t year = 0; return calc_week(this->daynr(), this->year(), this->month(), this->day(), mode, &year); } template uint32_t DateV2Value::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 bool DateV2Value::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; } } set_time(year, month, day, this->hour(), this->minute(), this->second(), this->microsecond()); return true; } template template bool DateV2Value::date_add_interval(const TimeInterval& interval, DateV2Value& to_value) { if (!is_valid_date()) 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; } to_value.set_time(seconds / 3600, (seconds / 60) % 60, seconds % 60, microseconds); } else if constexpr (unit == YEAR) { // This only change year information to_value.template set_time_unit(date_v2_value_.year_ + interval.year); if (to_value.year() > 9999) { return false; } if (date_v2_value_.month_ == 2 && date_v2_value_.day_ == 29 && !doris::is_leap(to_value.year())) { to_value.template set_time_unit(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; to_value.template set_time_unit(months / 12); if (months < 0) { return false; } if (to_value.year() > MAX_YEAR) { return false; } to_value.template set_time_unit((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())) { to_value.template set_time_unit(date_v2_value_.day_ + 1); } } } return true; } template template bool DateV2Value::date_add_interval(const TimeInterval& interval) { if constexpr (need_check) { if (!is_valid_date()) 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) { this->set_time(seconds / 3600, (seconds / 60) % 60, seconds % 60, microseconds); } } else if constexpr (unit == YEAR) { // This only change year information this->template set_time_unit(date_v2_value_.year_ + interval.year); if (this->year() > 9999) { return false; } if (date_v2_value_.month_ == 2 && date_v2_value_.day_ == 29 && !doris::is_leap(this->year())) { this->template set_time_unit(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; this->template set_time_unit(months / 12); if (months < 0) { return false; } if (this->year() > MAX_YEAR) { return false; } this->template set_time_unit((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())) { this->template set_time_unit(date_v2_value_.day_ + 1); } } } return true; } template template bool DateV2Value::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) { this->set_time(seconds / 3600, (seconds / 60) % 60, seconds % 60, 0); } } else if constexpr (unit == YEAR) { this->set_time(0, 1, 1, 0, 0, 0, 0); this->template set_time_unit(interval.year); } else if constexpr (unit == MONTH) { // This will change month and year information, maybe date. this->set_time(0, 1, 1, 0, 0, 0, 0); int64_t months = 12 * interval.year + interval.month; this->template set_time_unit(months / 12); this->template set_time_unit((months % 12) + 1); } return true; } template template bool DateV2Value::datetime_trunc() { if constexpr (is_datetime) { if (!is_valid_date()) { 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(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()) { return false; } switch (unit) { case SECOND: case MINUTE: case HOUR: case DAY: break; case WEEK: { TimeInterval interval(DAY, weekday(), true); date_add_interval(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 bool DateV2Value::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 bool DateV2Value::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 bool DateV2Value::unix_timestamp(std::pair* 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 bool DateV2Value::unix_timestamp(std::pair* 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 bool DateV2Value::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 void DateV2Value::from_unixtime(int64_t timestamp, const cctz::time_zone& ctz) { static const cctz::time_point epoch = std::chrono::time_point_cast( std::chrono::system_clock::from_time_t(0)); cctz::time_point t = epoch + cctz::seconds(timestamp); const auto tp = cctz::convert(t, ctz); // there's no overflow check since it's hot path set_time(tp.year(), tp.month(), tp.day(), tp.hour(), tp.minute(), tp.second(), 0); } template bool DateV2Value::from_unixtime(std::pair 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 void DateV2Value::from_unixtime(std::pair timestamp, const cctz::time_zone& ctz) { static const cctz::time_point epoch = std::chrono::time_point_cast( std::chrono::system_clock::from_time_t(0)); cctz::time_point t = epoch + cctz::seconds(timestamp.first); const auto tp = cctz::convert(t, ctz); set_time(tp.year(), tp.month(), tp.day(), tp.hour(), tp.minute(), tp.second(), timestamp.second); } template bool DateV2Value::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 void DateV2Value::from_unixtime(int64_t timestamp, int32_t nano_seconds, const cctz::time_zone& ctz, int scale) { static const cctz::time_point epoch = std::chrono::time_point_cast( std::chrono::system_clock::from_time_t(0)); cctz::time_point t = epoch + cctz::seconds(timestamp); const auto tp = cctz::convert(t, ctz); if (scale > 6) [[unlikely]] { scale = 6; } 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 const char* DateV2Value::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 const char* DateV2Value::day_name() const { int day = weekday(); if (day < 0 || day >= 7) { return nullptr; } return s_day_name[day]; } template void DateV2Value::set_time(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_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 void DateV2Value::set_time(uint8_t hour, uint8_t minute, uint8_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 { LOG(FATAL) << "Invalid operation 'set_time' for date!"; } } template void DateV2Value::set_microsecond(uint32_t microsecond) { if constexpr (is_datetime) { date_v2_value_.microsecond_ = microsecond; } else { LOG(FATAL) << "Invalid operation 'set_microsecond' for date!"; } } template bool DateV2Value::to_format_string(const char* format, int len, char* to) const { if (is_invalid(year(), month(), day(), hour(), minute(), second(), microsecond())) { return false; } 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 (*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: *to++ = ch; break; } } *to++ = '\0'; return true; } template bool DateV2Value::from_date(uint32_t value) { DCHECK(!is_datetime); if (value < MIN_DATE_V2 || value > MAX_DATE_V2) { return false; } return set_date_uint32(value); } template bool DateV2Value::from_datetime(uint64_t value) { DCHECK(is_datetime); if (value < MIN_DATETIME_V2 || value > MAX_DATETIME_V2) { return false; } return set_datetime_uint64(value); } template int64_t DateV2Value::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 bool DateV2Value::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); } } template uint8_t DateV2Value::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 std::ostream& operator<<(std::ostream& os, const DateV2Value& value) { char buf[30]; value.to_string(buf); return os << buf; } // NOTE: // only support DATE - DATE (no support DATETIME - DATETIME) template std::size_t operator-(const DateV2Value& v1, const DateV2Value& v2) { return v1.daynr() - v2.daynr(); } template std::size_t hash_value(DateV2Value const& value) { return HashUtil::hash(&value, sizeof(DateV2Value), 0); } template class DateV2Value; template class DateV2Value; template std::size_t hash_value(DateV2Value const& value); template std::size_t hash_value(DateV2Value const& value); template std::ostream& operator<<(std::ostream& os, const DateV2Value& value); template std::ostream& operator<<(std::ostream& os, const DateV2Value& value); template std::size_t operator-(const VecDateTimeValue& v1, const DateV2Value& v2); template std::size_t operator-(const VecDateTimeValue& v1, const DateV2Value& v2); template std::size_t operator-(const DateV2Value& v1, const VecDateTimeValue& v2); template std::size_t operator-(const DateV2Value& v1, const VecDateTimeValue& v2); template std::size_t operator-(const DateV2Value& v1, const DateV2Value& v2); template std::size_t operator-(const DateV2Value& v1, const DateV2Value& v2); template std::size_t operator-(const DateV2Value& v1, const DateV2Value& v2); template std::size_t operator-(const DateV2Value& v1, const DateV2Value& v2); template void VecDateTimeValue::create_from_date_v2( DateV2Value& value, TimeType type); template void VecDateTimeValue::create_from_date_v2( DateV2Value&& value, TimeType type); template void VecDateTimeValue::create_from_date_v2( DateV2Value& value, TimeType type); template void VecDateTimeValue::create_from_date_v2( DateV2Value&& value, TimeType type); template int64_t VecDateTimeValue::second_diff>( const DateV2Value& rhs) const; template int64_t VecDateTimeValue::second_diff>( const DateV2Value& rhs) const; #define DELARE_DATE_ADD_INTERVAL(DateValueType1, DateValueType2) \ template bool \ DateV2Value::date_add_interval( \ TimeInterval const&, DateV2Value&); \ template bool \ DateV2Value::date_add_interval( \ TimeInterval const&, DateV2Value&); \ template bool \ DateV2Value::date_add_interval( \ TimeInterval const&, DateV2Value&); \ template bool \ DateV2Value::date_add_interval( \ TimeInterval const&, DateV2Value&); \ template bool DateV2Value::date_add_interval( \ TimeInterval const&, DateV2Value&); \ template bool DateV2Value::date_add_interval( \ TimeInterval const&, DateV2Value&); \ template bool DateV2Value::date_add_interval( \ TimeInterval const&, DateV2Value&); \ template bool DateV2Value::date_add_interval( \ TimeInterval const&, DateV2Value&); \ template bool \ DateV2Value::date_add_interval( \ TimeInterval const&, DateV2Value&); \ template bool DateV2Value::date_add_interval( \ TimeInterval const&, DateV2Value&); 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(const TimeInterval& interval); template bool VecDateTimeValue::date_add_interval(const TimeInterval& interval); template bool VecDateTimeValue::date_add_interval(const TimeInterval& interval); template bool VecDateTimeValue::date_add_interval(const TimeInterval& interval); template bool VecDateTimeValue::date_add_interval(const TimeInterval& interval); template bool VecDateTimeValue::date_add_interval(const TimeInterval& interval); template bool VecDateTimeValue::date_add_interval(const TimeInterval& interval); template bool VecDateTimeValue::date_add_interval(const TimeInterval& interval); template bool VecDateTimeValue::date_add_interval( const TimeInterval& interval); template bool VecDateTimeValue::date_add_interval( const TimeInterval& interval); template bool VecDateTimeValue::date_add_interval( const TimeInterval& interval); template bool VecDateTimeValue::date_add_interval( const TimeInterval& interval); template bool VecDateTimeValue::date_add_interval( const TimeInterval& interval); template bool VecDateTimeValue::date_add_interval( const TimeInterval& interval); template bool VecDateTimeValue::date_add_interval( const TimeInterval& interval); template bool VecDateTimeValue::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool DateV2Value::date_add_interval( const TimeInterval& interval); template bool VecDateTimeValue::date_set_interval(const TimeInterval& interval); template bool VecDateTimeValue::date_set_interval(const TimeInterval& interval); template bool VecDateTimeValue::date_set_interval(const TimeInterval& interval); template bool VecDateTimeValue::date_set_interval(const TimeInterval& interval); template bool VecDateTimeValue::date_set_interval(const TimeInterval& interval); template bool VecDateTimeValue::date_set_interval(const TimeInterval& interval); template bool DateV2Value::date_set_interval( const TimeInterval& interval); template bool DateV2Value::date_set_interval( const TimeInterval& interval); template bool DateV2Value::date_set_interval( const TimeInterval& interval); template bool DateV2Value::date_set_interval( const TimeInterval& interval); template bool DateV2Value::date_set_interval( const TimeInterval& interval); template bool DateV2Value::date_set_interval( const TimeInterval& interval); template bool DateV2Value::date_set_interval( const TimeInterval& interval); template bool DateV2Value::date_set_interval( const TimeInterval& interval); template bool DateV2Value::date_set_interval( const TimeInterval& interval); template bool DateV2Value::date_set_interval( const TimeInterval& interval); template bool DateV2Value::date_set_interval( const TimeInterval& interval); template bool DateV2Value::date_set_interval( const TimeInterval& interval); template bool VecDateTimeValue::datetime_trunc(); template bool VecDateTimeValue::datetime_trunc(); template bool VecDateTimeValue::datetime_trunc(); template bool VecDateTimeValue::datetime_trunc(); template bool VecDateTimeValue::datetime_trunc(); template bool VecDateTimeValue::datetime_trunc(); template bool VecDateTimeValue::datetime_trunc(); template bool VecDateTimeValue::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); template bool DateV2Value::datetime_trunc(); } // namespace doris