2023-02-23 11:56:03 +00:00
|
|
|
#include <Columns/ColumnString.h>
|
|
|
|
#include <Columns/ColumnsDateTime.h>
|
2023-02-22 10:05:53 +00:00
|
|
|
#include <DataTypes/DataTypeDateTime.h>
|
2023-02-23 11:56:03 +00:00
|
|
|
#include <DataTypes/DataTypeString.h>
|
2023-02-22 10:05:53 +00:00
|
|
|
|
|
|
|
#include <Functions/FunctionFactory.h>
|
|
|
|
#include <Functions/FunctionHelpers.h>
|
|
|
|
#include <Functions/FunctionsConversion.h>
|
|
|
|
#include <Functions/IFunction.h>
|
|
|
|
#include <Functions/castTypeToEither.h>
|
2023-03-05 08:24:05 +00:00
|
|
|
#include <Functions/numLiteralChars.h>
|
2023-02-22 10:05:53 +00:00
|
|
|
|
|
|
|
#include <IO/WriteHelpers.h>
|
2023-02-24 09:07:27 +00:00
|
|
|
#include <base/types.h>
|
2023-03-05 08:24:05 +00:00
|
|
|
#include <boost/algorithm/string/case_conv.hpp>
|
2023-02-22 10:05:53 +00:00
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
2023-03-01 12:24:50 +00:00
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int LOGICAL_ERROR;
|
|
|
|
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
|
|
|
extern const int ILLEGAL_COLUMN;
|
|
|
|
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
|
|
|
extern const int NOT_IMPLEMENTED;
|
|
|
|
extern const int BAD_ARGUMENTS;
|
2023-03-05 08:24:05 +00:00
|
|
|
extern const int VALUE_IS_OUT_OF_RANGE_OF_DATA_TYPE;
|
|
|
|
extern const int CANNOT_PARSE_TEXT;
|
2023-03-01 12:24:50 +00:00
|
|
|
}
|
|
|
|
|
2023-02-22 10:05:53 +00:00
|
|
|
namespace
|
|
|
|
{
|
2023-02-27 13:41:38 +00:00
|
|
|
using Pos = const char *;
|
|
|
|
|
2023-03-01 03:02:41 +00:00
|
|
|
constexpr Int32 minYear = 1970;
|
|
|
|
constexpr Int32 maxYear = 2106;
|
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
const std::unordered_map<String, std::pair<String, Int32>> dayOfWeekMap{
|
|
|
|
{"mon", {"day", 1}},
|
|
|
|
{"tue", {"sday", 2}},
|
|
|
|
{"wed", {"nesday", 3}},
|
|
|
|
{"thu", {"rsday", 4}},
|
|
|
|
{"fri", {"day", 5}},
|
|
|
|
{"sat", {"urday", 6}},
|
|
|
|
{"sun", {"day", 7}},
|
|
|
|
};
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
const std::unordered_map<String, std::pair<String, Int32>> monthMap{
|
|
|
|
{"jan", {"uary", 1}},
|
|
|
|
{"feb", {"ruary", 2}},
|
|
|
|
{"mar", {"rch", 3}},
|
|
|
|
{"apr", {"il", 4}},
|
|
|
|
{"may", {"", 5}},
|
|
|
|
{"jun", {"e", 6}},
|
|
|
|
{"jul", {"y", 7}},
|
|
|
|
{"aug", {"ust", 8}},
|
|
|
|
{"sep", {"tember", 9}},
|
|
|
|
{"oct", {"ober", 10}},
|
|
|
|
{"nov", {"ember", 11}},
|
|
|
|
{"dec", {"ember", 12}},
|
|
|
|
};
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-01 03:02:41 +00:00
|
|
|
/// key: month, value: total days of current month if current year is leap year.
|
2023-02-27 13:41:38 +00:00
|
|
|
constexpr Int32 leapDays[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
2023-03-01 03:02:41 +00:00
|
|
|
|
|
|
|
/// key: month, value: total days of current month if current year is not leap year.
|
2023-02-27 13:41:38 +00:00
|
|
|
constexpr Int32 normalDays[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
|
|
|
|
2023-03-01 12:24:50 +00:00
|
|
|
/// key: month, value: cumulative days from January to current month(inclusive) if current year is leap year.
|
2023-02-27 13:41:38 +00:00
|
|
|
constexpr Int32 cumulativeLeapDays[] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366};
|
2023-03-01 03:02:41 +00:00
|
|
|
|
2023-03-01 12:24:50 +00:00
|
|
|
/// key: month, value: cumulative days from January to current month(inclusive) if current year is not leap year.
|
2023-02-27 13:41:38 +00:00
|
|
|
constexpr Int32 cumulativeDays[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
|
2023-03-01 03:02:41 +00:00
|
|
|
|
|
|
|
/// key: year, value: cumulative days from epoch(1970-01-01) to the first day of current year(exclusive).
|
2023-02-27 13:41:38 +00:00
|
|
|
constexpr Int32 cumulativeYearDays[]
|
|
|
|
= {0, 365, 730, 1096, 1461, 1826, 2191, 2557, 2922, 3287, 3652, 4018, 4383, 4748, 5113, 5479, 5844, 6209,
|
|
|
|
6574, 6940, 7305, 7670, 8035, 8401, 8766, 9131, 9496, 9862, 10227, 10592, 10957, 11323, 11688, 12053, 12418, 12784,
|
|
|
|
13149, 13514, 13879, 14245, 14610, 14975, 15340, 15706, 16071, 16436, 16801, 17167, 17532, 17897, 18262, 18628, 18993, 19358,
|
|
|
|
19723, 20089, 20454, 20819, 21184, 21550, 21915, 22280, 22645, 23011, 23376, 23741, 24106, 24472, 24837, 25202, 25567, 25933,
|
|
|
|
26298, 26663, 27028, 27394, 27759, 28124, 28489, 28855, 29220, 29585, 29950, 30316, 30681, 31046, 31411, 31777, 32142, 32507,
|
|
|
|
32872, 33238, 33603, 33968, 34333, 34699, 35064, 35429, 35794, 36160, 36525, 36890, 37255, 37621, 37986, 38351, 38716, 39082,
|
|
|
|
39447, 39812, 40177, 40543, 40908, 41273, 41638, 42004, 42369, 42734, 43099, 43465, 43830, 44195, 44560, 44926, 45291, 45656,
|
|
|
|
46021, 46387, 46752, 47117, 47482, 47847, 48212, 48577, 48942, 49308, 49673};
|
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
struct DateTime
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
|
|
|
Int32 year = 1970;
|
|
|
|
Int32 month = 1;
|
|
|
|
Int32 day = 1;
|
2023-03-01 09:15:29 +00:00
|
|
|
std::vector<Int32> day_of_month_values;
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
Int32 week = 1; // Week of year based on ISO week date, e.g: 27
|
|
|
|
Int32 day_of_week = 1; // Day of week, Monday:1, Tuesday:2, ..., Sunday:7
|
|
|
|
bool week_date_format = false;
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
Int32 day_of_year = 1;
|
2023-03-01 09:15:29 +00:00
|
|
|
std::vector<Int32> day_of_year_values;
|
2023-02-27 13:41:38 +00:00
|
|
|
bool day_of_year_format = false;
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
bool century_format = false;
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
bool is_year_of_era = false; // Year of era cannot be zero or negative.
|
|
|
|
bool has_year = false; // Whether year was explicitly specified.
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
Int32 hour = 0;
|
|
|
|
Int32 minute = 0;
|
|
|
|
Int32 second = 0;
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-01 09:15:29 +00:00
|
|
|
bool is_am = true; // AM -> true, PM -> false
|
2023-02-27 13:41:38 +00:00
|
|
|
bool is_clock_hour = false; // Whether most recent hour specifier is clockhour
|
|
|
|
bool is_hour_of_half_day = false; // Whether most recent hour specifier is of half day.
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-01 09:15:29 +00:00
|
|
|
std::optional<Int64> time_zone_offset;
|
|
|
|
|
|
|
|
void reset()
|
|
|
|
{
|
|
|
|
year = 1970;
|
|
|
|
month = 1;
|
|
|
|
day = 1;
|
|
|
|
day_of_month_values.clear();
|
|
|
|
|
|
|
|
week = 1;
|
|
|
|
day_of_week = 1;
|
|
|
|
week_date_format = false;
|
|
|
|
|
|
|
|
day_of_year = 1;
|
|
|
|
day_of_year_values.clear();
|
|
|
|
day_of_year_format = false;
|
|
|
|
|
|
|
|
century_format = false;
|
|
|
|
|
|
|
|
is_year_of_era = false; // Year of era cannot be zero or negative.
|
|
|
|
has_year = false; // Whether year was explicitly specified.
|
|
|
|
|
|
|
|
hour = 0;
|
|
|
|
minute = 0;
|
|
|
|
second = 0;
|
2023-03-03 02:42:35 +00:00
|
|
|
|
2023-03-01 09:15:29 +00:00
|
|
|
is_am = true; // AM -> true, PM -> false
|
|
|
|
is_clock_hour = false; // Whether most recent hour specifier is clockhour
|
|
|
|
is_hour_of_half_day = false; // Whether most recent hour specifier is of half day.
|
|
|
|
|
|
|
|
time_zone_offset.reset();
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
void setCentury(Int32 century)
|
2023-02-28 15:02:02 +00:00
|
|
|
{
|
|
|
|
if (century < 19 || century > 21)
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Value {} for century must be in the range [19, 21]", century);
|
|
|
|
|
|
|
|
century_format = true;
|
|
|
|
year = 100 * century;
|
|
|
|
has_year = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setDayOfWeek(Int32 day_of_week_)
|
|
|
|
{
|
|
|
|
if (day_of_week_ < 1 || day_of_week_ > 7)
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Value {} for day of week must be in the range [1, 7]", day_of_week_);
|
|
|
|
|
|
|
|
day_of_week = day_of_week_;
|
|
|
|
week_date_format = true;
|
|
|
|
day_of_year_format = false;
|
|
|
|
if (!has_year)
|
|
|
|
{
|
|
|
|
has_year = true;
|
|
|
|
year = 2000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void setMonth(Int32 month_)
|
|
|
|
{
|
|
|
|
if (month_ < 1 || month_ > 12)
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Value {} for month of year must be in the range [1, 12]", month_);
|
|
|
|
|
|
|
|
month = month_;
|
|
|
|
week_date_format = false;
|
|
|
|
day_of_year_format = false;
|
|
|
|
if (!has_year)
|
|
|
|
{
|
|
|
|
has_year = true;
|
|
|
|
year = 2000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-01 09:15:29 +00:00
|
|
|
ALWAYS_INLINE void appendDayOfMonth(Int32 day_of_month)
|
2023-02-28 15:02:02 +00:00
|
|
|
{
|
|
|
|
if (day_of_month < 1 || day_of_month > 31)
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Value {} for day of month must be in the range [1, 31]", day_of_month);
|
|
|
|
|
|
|
|
day_of_month_values.push_back(day_of_month);
|
|
|
|
day = day_of_month;
|
|
|
|
week_date_format = false;
|
|
|
|
day_of_year_format = false;
|
|
|
|
if (!has_year)
|
|
|
|
{
|
|
|
|
has_year = true;
|
|
|
|
year = 2000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-01 09:15:29 +00:00
|
|
|
ALWAYS_INLINE void appendDayOfYear(Int32 day_of_year_)
|
2023-02-28 15:02:02 +00:00
|
|
|
{
|
|
|
|
if (day_of_year_ < 1 || day_of_year_ > 366)
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Value {} for day of year must be in the range [1, 366]", day_of_year_);
|
|
|
|
|
|
|
|
day_of_year_values.push_back(day_of_year_);
|
|
|
|
day_of_year = day_of_year_;
|
|
|
|
day_of_year_format = true;
|
|
|
|
week_date_format = false;
|
|
|
|
if (!has_year)
|
|
|
|
{
|
|
|
|
has_year = true;
|
|
|
|
year = 2000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void setYear2(Int32 year_, bool is_year_of_era_ = false, bool is_week_year = false)
|
|
|
|
{
|
|
|
|
if (year_ >= 70 && year_ < 100)
|
|
|
|
year_ += 1900;
|
|
|
|
else if (year_ >= 0 && year_ < 70)
|
|
|
|
year_ += 2000;
|
|
|
|
else
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Value {} for year2 must be in the range [0, 99]", year_);
|
|
|
|
|
|
|
|
setYear(year_, is_year_of_era_, is_week_year);
|
|
|
|
}
|
|
|
|
|
|
|
|
void setYear(Int32 year_, bool is_year_of_era_ = false, bool is_week_year = false)
|
|
|
|
{
|
|
|
|
if (year_ < minYear || year_ > maxYear)
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Value {} for year must be in the range [{}, {}]", year_, minYear, maxYear);
|
|
|
|
|
|
|
|
year = year_;
|
|
|
|
century_format = false;
|
|
|
|
has_year = true;
|
|
|
|
is_year_of_era = is_year_of_era_;
|
|
|
|
if (is_week_year)
|
|
|
|
{
|
|
|
|
week_date_format = true;
|
|
|
|
day_of_year_format = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void setWeek(Int32 week_)
|
|
|
|
{
|
|
|
|
if (week_ < 1 || week_ > 53)
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Value {} for week of week year must be in the range [1, 53]", week_);
|
|
|
|
|
|
|
|
week = week_;
|
|
|
|
week_date_format = true;
|
|
|
|
day_of_year_format = false;
|
|
|
|
if (!has_year)
|
|
|
|
{
|
|
|
|
has_year = true;
|
|
|
|
year = 2000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void setMinute(Int32 minute_)
|
|
|
|
{
|
|
|
|
if (minute_ < 0 || minute_ > 59)
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Value {} for minute must be in the range [0, 59]", minute_);
|
|
|
|
|
|
|
|
minute = minute_;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setSecond(Int32 second_)
|
|
|
|
{
|
|
|
|
if (second_ < 0 || second_ > 59)
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Value {} for second must be in the range [0, 59]", second_);
|
|
|
|
|
|
|
|
second = second_;
|
|
|
|
}
|
|
|
|
|
2023-03-05 08:24:05 +00:00
|
|
|
void setEra(String & text) // NOLINT
|
2023-02-28 15:02:02 +00:00
|
|
|
{
|
2023-03-05 08:24:05 +00:00
|
|
|
boost::to_lower(text);
|
|
|
|
if (text == "bc")
|
|
|
|
throw Exception(ErrorCodes::VALUE_IS_OUT_OF_RANGE_OF_DATA_TYPE, "Era BC exceeds the range of DateTime");
|
|
|
|
else if (text != "ad")
|
2023-02-28 15:02:02 +00:00
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Unknown era {}", text);
|
|
|
|
}
|
|
|
|
|
2023-03-01 09:15:29 +00:00
|
|
|
ALWAYS_INLINE void setAMPM(String & text)
|
2023-02-28 15:02:02 +00:00
|
|
|
{
|
2023-03-05 08:24:05 +00:00
|
|
|
boost::to_lower(text);
|
2023-02-28 15:02:02 +00:00
|
|
|
if (text == "am")
|
|
|
|
is_am = true;
|
|
|
|
else if (text == "pm")
|
|
|
|
is_am = false;
|
|
|
|
else
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Unknown half day of day: {}", text);
|
|
|
|
}
|
|
|
|
|
2023-03-01 09:15:29 +00:00
|
|
|
ALWAYS_INLINE void setHour(Int32 hour_, bool is_hour_of_half_day_ = false, bool is_clock_hour_ = false)
|
2023-02-28 15:02:02 +00:00
|
|
|
{
|
|
|
|
Int32 max_hour;
|
|
|
|
Int32 min_hour;
|
2023-03-01 03:02:41 +00:00
|
|
|
Int32 new_hour = hour_;
|
2023-02-28 15:02:02 +00:00
|
|
|
if (!is_hour_of_half_day_ && !is_clock_hour_)
|
|
|
|
{
|
|
|
|
max_hour = 23;
|
|
|
|
min_hour = 0;
|
|
|
|
}
|
|
|
|
else if (!is_hour_of_half_day_ && is_clock_hour_)
|
|
|
|
{
|
|
|
|
max_hour = 24;
|
|
|
|
min_hour = 1;
|
|
|
|
new_hour = hour_ % 24;
|
|
|
|
}
|
|
|
|
else if (is_hour_of_half_day_ && !is_clock_hour_)
|
|
|
|
{
|
|
|
|
max_hour = 11;
|
|
|
|
min_hour = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
max_hour = 12;
|
|
|
|
min_hour = 1;
|
|
|
|
new_hour = hour_ % 12;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hour_ < min_hour || hour_ > max_hour)
|
|
|
|
throw Exception(
|
|
|
|
ErrorCodes::LOGICAL_ERROR,
|
|
|
|
"Value {} for hour must be in the range [{}, {}] if_hour_of_half_day={} and is_clock_hour={}",
|
|
|
|
hour,
|
|
|
|
max_hour,
|
|
|
|
min_hour,
|
|
|
|
is_hour_of_half_day_,
|
|
|
|
is_clock_hour_);
|
|
|
|
|
|
|
|
hour = new_hour;
|
|
|
|
is_hour_of_half_day = is_hour_of_half_day_;
|
|
|
|
is_clock_hour = is_clock_hour_;
|
|
|
|
}
|
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
/// For debug
|
|
|
|
[[maybe_unused]] String toString() const
|
|
|
|
{
|
|
|
|
String res;
|
|
|
|
res += "year:" + std::to_string(year);
|
|
|
|
res += ",";
|
|
|
|
res += "month:" + std::to_string(month);
|
|
|
|
res += ",";
|
|
|
|
res += "day:" + std::to_string(day);
|
|
|
|
res += ",";
|
|
|
|
res += "hour:" + std::to_string(hour);
|
|
|
|
res += ",";
|
|
|
|
res += "minute:" + std::to_string(minute);
|
|
|
|
res += ",";
|
|
|
|
res += "second:" + std::to_string(second);
|
2023-02-28 06:52:13 +00:00
|
|
|
res += ",";
|
|
|
|
res += "AM:" + std::to_string(is_am);
|
2023-02-27 13:41:38 +00:00
|
|
|
return res;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
static bool isLeapYear(Int32 year_) { return year_ % 4 == 0 && (year_ % 100 != 0 || year_ % 400 == 0); }
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
static bool isDateValid(Int32 year_, Int32 month_, Int32 day_)
|
|
|
|
{
|
2023-03-01 09:15:29 +00:00
|
|
|
/// The range of month[1, 12] and day[1, 31] already checked before
|
2023-02-27 13:41:38 +00:00
|
|
|
bool leap = isLeapYear(year_);
|
2023-03-01 09:15:29 +00:00
|
|
|
return (year_ >= minYear && year_ <= maxYear) && ((leap && day_ <= leapDays[month_]) || (!leap && day_ <= normalDays[month_]));
|
2023-02-27 13:41:38 +00:00
|
|
|
}
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
static bool isDayOfYearValid(Int32 year_, Int32 day_of_year_)
|
|
|
|
{
|
2023-03-01 09:15:29 +00:00
|
|
|
/// The range of day_of_year[1, 366] already checked before
|
|
|
|
bool leap = isLeapYear(year_);
|
|
|
|
return (year_ >= minYear && year_ <= maxYear) && (day_of_year_ <= 365 + (leap ? 1 : 0));
|
2023-02-27 13:41:38 +00:00
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
static Int32 extractISODayOfTheWeek(Int32 days_since_epoch)
|
|
|
|
{
|
|
|
|
if (days_since_epoch < 0)
|
|
|
|
{
|
|
|
|
// negative date: start off at 4 and cycle downwards
|
2023-03-01 09:15:29 +00:00
|
|
|
return (7 - ((-days_since_epoch + 3) % 7));
|
2023-02-27 13:41:38 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// positive date: start off at 4 and cycle upwards
|
2023-03-01 09:15:29 +00:00
|
|
|
return ((days_since_epoch + 3) % 7) + 1;
|
2023-02-27 13:41:38 +00:00
|
|
|
}
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-01 09:15:29 +00:00
|
|
|
static ALWAYS_INLINE Int32 daysSinceEpochFromWeekDate(int32_t week_year_, int32_t week_of_year_, int32_t day_of_week_)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-01 09:15:29 +00:00
|
|
|
/// The range of week_of_year[1, 53], day_of_week[1, 7] already checked before
|
|
|
|
if (week_year_ < minYear || week_year_ > maxYear)
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Invalid week year {}", week_year_);
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
Int32 days_since_epoch_of_jan_fourth = daysSinceEpochFromDate(week_year_, 1, 4);
|
|
|
|
Int32 first_day_of_week_year = extractISODayOfTheWeek(days_since_epoch_of_jan_fourth);
|
|
|
|
return days_since_epoch_of_jan_fourth - (first_day_of_week_year - 1) + 7 * (week_of_year_ - 1) + day_of_week_ - 1;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-01 09:15:29 +00:00
|
|
|
static ALWAYS_INLINE Int32 daysSinceEpochFromDayOfYear(Int32 year_, Int32 day_of_year_)
|
2023-02-23 11:56:03 +00:00
|
|
|
{
|
2023-02-27 13:41:38 +00:00
|
|
|
if (!isDayOfYearValid(year_, day_of_year_))
|
2023-02-28 09:27:59 +00:00
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Invalid day of year, year:{} day of year:{}", year_, day_of_year_);
|
2023-02-27 13:41:38 +00:00
|
|
|
|
|
|
|
Int32 res = daysSinceEpochFromDate(year_, 1, 1);
|
|
|
|
res += day_of_year_ - 1;
|
|
|
|
return res;
|
2023-02-23 11:56:03 +00:00
|
|
|
}
|
2023-02-27 13:41:38 +00:00
|
|
|
|
2023-03-01 09:15:29 +00:00
|
|
|
static ALWAYS_INLINE Int32 daysSinceEpochFromDate(Int32 year_, Int32 month_, Int32 day_)
|
2023-02-23 11:56:03 +00:00
|
|
|
{
|
2023-02-27 13:41:38 +00:00
|
|
|
if (!isDateValid(year_, month_, day_))
|
2023-02-28 09:27:59 +00:00
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Invalid date, year:{} month:{} day:{}", year_, month_, day_);
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
Int32 res = cumulativeYearDays[year_ - 1970];
|
|
|
|
res += isLeapYear(year_) ? cumulativeLeapDays[month_ - 1] : cumulativeDays[month_ - 1];
|
|
|
|
res += day_ - 1;
|
|
|
|
return res;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
Int64 checkAndGetDateTime(const DateLUTImpl & time_zone)
|
|
|
|
{
|
|
|
|
if (is_hour_of_half_day && !is_am)
|
|
|
|
hour += 12;
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
/// Ensure all day of year values are valid for ending year value
|
|
|
|
for (const auto d : day_of_month_values)
|
|
|
|
{
|
|
|
|
if (!isDateValid(year, month, d))
|
2023-02-28 09:27:59 +00:00
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Invalid day of month, year:{} month:{} day:{}", year, month, d);
|
2023-02-27 13:41:38 +00:00
|
|
|
}
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
// Ensure all day of year values are valid for ending year value
|
|
|
|
for (const auto d : day_of_year_values)
|
|
|
|
{
|
|
|
|
if (!isDayOfYearValid(year, d))
|
2023-02-28 09:27:59 +00:00
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Invalid day of year, year:{} day of year:{}", year, d);
|
2023-02-27 13:41:38 +00:00
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
// Convert the parsed date/time into a timestamp.
|
|
|
|
Int32 days_since_epoch;
|
|
|
|
if (week_date_format)
|
|
|
|
days_since_epoch = daysSinceEpochFromWeekDate(year, week, day_of_week);
|
|
|
|
else if (day_of_year_format)
|
|
|
|
days_since_epoch = daysSinceEpochFromDayOfYear(year, day_of_year);
|
|
|
|
else
|
|
|
|
days_since_epoch = daysSinceEpochFromDate(year, month, day);
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
Int64 seconds_since_epoch = days_since_epoch * 86400 + hour * 3600 + minute * 60 + second;
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
/// Time zone is not specified, use local time zone
|
|
|
|
if (!time_zone_offset)
|
2023-03-02 08:58:34 +00:00
|
|
|
*time_zone_offset = time_zone.timezoneOffset(seconds_since_epoch);
|
2023-03-02 09:01:16 +00:00
|
|
|
// std::cout << "time_zone:" << time_zone.getTimeZone() << ",offset:" << *time_zone_offset << std::endl;
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
/// Time zone is specified in format string.
|
2023-02-28 06:08:03 +00:00
|
|
|
if (seconds_since_epoch >= *time_zone_offset)
|
|
|
|
seconds_since_epoch -= *time_zone_offset;
|
|
|
|
else
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Seconds since epoch is negative");
|
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
return seconds_since_epoch;
|
2023-02-23 11:56:03 +00:00
|
|
|
}
|
2023-02-27 13:41:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct ParseDateTimeTraits
|
|
|
|
{
|
|
|
|
enum class ParseSyntax
|
2023-02-23 11:56:03 +00:00
|
|
|
{
|
2023-02-27 13:41:38 +00:00
|
|
|
MySQL,
|
|
|
|
Joda
|
|
|
|
};
|
|
|
|
};
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
/// _FUNC_(str[, format, timezone])
|
2023-03-03 02:42:35 +00:00
|
|
|
template <typename Name, ParseDateTimeTraits::ParseSyntax parse_syntax>
|
2023-02-27 13:41:38 +00:00
|
|
|
class FunctionParseDateTimeImpl : public IFunction
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static constexpr auto name = Name::name;
|
|
|
|
static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionParseDateTimeImpl>(); }
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
String getName() const override { return name; }
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
bool useDefaultImplementationForConstants() const override { return true; }
|
|
|
|
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1, 2}; }
|
|
|
|
bool isVariadic() const override { return true; }
|
|
|
|
size_t getNumberOfArguments() const override { return 0; }
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
|
2023-02-23 11:56:03 +00:00
|
|
|
{
|
2023-02-27 13:41:38 +00:00
|
|
|
if (arguments.size() != 1 && arguments.size() != 2 && arguments.size() != 3)
|
2023-02-24 09:07:27 +00:00
|
|
|
throw Exception(
|
2023-02-27 13:41:38 +00:00
|
|
|
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH,
|
|
|
|
"Number of arguments for function {} doesn't match: passed {}, should be 1, 2 or 3",
|
|
|
|
getName(),
|
|
|
|
arguments.size());
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
if (!isString(arguments[0].type))
|
|
|
|
throw Exception(
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
2023-03-03 02:42:35 +00:00
|
|
|
"Illegal type {} of first argument of function {} when arguments size is 1. Should be String",
|
2023-02-27 13:41:38 +00:00
|
|
|
arguments[0].type->getName(),
|
|
|
|
getName());
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
if (arguments.size() > 1 && !isString(arguments[1].type))
|
|
|
|
throw Exception(
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
2023-03-03 02:42:35 +00:00
|
|
|
"Illegal type {} of second argument of function {} when arguments size is 1. Should be String",
|
2023-02-27 13:41:38 +00:00
|
|
|
arguments[0].type->getName(),
|
|
|
|
getName());
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
if (arguments.size() > 2 && !isString(arguments[2].type))
|
|
|
|
throw Exception(
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
2023-03-03 02:42:35 +00:00
|
|
|
"Illegal type {} of third argument of function {} when arguments size is 1. Should be String",
|
2023-02-27 13:41:38 +00:00
|
|
|
arguments[0].type->getName(),
|
|
|
|
getName());
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
String time_zone_name = getTimeZone(arguments).second;
|
|
|
|
return std::make_shared<DataTypeDateTime>(time_zone_name);
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
ColumnPtr
|
|
|
|
executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & /*result_type*/, size_t input_rows_count) const override
|
|
|
|
{
|
|
|
|
const auto * col_str = checkAndGetColumn<ColumnString>(arguments[0].column.get());
|
|
|
|
if (!col_str)
|
|
|
|
throw Exception(
|
|
|
|
ErrorCodes::ILLEGAL_COLUMN,
|
|
|
|
"Illegal column {} of first ('str') argument of function {}. Must be string.",
|
|
|
|
arguments[0].column->getName(),
|
|
|
|
getName());
|
|
|
|
|
|
|
|
String format = getFormat(arguments);
|
|
|
|
const auto * time_zone = getTimeZone(arguments).first;
|
|
|
|
|
2023-03-05 08:24:05 +00:00
|
|
|
std::vector<Instruction> instructions;
|
2023-02-27 13:41:38 +00:00
|
|
|
parseFormat(format, instructions);
|
|
|
|
|
|
|
|
auto col_res = ColumnDateTime::create();
|
|
|
|
col_res->reserve(input_rows_count);
|
|
|
|
auto & data_res = col_res->getData();
|
2023-03-03 02:42:35 +00:00
|
|
|
DateTime date;
|
2023-02-27 13:41:38 +00:00
|
|
|
for (size_t i = 0; i < input_rows_count; ++i)
|
|
|
|
{
|
2023-03-01 09:15:29 +00:00
|
|
|
date.reset();
|
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
StringRef str_ref = col_str->getDataAt(i);
|
|
|
|
Pos cur = str_ref.data;
|
|
|
|
Pos end = str_ref.data + str_ref.size;
|
|
|
|
for (const auto & instruction : instructions)
|
2023-03-02 08:58:34 +00:00
|
|
|
{
|
2023-03-02 09:01:16 +00:00
|
|
|
// std::cout << "instruction:" << instruction.toString() << std::endl;
|
2023-02-27 13:41:38 +00:00
|
|
|
cur = instruction.perform(cur, end, date);
|
2023-03-02 09:01:16 +00:00
|
|
|
// std::cout << "date:" << date.toString() << std::endl;
|
2023-03-02 08:58:34 +00:00
|
|
|
}
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
// Ensure all input was consumed.
|
|
|
|
if (cur < end)
|
|
|
|
throw Exception(
|
|
|
|
ErrorCodes::LOGICAL_ERROR,
|
|
|
|
"Invalid format input {} is malformed at {}",
|
|
|
|
str_ref.toView(),
|
|
|
|
std::string_view(cur, end - cur));
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
Int64 time = date.checkAndGetDateTime(*time_zone);
|
|
|
|
data_res.push_back(static_cast<UInt32>(time));
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
return col_res;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
private:
|
2023-03-05 08:24:05 +00:00
|
|
|
class Instruction
|
2023-02-22 10:05:53 +00:00
|
|
|
{
|
2023-02-27 13:41:38 +00:00
|
|
|
private:
|
2023-03-03 02:42:35 +00:00
|
|
|
enum class NeedCheckSpace
|
|
|
|
{
|
|
|
|
Yes,
|
|
|
|
No
|
|
|
|
};
|
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
using Func = std::conditional_t<
|
|
|
|
parse_syntax == ParseDateTimeTraits::ParseSyntax::MySQL,
|
2023-03-03 02:42:35 +00:00
|
|
|
Pos (*)(Pos, Pos, DateTime &),
|
|
|
|
std::function<Pos(Pos, Pos, DateTime &)>>;
|
2023-02-27 13:41:38 +00:00
|
|
|
Func func{};
|
|
|
|
std::string func_name;
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
std::string literal;
|
2023-02-22 12:42:59 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
public:
|
2023-03-05 08:24:05 +00:00
|
|
|
explicit Instruction(Func && func_, const char * func_name_) : func(std::move(func_)), func_name(func_name_) { }
|
2023-02-22 12:42:59 +00:00
|
|
|
|
2023-03-05 08:24:05 +00:00
|
|
|
explicit Instruction(const String & literal_) : literal(literal_) { }
|
|
|
|
explicit Instruction(String && literal_) : literal(std::move(literal_)) { }
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
/// For debug
|
|
|
|
[[maybe_unused]] String toString() const
|
|
|
|
{
|
|
|
|
if (func)
|
|
|
|
return "func:" + func_name;
|
|
|
|
else
|
|
|
|
return "literal:" + literal;
|
|
|
|
}
|
2023-02-22 12:42:59 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
Pos perform(Pos cur, Pos end, DateTime & date) const
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
|
|
|
if (func)
|
|
|
|
return func(cur, end, date);
|
|
|
|
else
|
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, literal.size(), "required literal size not matched");
|
2023-02-27 13:41:38 +00:00
|
|
|
if (std::string_view(cur, literal.size()) != literal)
|
|
|
|
throw Exception(
|
|
|
|
ErrorCodes::LOGICAL_ERROR, "Expect literal {} but {} provided", literal, std::string_view(cur, literal.size()));
|
|
|
|
cur += literal.size();
|
|
|
|
return cur;
|
|
|
|
}
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
template <typename T, NeedCheckSpace need_check_space>
|
2023-02-27 13:41:38 +00:00
|
|
|
static Pos readNumber2(Pos cur, Pos end, T & res)
|
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
if constexpr (need_check_space == NeedCheckSpace::Yes)
|
|
|
|
checkSpace(cur, end, 2, "readNumber2 requires size >= 2");
|
2023-03-01 09:15:29 +00:00
|
|
|
|
|
|
|
res = (*cur - '0');
|
2023-02-27 13:41:38 +00:00
|
|
|
++cur;
|
2023-03-01 09:15:29 +00:00
|
|
|
res = res * 10 + (*cur - '0');
|
2023-02-27 13:41:38 +00:00
|
|
|
++cur;
|
|
|
|
return cur;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
template <typename T, NeedCheckSpace need_check_space>
|
2023-02-27 13:41:38 +00:00
|
|
|
static Pos readNumber3(Pos cur, Pos end, T & res)
|
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
if constexpr (need_check_space == NeedCheckSpace::Yes)
|
|
|
|
checkSpace(cur, end, 3, "readNumber4 requires size >= 3");
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-01 09:15:29 +00:00
|
|
|
res = (*cur - '0');
|
|
|
|
++cur;
|
|
|
|
res = res * 10 + (*cur - '0');
|
|
|
|
++cur;
|
2023-02-27 13:41:38 +00:00
|
|
|
res = res * 10 + (*cur - '0');
|
|
|
|
++cur;
|
|
|
|
return cur;
|
|
|
|
}
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
template <typename T, NeedCheckSpace need_check_space>
|
2023-02-27 13:41:38 +00:00
|
|
|
static Pos readNumber4(Pos cur, Pos end, T & res)
|
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
if constexpr (need_check_space == NeedCheckSpace::Yes)
|
|
|
|
checkSpace(cur, end, 4, "readNumber4 requires size >= 4");
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-03-01 09:15:29 +00:00
|
|
|
res = (*cur - '0');
|
|
|
|
++cur;
|
|
|
|
res = res * 10 + (*cur - '0');
|
|
|
|
++cur;
|
|
|
|
res = res * 10 + (*cur - '0');
|
|
|
|
++cur;
|
|
|
|
res = res * 10 + (*cur - '0');
|
|
|
|
++cur;
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static void checkSpace(Pos cur, Pos end, size_t len, const String & msg)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-05 08:24:05 +00:00
|
|
|
if (cur > end || cur + len > end) [[unlikely]]
|
2023-02-27 13:41:38 +00:00
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Unable to parse because {}", msg);
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
template <NeedCheckSpace need_check_space>
|
2023-03-01 09:15:29 +00:00
|
|
|
static Pos assertChar(Pos cur, Pos end, char ch)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
if constexpr (need_check_space == NeedCheckSpace::Yes)
|
|
|
|
checkSpace(cur, end, 1, "assertChar requires size >= 1");
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
if (*cur != ch)
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Expect char {}, but {} provided", String(ch, 1), String(*cur, 1));
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
++cur;
|
|
|
|
return cur;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlDayOfWeekTextShort(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 3, "Parsing DayOfWeekTextShort requires size >= 3");
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-28 15:02:02 +00:00
|
|
|
String text(cur, 3);
|
2023-03-05 08:24:05 +00:00
|
|
|
boost::to_lower(text);
|
2023-03-01 03:02:41 +00:00
|
|
|
auto it = dayOfWeekMap.find(text);
|
|
|
|
if (it == dayOfWeekMap.end())
|
2023-03-05 08:24:05 +00:00
|
|
|
throw Exception(ErrorCodes::CANNOT_PARSE_TEXT, "Unknown day of week short text {}", text);
|
2023-03-01 03:02:41 +00:00
|
|
|
date.setDayOfWeek(it->second.second);
|
2023-02-27 13:41:38 +00:00
|
|
|
cur += 3;
|
|
|
|
return cur;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlMonthOfYearTextShort(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 3, "Parsing MonthOfYearTextShort requires size >= 3");
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-01 03:02:41 +00:00
|
|
|
String text(cur, 3);
|
2023-03-05 08:24:05 +00:00
|
|
|
boost::to_lower(text);
|
2023-03-01 03:02:41 +00:00
|
|
|
auto it = monthMap.find(text);
|
|
|
|
if (it == monthMap.end())
|
2023-03-05 08:24:05 +00:00
|
|
|
throw Exception(ErrorCodes::CANNOT_PARSE_TEXT, "Unknown month of year short text {}", text);
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-01 03:02:41 +00:00
|
|
|
date.setMonth(it->second.second);
|
2023-02-27 13:41:38 +00:00
|
|
|
cur += 3;
|
|
|
|
return cur;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlMonth(Pos cur, Pos end, DateTime & date)
|
2023-02-28 06:08:03 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 month;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::Yes>(cur, end, month);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setMonth(month);
|
2023-02-28 06:08:03 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlCentury(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 century;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::Yes>(cur, end, century);
|
|
|
|
date.setCentury(century);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlDayOfMonth(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 day_of_month;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::Yes>(cur, end, day_of_month);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.appendDayOfMonth(day_of_month);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlAmericanDate(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 8, "mysqlAmericanDate requires size >= 8");
|
2023-03-01 09:15:29 +00:00
|
|
|
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 month;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::No>(cur, end, month);
|
|
|
|
cur = assertChar<NeedCheckSpace::No>(cur, end, '/');
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setMonth(month);
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 day;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::No>(cur, end, day);
|
|
|
|
cur = assertChar<NeedCheckSpace::No>(cur, end, '/');
|
2023-02-28 15:02:02 +00:00
|
|
|
date.appendDayOfMonth(day);
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 year;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::No>(cur, end, year);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setYear(year);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlDayOfMonthSpacePadded(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 2, "mysqlDayOfMonthSpacePadded requires size >= 2");
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 day_of_month = *cur == ' ' ? 0 : (*cur - '0');
|
2023-02-27 13:41:38 +00:00
|
|
|
++cur;
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-28 15:02:02 +00:00
|
|
|
day_of_month = 10 * day_of_month + (*cur - '0');
|
2023-02-27 13:41:38 +00:00
|
|
|
++cur;
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-28 15:02:02 +00:00
|
|
|
date.appendDayOfMonth(day_of_month);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlISO8601Date(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 10, "mysqlISO8601Date requires size >= 10");
|
2023-02-27 13:41:38 +00:00
|
|
|
|
2023-03-01 09:15:29 +00:00
|
|
|
Int32 year;
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 month;
|
|
|
|
Int32 day;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber4<Int32, NeedCheckSpace::No>(cur, end, year);
|
|
|
|
cur = assertChar<NeedCheckSpace::No>(cur, end, '-');
|
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::No>(cur, end, month);
|
|
|
|
cur = assertChar<NeedCheckSpace::No>(cur, end, '-');
|
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::No>(cur, end, day);
|
2023-03-01 09:15:29 +00:00
|
|
|
|
|
|
|
date.setYear(year);
|
|
|
|
date.setMonth(month);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.appendDayOfMonth(day);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlISO8601Year2(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 year2;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::Yes>(cur, end, year2);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setYear2(year2);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlISO8601Year4(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 year;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber4<Int32, NeedCheckSpace::Yes>(cur, end, year);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setYear(year);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlDayOfYear(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 day_of_year;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber3<Int32, NeedCheckSpace::Yes>(cur, end, day_of_year);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.appendDayOfYear(day_of_year);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlDayOfWeek(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 1, "mysqlDayOfWeek requires size >= 1");
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setDayOfWeek(*cur - '0');
|
|
|
|
++cur;
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlISO8601Week(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 week;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::Yes>(cur, end, week);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setWeek(week);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlDayOfWeek0To6(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 1, "mysqlDayOfWeek requires size >= 1");
|
2023-02-28 15:02:02 +00:00
|
|
|
|
|
|
|
Int32 day_of_week = *cur - '0';
|
|
|
|
if (day_of_week == 0)
|
|
|
|
day_of_week = 7;
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setDayOfWeek(day_of_week);
|
|
|
|
++cur;
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlDayOfWeekTextLong(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 6, "jodaDayOfWeekText requires the size >= 6");
|
2023-02-28 15:02:02 +00:00
|
|
|
String text1(cur, 3);
|
2023-03-05 08:24:05 +00:00
|
|
|
boost::to_lower(text1);
|
2023-02-28 15:02:02 +00:00
|
|
|
auto it = dayOfWeekMap.find(text1);
|
|
|
|
if (it == dayOfWeekMap.end())
|
2023-03-05 08:24:05 +00:00
|
|
|
throw Exception(ErrorCodes::CANNOT_PARSE_TEXT, "Unknown day of week text: {}", text1);
|
2023-02-28 15:02:02 +00:00
|
|
|
cur += 3;
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-02-28 15:02:02 +00:00
|
|
|
size_t left_size = it->second.first.size();
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, left_size, "jodaDayOfWeekText requires the second parg size >= " + std::to_string(left_size));
|
2023-02-28 15:02:02 +00:00
|
|
|
String text2(cur, left_size);
|
2023-03-05 08:24:05 +00:00
|
|
|
boost::to_lower(text2);
|
2023-02-28 15:02:02 +00:00
|
|
|
if (text2 != it->second.first)
|
2023-03-05 08:24:05 +00:00
|
|
|
throw Exception(ErrorCodes::CANNOT_PARSE_TEXT, "Unknown day of week text: {}", text1 + text2);
|
2023-02-28 15:02:02 +00:00
|
|
|
cur += left_size;
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setDayOfWeek(it->second.second);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlYear2(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 year2;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::Yes>(cur, end, year2);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setYear2(year2);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlYear4(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 year;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber4<Int32, NeedCheckSpace::Yes>(cur, end, year);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setYear(year);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-22 12:42:59 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlTimezoneOffset(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 5, "mysqlTimezoneOffset requires size >= 5");
|
2023-03-01 09:15:29 +00:00
|
|
|
|
|
|
|
Int32 sign;
|
2023-02-27 13:41:38 +00:00
|
|
|
if (*cur == '-')
|
|
|
|
sign = -1;
|
2023-03-01 09:15:29 +00:00
|
|
|
else if (*cur == '+')
|
|
|
|
sign = 1;
|
|
|
|
else
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Unknown sign time zone offset: {}", std::string_view(cur, 1));
|
2023-02-27 13:41:38 +00:00
|
|
|
++cur;
|
2023-02-22 12:42:59 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
Int32 hour;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::No>(cur, end, hour);
|
2023-02-22 12:42:59 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
Int32 minute;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::No>(cur, end, minute);
|
2023-02-22 12:42:59 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
*date.time_zone_offset = sign * (hour * 3600 + minute * 60);
|
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlMinute(Pos cur, Pos end, DateTime & date)
|
2023-02-28 06:08:03 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 minute;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::Yes>(cur, end, minute);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setMinute(minute);
|
2023-02-28 06:08:03 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-27 13:41:38 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlAMPM(Pos cur, Pos end, DateTime & date)
|
2023-02-24 10:27:21 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 2, "mysqlAMPM requires size >= 2");
|
2023-02-27 13:41:38 +00:00
|
|
|
|
2023-02-28 15:02:02 +00:00
|
|
|
String text(cur, 2);
|
|
|
|
date.setAMPM(text);
|
2023-02-27 13:41:38 +00:00
|
|
|
cur += 2;
|
|
|
|
return cur;
|
2023-02-24 10:27:21 +00:00
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlHHMM12(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 8, "mysqlHHMM12 requires size >= 8");
|
2023-03-01 09:15:29 +00:00
|
|
|
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 hour;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::No>(cur, end, hour);
|
|
|
|
cur = assertChar<NeedCheckSpace::No>(cur, end, ':');
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setHour(hour, true, true);
|
|
|
|
|
|
|
|
Int32 minute;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::No>(cur, end, minute);
|
|
|
|
cur = assertChar<NeedCheckSpace::No>(cur, end, ' ');
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setMinute(minute);
|
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
cur = mysqlAMPM(cur, end, date);
|
|
|
|
return cur;
|
|
|
|
}
|
2023-02-22 12:42:59 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlHHMM24(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 5, "mysqlHHMM24 requires size >= 5");
|
2023-03-01 09:15:29 +00:00
|
|
|
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 hour;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::No>(cur, end, hour);
|
|
|
|
cur = assertChar<NeedCheckSpace::No>(cur, end, ':');
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setHour(hour, false, false);
|
|
|
|
|
|
|
|
Int32 minute;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::No>(cur, end, minute);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setMinute(minute);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlSecond(Pos cur, Pos end, DateTime & date)
|
2023-02-28 06:08:03 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 second;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::Yes>(cur, end, second);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setSecond(second);
|
2023-02-28 06:08:03 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlISO8601Time(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 8, "mysqlISO8601Time requires size >= 8");
|
2023-02-28 15:02:02 +00:00
|
|
|
|
2023-03-01 09:15:29 +00:00
|
|
|
Int32 hour;
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 minute;
|
|
|
|
Int32 second;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::No>(cur, end, hour);
|
|
|
|
cur = assertChar<NeedCheckSpace::No>(cur, end, ':');
|
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::No>(cur, end, minute);
|
|
|
|
cur = assertChar<NeedCheckSpace::No>(cur, end, ':');
|
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::No>(cur, end, second);
|
2023-03-01 09:15:29 +00:00
|
|
|
|
|
|
|
date.setHour(hour, false, false);
|
|
|
|
date.setMinute(minute);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setSecond(second);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlHour12(Pos cur, Pos end, DateTime & date)
|
2023-02-22 12:42:59 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 hour;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::Yes>(cur, end, hour);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setHour(hour, true, true);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-23 11:56:03 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos mysqlHour24(Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 hour;
|
2023-03-03 02:42:35 +00:00
|
|
|
cur = readNumber2<Int32, NeedCheckSpace::Yes>(cur, end, hour);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setHour(hour, false, false);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
static Pos readNumberWithVariableLength(
|
|
|
|
Pos cur,
|
|
|
|
Pos end,
|
|
|
|
bool allow_negative,
|
|
|
|
bool allow_plus_sign,
|
|
|
|
bool is_year,
|
|
|
|
int repetitions,
|
|
|
|
int max_digits_consume,
|
|
|
|
Int32 & number)
|
|
|
|
{
|
|
|
|
bool negative = false;
|
|
|
|
if (allow_negative && cur < end && *cur == '-')
|
2023-02-24 09:07:27 +00:00
|
|
|
{
|
2023-02-27 13:41:38 +00:00
|
|
|
negative = true;
|
|
|
|
++cur;
|
|
|
|
}
|
|
|
|
else if (allow_plus_sign && cur < end && *cur == '+')
|
|
|
|
{
|
|
|
|
negative = false;
|
|
|
|
++cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
number = 0;
|
|
|
|
Pos start = cur;
|
|
|
|
if (is_year && repetitions == 2)
|
|
|
|
{
|
|
|
|
// If abbreviated two year digit is provided in format string, try to read
|
|
|
|
// in two digits of year and convert to appropriate full length year The
|
|
|
|
// two-digit mapping is as follows: [00, 69] -> [2000, 2069]
|
|
|
|
// [70, 99] -> [1970, 1999]
|
|
|
|
// If more than two digits are provided, then simply read in full year
|
|
|
|
// normally without conversion
|
|
|
|
int count = 0;
|
|
|
|
while (cur < end && cur < start + max_digits_consume && *cur >= '0' && *cur <= '9')
|
|
|
|
{
|
|
|
|
number = number * 10 + (*cur - '0');
|
|
|
|
++cur;
|
|
|
|
++count;
|
|
|
|
}
|
|
|
|
if (count == 2)
|
|
|
|
{
|
|
|
|
if (number >= 70)
|
|
|
|
number += 1900;
|
|
|
|
else if (number >= 0 && number < 70)
|
|
|
|
number += 2000;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while (cur < end && cur < start + max_digits_consume && *cur >= '0' && *cur <= '9')
|
|
|
|
{
|
|
|
|
number = number * 10 + (*cur - '0');
|
|
|
|
++cur;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while (cur < end && cur < start + max_digits_consume && *cur >= '0' and *cur <= '9')
|
|
|
|
{
|
|
|
|
number = number * 10 + (*cur - '0');
|
|
|
|
++cur;
|
|
|
|
}
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
/// Need to have read at least one digit.
|
|
|
|
if (cur <= start)
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "read number from {} failed", String(cur, end - cur));
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
if (negative)
|
|
|
|
number *= -1;
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaEra(int, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 2, "jodaEra requires size >= 2");
|
2023-02-28 12:42:16 +00:00
|
|
|
|
2023-02-28 15:02:02 +00:00
|
|
|
String era(cur, 2);
|
|
|
|
date.setEra(era);
|
2023-02-27 13:41:38 +00:00
|
|
|
cur += 2;
|
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaCenturyOfEra(int repetitions, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 century;
|
|
|
|
cur = readNumberWithVariableLength(cur, end, false, false, false, repetitions, repetitions, century);
|
2023-03-03 02:42:35 +00:00
|
|
|
date.setCentury(century);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaYearOfEra(int repetitions, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 year_of_era;
|
|
|
|
cur = readNumberWithVariableLength(cur, end, false, false, true, repetitions, repetitions, year_of_era);
|
|
|
|
date.setYear(year_of_era, true);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaWeekYear(int repetitions, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 week_year;
|
|
|
|
cur = readNumberWithVariableLength(cur, end, true, true, true, repetitions, repetitions, week_year);
|
|
|
|
date.setYear(week_year, false, true);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaWeekOfWeekYear(int repetitions, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 week;
|
|
|
|
cur = readNumberWithVariableLength(cur, end, false, false, false, repetitions, std::max(repetitions, 2), week);
|
|
|
|
date.setWeek(week);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaDayOfWeek1Based(int repetitions, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 day_of_week;
|
|
|
|
cur = readNumberWithVariableLength(cur, end, false, false, false, repetitions, repetitions, day_of_week);
|
|
|
|
if (day_of_week < 1 || day_of_week > 7)
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Value {} for day of week 1-based must be in the range [1, 7]", day_of_week);
|
2023-02-27 13:41:38 +00:00
|
|
|
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setDayOfWeek(day_of_week);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaDayOfWeekText(size_t /*min_represent_digits*/, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 3, "jodaDayOfWeekText requires size >= 3");
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
String text1(cur, 3);
|
2023-03-05 08:24:05 +00:00
|
|
|
boost::to_lower(text1);
|
2023-02-27 13:41:38 +00:00
|
|
|
auto it = dayOfWeekMap.find(text1);
|
|
|
|
if (it == dayOfWeekMap.end())
|
2023-03-05 08:24:05 +00:00
|
|
|
throw Exception(ErrorCodes::CANNOT_PARSE_TEXT, "Unknown day of week text: {}", text1);
|
2023-02-27 13:41:38 +00:00
|
|
|
cur += 3;
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setDayOfWeek(it->second.second);
|
|
|
|
|
|
|
|
size_t left_size = it->second.first.size();
|
|
|
|
if (cur + left_size <= end)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
String text2(cur, left_size);
|
2023-03-05 08:24:05 +00:00
|
|
|
boost::to_lower(text2);
|
2023-02-27 13:41:38 +00:00
|
|
|
if (text2 == it->second.first)
|
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
cur += left_size;
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaYear(int repetitions, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 year;
|
|
|
|
cur = readNumberWithVariableLength(cur, end, true, true, true, repetitions, repetitions, year);
|
|
|
|
date.setYear(year);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaDayOfYear(int repetitions, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 day_of_year;
|
|
|
|
cur = readNumberWithVariableLength(cur, end, false, false, false, repetitions, std::max(repetitions, 3), day_of_year);
|
|
|
|
date.appendDayOfYear(day_of_year);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaMonthOfYear(int repetitions, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 month;
|
|
|
|
cur = readNumberWithVariableLength(cur, end, false, false, false, repetitions, 2, month);
|
|
|
|
date.setMonth(month);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaMonthOfYearText(int, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 3, "jodaMonthOfYearText requires size >= 3");
|
2023-02-27 13:41:38 +00:00
|
|
|
String text1(cur, 3);
|
2023-03-05 08:24:05 +00:00
|
|
|
boost::to_lower(text1);
|
2023-02-27 13:41:38 +00:00
|
|
|
auto it = monthMap.find(text1);
|
|
|
|
if (it == monthMap.end())
|
2023-03-05 08:24:05 +00:00
|
|
|
throw Exception(ErrorCodes::CANNOT_PARSE_TEXT, "Unknown month of year text: {}", text1);
|
2023-02-27 13:41:38 +00:00
|
|
|
cur += 3;
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setMonth(it->second.second);
|
|
|
|
|
|
|
|
size_t left_size = it->second.first.size();
|
|
|
|
if (cur + left_size <= end)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
String text2(cur, left_size);
|
2023-03-05 08:24:05 +00:00
|
|
|
boost::to_lower(text2);
|
2023-02-27 13:41:38 +00:00
|
|
|
if (text2 == it->second.first)
|
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
cur += left_size;
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaDayOfMonth(int repetitions, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 day_of_month;
|
|
|
|
cur = readNumberWithVariableLength(cur, end, false, false, false, repetitions, std::max(repetitions, 2), day_of_month);
|
|
|
|
date.appendDayOfMonth(day_of_month);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaHalfDayOfDay(int, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-03 02:42:35 +00:00
|
|
|
checkSpace(cur, end, 2, "jodaHalfDayOfDay requires size >= 2");
|
2023-02-27 13:41:38 +00:00
|
|
|
|
|
|
|
String text(cur, 2);
|
2023-02-28 15:02:02 +00:00
|
|
|
date.setAMPM(text);
|
2023-02-27 13:41:38 +00:00
|
|
|
cur += 2;
|
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaHourOfHalfDay(int repetitions, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 hour;
|
|
|
|
cur = readNumberWithVariableLength(cur, end, false, false, false, repetitions, std::max(repetitions, 2), hour);
|
|
|
|
date.setHour(hour, true, false);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaClockHourOfHalfDay(int repetitions, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 hour;
|
|
|
|
cur = readNumberWithVariableLength(cur, end, false, false, false, repetitions, std::max(repetitions, 2), hour);
|
|
|
|
date.setHour(hour, true, true);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaHourOfDay(int repetitions, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 hour;
|
|
|
|
cur = readNumberWithVariableLength(cur, end, false, false, false, repetitions, std::max(repetitions, 2), hour);
|
|
|
|
date.setHour(hour, false, false);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaClockHourOfDay(int repetitions, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 hour;
|
|
|
|
cur = readNumberWithVariableLength(cur, end, false, false, false, repetitions, std::max(repetitions, 2), hour);
|
|
|
|
date.setHour(hour, false, true);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaMinuteOfHour(int repetitions, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 minute;
|
|
|
|
cur = readNumberWithVariableLength(cur, end, false, false, false, repetitions, std::max(repetitions, 2), minute);
|
|
|
|
date.setMinute(minute);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
static Pos jodaSecondOfMinute(int repetitions, Pos cur, Pos end, DateTime & date)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-02-28 15:02:02 +00:00
|
|
|
Int32 second;
|
|
|
|
cur = readNumberWithVariableLength(cur, end, false, false, false, repetitions, std::max(repetitions, 2), second);
|
|
|
|
date.setSecond(second);
|
2023-02-27 13:41:38 +00:00
|
|
|
return cur;
|
|
|
|
}
|
|
|
|
};
|
2023-02-24 09:07:27 +00:00
|
|
|
|
|
|
|
|
2023-03-05 08:24:05 +00:00
|
|
|
ALWAYS_INLINE void parseFormat(const String & format, std::vector<Instruction> & instructions) const
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
|
|
|
if constexpr (parse_syntax == ParseDateTimeTraits::ParseSyntax::MySQL)
|
|
|
|
parseMysqlFormat(format, instructions);
|
|
|
|
else if constexpr (parse_syntax == ParseDateTimeTraits::ParseSyntax::Joda)
|
|
|
|
parseJodaFormat(format, instructions);
|
|
|
|
else
|
|
|
|
throw Exception(
|
|
|
|
ErrorCodes::NOT_IMPLEMENTED,
|
|
|
|
"Unknown datetime format style {} in function {}",
|
|
|
|
magic_enum::enum_name(parse_syntax),
|
|
|
|
getName());
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-03-05 08:24:05 +00:00
|
|
|
ALWAYS_INLINE void parseMysqlFormat(const String & format, std::vector<Instruction> & instructions) const
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
|
|
|
#define ACTION_ARGS(func) &(func), #func
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
Pos pos = format.data();
|
|
|
|
Pos end = pos + format.size();
|
|
|
|
while (true)
|
|
|
|
{
|
2023-03-05 08:24:05 +00:00
|
|
|
Pos next_percent_pos = find_first_symbols<'%'>(pos, end);
|
|
|
|
if (next_percent_pos < end)
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
2023-03-05 08:24:05 +00:00
|
|
|
if (pos < next_percent_pos)
|
|
|
|
instructions.emplace_back(String(pos, next_percent_pos - pos));
|
2023-02-27 13:41:38 +00:00
|
|
|
|
2023-03-05 08:24:05 +00:00
|
|
|
pos = next_percent_pos + 1;
|
2023-02-27 13:41:38 +00:00
|
|
|
if (pos >= end)
|
|
|
|
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Sign '%' is the last in format, if you need it, use '%%'");
|
|
|
|
|
|
|
|
switch (*pos)
|
|
|
|
{
|
|
|
|
// Abbreviated weekday [Mon...Sun]
|
|
|
|
case 'a':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlDayOfWeekTextShort));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Abbreviated month [Jan...Dec]
|
|
|
|
case 'b':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlMonthOfYearTextShort));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Month as a decimal number (01-12)
|
|
|
|
case 'c':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlMonth));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Year, divided by 100, zero-padded
|
|
|
|
case 'C':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlCentury));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Day of month, zero-padded (01-31)
|
|
|
|
case 'd':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlDayOfMonth));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Short MM/DD/YY date, equivalent to %m/%d/%y
|
|
|
|
case 'D':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlAmericanDate));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Day of month, space-padded ( 1-31) 23
|
|
|
|
case 'e':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlDayOfMonthSpacePadded));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Fractional seconds
|
|
|
|
case 'f':
|
|
|
|
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "format is not supported for fractional seconds");
|
|
|
|
|
|
|
|
// Short YYYY-MM-DD date, equivalent to %Y-%m-%d 2001-08-23
|
|
|
|
case 'F':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlISO8601Date));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Last two digits of year of ISO 8601 week number (see %G)
|
|
|
|
case 'g':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlISO8601Year2));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Year of ISO 8601 week number (see %V)
|
|
|
|
case 'G':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlISO8601Year4));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Day of the year (001-366) 235
|
|
|
|
case 'j':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlDayOfYear));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Month as a decimal number (01-12)
|
|
|
|
case 'm':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlMonth));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// ISO 8601 weekday as number with Monday as 1 (1-7)
|
|
|
|
case 'u':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlDayOfWeek));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// ISO 8601 week number (01-53)
|
|
|
|
case 'V':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlISO8601Week));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Weekday as a decimal number with Sunday as 0 (0-6) 4
|
|
|
|
case 'w':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlDayOfWeek0To6));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Full weekday [Monday...Sunday]
|
|
|
|
case 'W':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlDayOfWeekTextLong));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Two digits year
|
|
|
|
case 'y':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlYear2));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Four digits year
|
|
|
|
case 'Y':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlYear4));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Quarter (1-4)
|
|
|
|
case 'Q':
|
|
|
|
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "format is not supported for quarter");
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Offset from UTC timezone as +hhmm or -hhmm
|
|
|
|
case 'z':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlTimezoneOffset));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/// Time components. If the argument is Date, not a DateTime, then this components will have default value.
|
|
|
|
|
|
|
|
// Minute (00-59)
|
|
|
|
case 'M':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlMinute));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// AM or PM
|
|
|
|
case 'p':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlAMPM));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// 12-hour HH:MM time, equivalent to %h:%i %p 2:55 PM
|
|
|
|
case 'r':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlHHMM12));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// 24-hour HH:MM time, equivalent to %H:%i 14:55
|
|
|
|
case 'R':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlHHMM24));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Seconds
|
|
|
|
case 's':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlSecond));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Seconds
|
|
|
|
case 'S':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlSecond));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%i:%S 14:55:02
|
|
|
|
case 'T':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlISO8601Time));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Hour in 12h format (01-12)
|
|
|
|
case 'h':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlHour12));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Hour in 24h format (00-23)
|
|
|
|
case 'H':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlHour24));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Minute of hour range [0, 59]
|
|
|
|
case 'i':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlMinute));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Hour in 12h format (01-12)
|
|
|
|
case 'I':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlHour12));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Hour in 24h format (00-23)
|
|
|
|
case 'k':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlHour24));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Hour in 12h format (01-12)
|
|
|
|
case 'l':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlHour12));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 't':
|
|
|
|
instructions.emplace_back("\t");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'n':
|
|
|
|
instructions.emplace_back("\n");
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Escaped literal characters.
|
|
|
|
case '%':
|
|
|
|
instructions.emplace_back("\n");
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Unimplemented
|
|
|
|
case 'U':
|
|
|
|
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "format is not supported for WEEK (Sun-Sat)");
|
|
|
|
case 'v':
|
|
|
|
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "format is not supported for WEEK (Mon-Sun)");
|
|
|
|
case 'x':
|
|
|
|
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "format is not supported for YEAR for week (Mon-Sun)");
|
|
|
|
case 'X':
|
|
|
|
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "format is not supported for YEAR for week (Sun-Sat)");
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw Exception(
|
|
|
|
ErrorCodes::BAD_ARGUMENTS,
|
|
|
|
"Incorrect syntax '{}', symbol is not supported '{}' for function {}",
|
|
|
|
format,
|
|
|
|
*pos,
|
|
|
|
getName());
|
|
|
|
}
|
|
|
|
|
|
|
|
++pos;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (pos < end)
|
|
|
|
instructions.emplace_back(String(pos, end - pos));
|
|
|
|
break;
|
|
|
|
}
|
2023-02-24 09:07:27 +00:00
|
|
|
}
|
2023-02-27 13:41:38 +00:00
|
|
|
#undef ACTION_ARGS
|
|
|
|
}
|
|
|
|
|
2023-03-05 08:24:05 +00:00
|
|
|
void parseJodaFormat(const String & format, std::vector<Instruction> & instructions) const
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
|
|
|
#define ACTION_ARGS_WITH_BIND(func, arg) std::bind_front(&(func), (arg)), #func
|
|
|
|
|
|
|
|
// size_t reserve_size = 0;
|
|
|
|
const char * pos = format.data();
|
|
|
|
const char * end = pos + format.size();
|
|
|
|
|
|
|
|
while (pos < end)
|
2023-02-22 10:05:53 +00:00
|
|
|
{
|
2023-02-27 13:41:38 +00:00
|
|
|
const char * cur_token = pos;
|
|
|
|
|
|
|
|
// Literal case
|
|
|
|
if (*cur_token == '\'')
|
|
|
|
{
|
|
|
|
// Case 1: 2 consecutive single quote
|
|
|
|
if (pos + 1 < end && *(pos + 1) == '\'')
|
|
|
|
{
|
|
|
|
instructions.emplace_back(String(cur_token, 1));
|
|
|
|
pos += 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Case 2: find closing single quote
|
|
|
|
Int64 count = numLiteralChars(cur_token + 1, end);
|
|
|
|
if (count == -1)
|
|
|
|
throw Exception(ErrorCodes::BAD_ARGUMENTS, "No closing single quote for literal");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (Int64 i = 1; i <= count; i++)
|
|
|
|
{
|
|
|
|
instructions.emplace_back(String(cur_token + i, 1));
|
|
|
|
// ++reserve_size;
|
|
|
|
if (*(cur_token + i) == '\'')
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
pos += count + 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int repetitions = 1;
|
|
|
|
++pos;
|
|
|
|
while (pos < end && *cur_token == *pos)
|
|
|
|
{
|
|
|
|
++repetitions;
|
|
|
|
++pos;
|
|
|
|
}
|
|
|
|
switch (*cur_token)
|
|
|
|
{
|
|
|
|
case 'G':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaEra, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'C':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaCenturyOfEra, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'Y':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaYearOfEra, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'x':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaWeekYear, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'w':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaWeekOfWeekYear, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'e':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaDayOfWeek1Based, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'E':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaDayOfWeekText, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'y':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaYear, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'D':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaDayOfYear, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'M':
|
|
|
|
if (repetitions <= 2)
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaMonthOfYear, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
else
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaMonthOfYearText, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'd':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaDayOfMonth, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'a':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaHalfDayOfDay, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'K':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaHourOfHalfDay, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'h':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaClockHourOfHalfDay, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'H':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaHourOfDay, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'k':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaClockHourOfDay, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'm':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaMinuteOfHour, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 's':
|
2023-03-05 08:24:05 +00:00
|
|
|
instructions.emplace_back(ACTION_ARGS_WITH_BIND(Instruction::jodaSecondOfMinute, repetitions));
|
2023-02-27 13:41:38 +00:00
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "format is not supported for fractional seconds");
|
|
|
|
break;
|
|
|
|
case 'z':
|
|
|
|
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "format is not supported for timezone");
|
|
|
|
break;
|
|
|
|
case 'Z':
|
|
|
|
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "format is not supported for timezone offset id");
|
|
|
|
default:
|
|
|
|
if (isalpha(*cur_token))
|
|
|
|
throw Exception(
|
|
|
|
ErrorCodes::NOT_IMPLEMENTED, "format is not supported for {}", String(cur_token, repetitions));
|
|
|
|
|
|
|
|
instructions.emplace_back(String(cur_token, pos - cur_token));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
}
|
2023-02-28 06:08:03 +00:00
|
|
|
#undef ACTION_ARGS_WITH_BIND
|
2023-02-22 10:05:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
String getFormat(const ColumnsWithTypeAndName & arguments) const
|
2023-02-27 13:41:38 +00:00
|
|
|
{
|
|
|
|
if (arguments.size() < 2)
|
|
|
|
{
|
|
|
|
if constexpr (parse_syntax == ParseDateTimeTraits::ParseSyntax::Joda)
|
|
|
|
return "yyyy-MM-dd HH:mm:ss";
|
|
|
|
else
|
|
|
|
return "%Y-%m-%d %H:%M:%S";
|
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
const auto * format_column = checkAndGetColumnConst<ColumnString>(arguments[1].column.get());
|
|
|
|
if (!format_column)
|
|
|
|
throw Exception(
|
|
|
|
ErrorCodes::ILLEGAL_COLUMN,
|
|
|
|
"Illegal column {} of second ('format') argument of function {}. Must be constant string.",
|
|
|
|
arguments[1].column->getName(),
|
|
|
|
getName());
|
|
|
|
return format_column->getValue<String>();
|
|
|
|
}
|
|
|
|
|
2023-03-03 02:42:35 +00:00
|
|
|
std::pair<const DateLUTImpl *, String> getTimeZone(const ColumnsWithTypeAndName & arguments) const
|
2023-02-22 10:05:53 +00:00
|
|
|
{
|
2023-02-27 13:41:38 +00:00
|
|
|
if (arguments.size() < 3)
|
|
|
|
return {&DateLUT::instance(), ""};
|
|
|
|
|
|
|
|
const auto * col = checkAndGetColumnConst<ColumnString>(arguments[2].column.get());
|
|
|
|
if (!col)
|
|
|
|
throw Exception(
|
|
|
|
ErrorCodes::ILLEGAL_COLUMN,
|
|
|
|
"Illegal column {} of third ('timezone') argument of function {}. Must be constant string.",
|
|
|
|
arguments[2].column->getName(),
|
|
|
|
getName());
|
|
|
|
|
|
|
|
String time_zone = col->getValue<String>();
|
|
|
|
if (time_zone.empty())
|
|
|
|
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Provided time zone must be non-empty and be a valid time zone");
|
|
|
|
return {&DateLUT::instance(time_zone), time_zone};
|
2023-02-22 10:05:53 +00:00
|
|
|
}
|
2023-02-27 13:41:38 +00:00
|
|
|
};
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
struct NameParseDateTime
|
|
|
|
{
|
|
|
|
static constexpr auto name = "parseDateTime";
|
|
|
|
};
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
struct NameParseDateTimeInJodaSyntax
|
2023-02-24 09:07:27 +00:00
|
|
|
{
|
2023-02-27 13:41:38 +00:00
|
|
|
static constexpr auto name = "parseDateTimeInJodaSyntax";
|
|
|
|
};
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-27 13:41:38 +00:00
|
|
|
using FunctionParseDateTime = FunctionParseDateTimeImpl<NameParseDateTime, ParseDateTimeTraits::ParseSyntax::MySQL>;
|
|
|
|
using FunctionParseDateTimeInJodaSyntax
|
|
|
|
= FunctionParseDateTimeImpl<NameParseDateTimeInJodaSyntax, ParseDateTimeTraits::ParseSyntax::Joda>;
|
2023-02-24 09:07:27 +00:00
|
|
|
}
|
2023-02-22 10:05:53 +00:00
|
|
|
|
2023-02-24 09:07:27 +00:00
|
|
|
REGISTER_FUNCTION(ParseDateTime)
|
|
|
|
{
|
|
|
|
factory.registerFunction<FunctionParseDateTime>();
|
|
|
|
factory.registerAlias("TO_UNIXTIME", "parseDateTime");
|
2023-02-27 13:41:38 +00:00
|
|
|
|
|
|
|
factory.registerFunction<FunctionParseDateTimeInJodaSyntax>();
|
2023-02-22 10:05:53 +00:00
|
|
|
}
|
|
|
|
|
2023-02-24 09:07:27 +00:00
|
|
|
|
2023-02-22 10:05:53 +00:00
|
|
|
}
|