2023-03-26 13:43:15 +00:00
# include <Columns/ColumnNullable.h>
# include <Columns/ColumnsNumber.h>
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 ILLEGAL_COLUMN ;
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 ;
2023-03-07 06:35:50 +00:00
extern const int CANNOT_PARSE_DATETIME ;
2023-03-06 09:08:55 +00:00
extern const int NOT_ENOUGH_SPACE ;
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 } } ,
2023-03-23 22:16:10 +00:00
{ " mar " , { " ch " , 3 } } ,
2023-02-27 13:41:38 +00:00
{ " 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
{
2023-03-07 03:45:32 +00:00
/// If both week_date_format and week_date_format is false, date is composed of year, month and day
Int32 year = 1970 ; /// year, range [1970, 2106]
Int32 month = 1 ; /// month of year, range [1, 12]
Int32 day = 1 ; /// day of month, range [1, 31]
2023-02-22 10:05:53 +00:00
2023-03-07 03:45:32 +00:00
Int32 week = 1 ; /// ISO week of year, range [1, 53]
Int32 day_of_week = 1 ; /// day of week, range [1, 7], 1 represents Monday, 2 represents Tuesday...
bool week_date_format
= false ; /// If true, date is composed of week year(reuse year), week of year(use week) and day of week(use day_of_week)
2023-02-22 10:05:53 +00:00
2023-03-07 03:45:32 +00:00
Int32 day_of_year = 1 ; /// day of year, range [1, 366]
bool day_of_year_format = false ; /// If true, date is composed of year(reuse year), day of year(use day_of_year)
2023-02-22 10:05:53 +00:00
2023-03-07 03:45:32 +00:00
bool is_year_of_era = false ; /// If true, year is calculated from era and year of era, the latter cannot be zero or negative.
bool has_year = false ; /// Whether year was explicitly specified.
2023-02-22 10:05:53 +00:00
2023-03-23 21:41:01 +00:00
/// If hour_starts_at_1 = true, is_hour_of_half_day = true, hour's range is [1, 12]
/// If hour_starts_at_1 = true, is_hour_of_half_day = false, hour's range is [1, 24]
/// If hour_starts_at_1 = false, is_hour_of_half_day = true, hour's range is [0, 11]
/// If hour_starts_at_1 = false, is_hour_of_half_day = false, hour's range is [0, 23]
2023-02-27 13:41:38 +00:00
Int32 hour = 0 ;
2023-03-07 03:45:32 +00:00
Int32 minute = 0 ; /// range [0, 59]
Int32 second = 0 ; /// range [0, 59]
2023-02-22 10:05:53 +00:00
2023-03-20 15:20:57 +00:00
bool is_am = true ; /// If is_hour_of_half_day = true and is_am = false (i.e. pm) then add 12 hours to the result DateTime
2023-03-23 21:41:01 +00:00
bool hour_starts_at_1 = false ; /// Whether the hour is clockhour
2023-03-07 03:45:32 +00:00
bool is_hour_of_half_day = false ; /// Whether the hour is of half day
2023-02-22 10:05:53 +00:00
2023-03-20 16:24:06 +00:00
bool has_time_zone_offset = false ; /// If true, time zone offset is explicitly specified.
2023-03-07 06:35:50 +00:00
Int64 time_zone_offset = 0 ; /// Offset in seconds between current timezone to UTC.
2023-03-01 09:15:29 +00:00
void reset ( )
{
year = 1970 ;
month = 1 ;
day = 1 ;
week = 1 ;
day_of_week = 1 ;
week_date_format = false ;
day_of_year = 1 ;
day_of_year_format = false ;
2023-03-07 03:45:32 +00:00
is_year_of_era = false ;
has_year = false ;
2023-03-01 09:15:29 +00:00
hour = 0 ;
minute = 0 ;
second = 0 ;
2023-03-03 02:42:35 +00:00
2023-03-07 03:45:32 +00:00
is_am = true ;
2023-03-23 21:41:01 +00:00
hour_starts_at_1 = false ;
2023-03-07 03:45:32 +00:00
is_hour_of_half_day = false ;
2023-03-01 09:15:29 +00:00
2023-03-07 03:45:32 +00:00
has_time_zone_offset = false ;
time_zone_offset = 0 ;
2023-03-01 09:15:29 +00:00
}
2023-02-22 10:05:53 +00:00
2023-03-20 15:58:37 +00:00
/// Input text is expected to be lowered by caller
void setEra ( const String & text ) // NOLINT
2023-03-20 15:20:57 +00:00
{
if ( text = = " bc " )
throw Exception ( ErrorCodes : : CANNOT_PARSE_DATETIME , " Era BC exceeds the range of DateTime " ) ;
else if ( text ! = " ad " )
2023-05-02 15:52:41 +00:00
throw Exception ( ErrorCodes : : CANNOT_PARSE_DATETIME , " Unknown era {} (expected 'ad' or 'bc') " , text) ;
2023-03-20 15:20:57 +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 )
2023-03-07 06:35:50 +00:00
throw Exception ( ErrorCodes : : CANNOT_PARSE_DATETIME , " Value {} for century must be in the range [19, 21] " , century ) ;
2023-02-28 15:02:02 +00:00
year = 100 * century ;
has_year = true ;
}
2023-03-20 15:20:57 +00:00
void setYear ( Int32 year_ , bool is_year_of_era_ = false , bool is_week_year = false )
2023-02-28 15:02:02 +00:00
{
2023-03-20 15:20:57 +00:00
if ( year_ < minYear | | year_ > maxYear )
throw Exception ( ErrorCodes : : CANNOT_PARSE_DATETIME , " Value {} for year must be in the range [{}, {}] " , year_ , minYear , maxYear ) ;
2023-02-28 15:02:02 +00:00
2023-03-20 15:20:57 +00:00
year = year_ ;
has_year = true ;
is_year_of_era = is_year_of_era_ ;
if ( is_week_year )
2023-02-28 15:02:02 +00:00
{
2023-03-20 15:20:57 +00:00
week_date_format = true ;
day_of_year_format = false ;
2023-02-28 15:02:02 +00:00
}
}
2023-03-20 15:58:37 +00:00
void setYear2 ( Int32 year_ )
2023-03-20 15:20:57 +00:00
{
if ( year_ > = 70 & & year_ < 100 )
year_ + = 1900 ;
else if ( year_ > = 0 & & year_ < 70 )
year_ + = 2000 ;
else
throw Exception ( ErrorCodes : : CANNOT_PARSE_DATETIME , " Value {} for year2 must be in the range [0, 99] " , year_ ) ;
2023-03-20 15:58:37 +00:00
setYear ( year_ , false , false ) ;
2023-03-20 15:20:57 +00:00
}
2023-02-28 15:02:02 +00:00
void setMonth ( Int32 month_ )
{
if ( month_ < 1 | | month_ > 12 )
2023-03-07 06:35:50 +00:00
throw Exception ( ErrorCodes : : CANNOT_PARSE_DATETIME , " Value {} for month of year must be in the range [1, 12] " , month_ ) ;
2023-02-28 15:02:02 +00:00
month = month_ ;
week_date_format = false ;
day_of_year_format = false ;
if ( ! has_year )
{
has_year = true ;
year = 2000 ;
}
}
2023-03-20 15:20:57 +00:00
void setWeek ( Int32 week_ )
2023-02-28 15:02:02 +00:00
{
2023-03-20 15:20:57 +00:00
if ( week_ < 1 | | week_ > 53 )
throw Exception ( ErrorCodes : : CANNOT_PARSE_DATETIME , " Value {} for week of week year must be in the range [1, 53] " , week_ ) ;
2023-02-28 15:02:02 +00:00
2023-03-20 15:20:57 +00:00
week = week_ ;
week_date_format = true ;
2023-02-28 15:02:02 +00:00
day_of_year_format = false ;
if ( ! has_year )
{
has_year = true ;
year = 2000 ;
}
}
2023-03-20 15:20:57 +00:00
void setDayOfYear ( Int32 day_of_year_ )
2023-02-28 15:02:02 +00:00
{
if ( day_of_year_ < 1 | | day_of_year_ > 366 )
2023-03-07 06:35:50 +00:00
throw Exception ( ErrorCodes : : CANNOT_PARSE_DATETIME , " Value {} for day of year must be in the range [1, 366] " , day_of_year_ ) ;
2023-02-28 15:02:02 +00:00
day_of_year = day_of_year_ ;
day_of_year_format = true ;
week_date_format = false ;
if ( ! has_year )
{
has_year = true ;
year = 2000 ;
}
}
2023-03-20 15:20:57 +00:00
void setDayOfMonth ( Int32 day_of_month )
2023-02-28 15:02:02 +00:00
{
2023-03-20 15:20:57 +00:00
if ( day_of_month < 1 | | day_of_month > 31 )
throw Exception ( ErrorCodes : : CANNOT_PARSE_DATETIME , " Value {} for day of month must be in the range [1, 31] " , day_of_month ) ;
2023-02-28 15:02:02 +00:00
2023-03-20 15:20:57 +00:00
day = day_of_month ;
week_date_format = false ;
day_of_year_format = false ;
if ( ! has_year )
2023-02-28 15:02:02 +00:00
{
2023-03-20 15:20:57 +00:00
has_year = true ;
year = 2000 ;
2023-02-28 15:02:02 +00:00
}
}
2023-03-20 15:20:57 +00:00
void setDayOfWeek ( Int32 day_of_week_ )
2023-02-28 15:02:02 +00:00
{
2023-03-20 15:20:57 +00:00
if ( day_of_week_ < 1 | | day_of_week_ > 7 )
throw Exception ( ErrorCodes : : CANNOT_PARSE_DATETIME , " Value {} for day of week must be in the range [1, 7] " , day_of_week_ ) ;
2023-02-28 15:02:02 +00:00
2023-03-20 15:20:57 +00:00
day_of_week = day_of_week_ ;
2023-02-28 15:02:02 +00:00
week_date_format = true ;
day_of_year_format = false ;
if ( ! has_year )
{
has_year = true ;
year = 2000 ;
}
}
2023-03-20 15:58:37 +00:00
/// Input text is expected to be lowered by caller
void setAMPM ( const String & text )
2023-02-28 15:02:02 +00:00
{
if ( text = = " am " )
is_am = true ;
else if ( text = = " pm " )
is_am = false ;
else
2023-03-07 06:35:50 +00:00
throw Exception ( ErrorCodes : : CANNOT_PARSE_DATETIME , " Unknown half day of day: {} " , text ) ;
2023-02-28 15:02:02 +00:00
}
2023-03-23 21:41:01 +00:00
void setHour ( Int32 hour_ , bool is_hour_of_half_day_ = false , bool hour_starts_at_1_ = 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-03-23 21:41:01 +00:00
if ( ! is_hour_of_half_day_ & & ! hour_starts_at_1_ )
2023-02-28 15:02:02 +00:00
{
max_hour = 23 ;
min_hour = 0 ;
}
2023-03-23 21:41:01 +00:00
else if ( ! is_hour_of_half_day_ & & hour_starts_at_1_ )
2023-02-28 15:02:02 +00:00
{
max_hour = 24 ;
min_hour = 1 ;
new_hour = hour_ % 24 ;
}
2023-03-23 21:41:01 +00:00
else if ( is_hour_of_half_day_ & & ! hour_starts_at_1_ )
2023-02-28 15:02:02 +00:00
{
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 (
2023-03-07 06:35:50 +00:00
ErrorCodes : : CANNOT_PARSE_DATETIME ,
2023-03-23 21:41:01 +00:00
" Value {} for hour must be in the range [{}, {}] if_hour_of_half_day={} and hour_starts_at_1={} " ,
2023-02-28 15:02:02 +00:00
hour ,
max_hour ,
min_hour ,
is_hour_of_half_day_ ,
2023-03-23 21:41:01 +00:00
hour_starts_at_1_ ) ;
2023-02-28 15:02:02 +00:00
hour = new_hour ;
is_hour_of_half_day = is_hour_of_half_day_ ;
2023-03-23 21:41:01 +00:00
hour_starts_at_1 = hour_starts_at_1_ ;
2023-02-28 15:02:02 +00:00
}
2023-03-20 15:20:57 +00:00
void setMinute ( Int32 minute_ )
{
if ( minute_ < 0 | | minute_ > 59 )
throw Exception ( ErrorCodes : : CANNOT_PARSE_DATETIME , " 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 : : CANNOT_PARSE_DATETIME , " Value {} for second must be in the range [0, 59] " , second_ ) ;
second = second_ ;
}
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-20 16:24:06 +00:00
static 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 )
2023-03-07 06:35:50 +00:00
throw Exception ( ErrorCodes : : CANNOT_PARSE_DATETIME , " 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-20 16:24:06 +00:00
static 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-03-07 06:35:50 +00:00
throw Exception ( ErrorCodes : : CANNOT_PARSE_DATETIME , " 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-20 16:24:06 +00:00
static 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-03-07 06:35:50 +00:00
throw Exception ( ErrorCodes : : CANNOT_PARSE_DATETIME , " 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-03-20 16:24:06 +00:00
Int64 buildDateTime ( const DateLUTImpl & time_zone )
2023-02-27 13:41:38 +00:00
{
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
// 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-03-22 08:53:47 +00:00
Int64 seconds_since_epoch = days_since_epoch * 86400U Z + hour * 3600U Z + minute * 60U Z + 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
2023-03-07 06:35:50 +00:00
if ( ! has_time_zone_offset )
time_zone_offset = time_zone . timezoneOffset ( seconds_since_epoch ) ;
2023-02-22 10:05:53 +00:00
2023-02-27 13:41:38 +00:00
/// Time zone is specified in format string.
2023-03-07 03:45:32 +00:00
if ( seconds_since_epoch > = time_zone_offset )
seconds_since_epoch - = time_zone_offset ;
2023-02-28 06:08:03 +00:00
else
2023-03-07 06:35:50 +00:00
throw Exception ( ErrorCodes : : VALUE_IS_OUT_OF_RANGE_OF_DATA_TYPE , " Seconds since epoch is negative " ) ;
2023-02-28 06:08:03 +00:00
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
} ;
2023-03-20 15:58:37 +00:00
enum class ParseSyntax
2023-02-27 13:41:38 +00:00
{
2023-03-20 15:58:37 +00:00
MySQL ,
Joda
2023-02-27 13:41:38 +00:00
} ;
2023-02-22 10:05:53 +00:00
2023-03-26 13:43:15 +00:00
enum class ErrorHandling
{
Exception ,
Zero ,
Null
} ;
2023-02-27 13:41:38 +00:00
/// _FUNC_(str[, format, timezone])
2023-03-26 13:43:15 +00:00
template < typename Name , ParseSyntax parse_syntax , ErrorHandling error_handling >
2023-02-27 13:41:38 +00:00
class FunctionParseDateTimeImpl : public IFunction
{
public :
2023-04-05 12:25:51 +00:00
const bool mysql_M_is_month_name ;
2023-02-27 13:41:38 +00:00
static constexpr auto name = Name : : name ;
2023-04-05 12:25:51 +00:00
static FunctionPtr create ( ContextPtr context ) { return std : : make_shared < FunctionParseDateTimeImpl > ( context ) ; }
explicit FunctionParseDateTimeImpl ( ContextPtr context )
: mysql_M_is_month_name ( context - > getSettings ( ) . formatdatetime_parsedatetime_m_is_month_name )
{
}
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-03-31 14:22:10 +00:00
FunctionArgumentDescriptors args {
{ " time " , & isString < IDataType > , nullptr , " String " } ,
{ " format " , & isString < IDataType > , nullptr , " String " } ,
} ;
2023-02-22 10:05:53 +00:00
2023-03-31 14:22:10 +00:00
if ( arguments . size ( ) = = 3 )
args . emplace_back ( FunctionArgumentDescriptor { " timezone " , & isString < IDataType > , nullptr , " String " } ) ;
2023-02-24 09:07:27 +00:00
2023-03-31 14:22:10 +00:00
validateFunctionArgumentTypes ( * this , arguments , args ) ;
2023-02-23 11:56:03 +00:00
2023-03-06 09:08:55 +00:00
String time_zone_name = getTimeZone ( arguments ) . getTimeZone ( ) ;
2023-03-26 13:43:15 +00:00
DataTypePtr date_type = std : : make_shared < DataTypeDateTime > ( time_zone_name ) ;
if ( error_handling = = ErrorHandling : : Null )
return std : : make_shared < DataTypeNullable > ( date_type ) ;
else
return date_type ;
2023-02-27 13:41:38 +00:00
}
2023-02-22 10:05:53 +00:00
2023-03-26 13:43:15 +00:00
ColumnPtr executeImpl ( const ColumnsWithTypeAndName & arguments , const DataTypePtr & /*result_type*/ , size_t input_rows_count ) const override
2023-02-27 13:41:38 +00:00
{
const auto * col_str = checkAndGetColumn < ColumnString > ( arguments [ 0 ] . column . get ( ) ) ;
2023-03-22 08:45:46 +00:00
if ( ! col_str )
throw Exception (
ErrorCodes : : ILLEGAL_COLUMN ,
" Illegal column {} of first ('str') argument of function {}. Must be string. " ,
arguments [ 0 ] . column - > getName ( ) ,
getName ( ) ) ;
2023-02-27 13:41:38 +00:00
String format = getFormat ( arguments ) ;
2023-03-06 09:08:55 +00:00
const auto & time_zone = getTimeZone ( arguments ) ;
std : : vector < Instruction > instructions = parseFormat ( format ) ;
2023-02-27 13:41:38 +00:00
2023-03-26 13:43:15 +00:00
auto col_res = ColumnDateTime : : create ( input_rows_count ) ;
ColumnUInt8 : : MutablePtr col_null_map ;
if constexpr ( error_handling = = ErrorHandling : : Null )
col_null_map = ColumnUInt8 : : create ( input_rows_count , 0 ) ;
2023-03-20 16:24:06 +00:00
auto & res_data = col_res - > getData ( ) ;
2023-03-07 07:50:11 +00:00
/// Make datetime fit in a cache line.
alignas ( 64 ) DateTime datetime ;
2023-02-27 13:41:38 +00:00
for ( size_t i = 0 ; i < input_rows_count ; + + i )
{
2023-03-07 07:50:11 +00:00
datetime . 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 ;
2023-03-26 13:43:15 +00:00
bool error = false ;
2023-02-27 13:41:38 +00:00
for ( const auto & instruction : instructions )
2023-03-02 08:58:34 +00:00
{
2023-03-26 13:43:15 +00:00
try
{
cur = instruction . perform ( cur , end , datetime ) ;
}
catch ( . . . )
{
if constexpr ( error_handling = = ErrorHandling : : Zero )
{
res_data [ i ] = 0 ;
error = true ;
break ;
}
else if constexpr ( error_handling = = ErrorHandling : : Null )
{
res_data [ i ] = 0 ;
col_null_map - > getData ( ) [ i ] = 1 ;
error = true ;
break ;
}
else
{
static_assert ( error_handling = = ErrorHandling : : Exception ) ;
throw ;
}
}
2023-03-02 08:58:34 +00:00
}
2023-02-23 11:56:03 +00:00
2023-03-26 13:43:15 +00:00
if ( error )
continue ;
2023-02-23 11:56:03 +00:00
2023-03-26 13:43:15 +00:00
try
{
/// Ensure all input was consumed
if ( cur < end )
throw Exception (
ErrorCodes : : CANNOT_PARSE_DATETIME ,
" Invalid format input {} is malformed at {} " ,
str_ref . toView ( ) ,
std : : string_view ( cur , end - cur ) ) ;
Int64 time = datetime . buildDateTime ( time_zone ) ;
res_data [ i ] = static_cast < UInt32 > ( time ) ;
}
catch ( . . . )
{
if constexpr ( error_handling = = ErrorHandling : : Zero )
res_data [ i ] = 0 ;
else if constexpr ( error_handling = = ErrorHandling : : Null )
{
res_data [ i ] = 0 ;
col_null_map - > getData ( ) [ i ] = 1 ;
}
else
{
static_assert ( error_handling = = ErrorHandling : : Exception ) ;
throw ;
}
}
2023-02-27 13:41:38 +00:00
}
2023-02-24 09:07:27 +00:00
2023-03-26 13:43:15 +00:00
if constexpr ( error_handling = = ErrorHandling : : Null )
return ColumnNullable : : create ( std : : move ( col_res ) , std : : move ( col_null_map ) ) ;
else
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 <
2023-03-20 15:58:37 +00:00
parse_syntax = = ParseSyntax : : MySQL ,
2023-03-06 09:08:55 +00:00
Pos ( * ) ( Pos , Pos , const String & , DateTime & ) ,
std : : function < Pos ( Pos , Pos , const String & , DateTime & ) > > ;
2023-03-20 16:24:06 +00:00
const Func func { } ;
const String func_name ;
const String literal ; /// Only used when current instruction parses literal
const String fragment ; /// Parsed fragments in MySQL or Joda format string
2023-02-22 12:42:59 +00:00
2023-02-27 13:41:38 +00:00
public :
2023-03-20 16:24:06 +00:00
explicit Instruction ( Func & & func_ , const char * func_name_ , const std : : string_view & fragment_ )
: func ( std : : move ( func_ ) ) , func_name ( func_name_ ) , fragment ( fragment_ )
2023-03-06 09:08:55 +00:00
{
}
2023-02-22 12:42:59 +00:00
2023-03-20 16:24:06 +00:00
explicit Instruction ( const String & literal_ ) : literal ( literal_ ) , fragment ( " LITERAL " ) { }
explicit Instruction ( String & & literal_ ) : literal ( std : : move ( literal_ ) ) , fragment ( " 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 )
2023-03-20 16:24:06 +00:00
return " func: " + func_name + " ,fragment: " + fragment ;
2023-02-27 13:41:38 +00:00
else
2023-03-20 16:24:06 +00:00
return " literal: " + literal + " ,fragment: " + fragment ;
2023-02-27 13:41:38 +00:00
}
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 )
2023-03-20 16:24:06 +00:00
return func ( cur , end , fragment , date ) ;
2023-02-27 13:41:38 +00:00
else
{
2023-03-20 16:24:06 +00:00
/// literal:
checkSpace ( cur , end , literal . size ( ) , " insufficient space to parse literal " , fragment ) ;
2023-02-27 13:41:38 +00:00
if ( std : : string_view ( cur , literal . size ( ) ) ! = literal )
throw Exception (
2023-03-07 06:35:50 +00:00
ErrorCodes : : CANNOT_PARSE_DATETIME ,
2023-03-20 16:24:06 +00:00
" Unable to parse fragment {} from {} because literal {} is expected but {} provided " ,
fragment ,
2023-03-06 09:08:55 +00:00
std : : string_view ( cur , end - cur ) ,
literal ,
std : : string_view ( cur , literal . size ( ) ) ) ;
2023-02-27 13:41:38 +00:00
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-03-20 16:24:06 +00:00
static Pos readNumber2 ( Pos cur , Pos end , [[maybe_unused]] const String & fragment , T & res )
2023-02-27 13:41:38 +00:00
{
2023-03-03 02:42:35 +00:00
if constexpr ( need_check_space = = NeedCheckSpace : : Yes )
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 2 , " readNumber2 requires size >= 2 " , fragment ) ;
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-03-20 16:24:06 +00:00
static Pos readNumber3 ( Pos cur , Pos end , [[maybe_unused]] const String & fragment , T & res )
2023-02-27 13:41:38 +00:00
{
2023-03-03 02:42:35 +00:00
if constexpr ( need_check_space = = NeedCheckSpace : : Yes )
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 3 , " readNumber4 requires size >= 3 " , fragment ) ;
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-03-20 16:24:06 +00:00
static Pos readNumber4 ( Pos cur , Pos end , [[maybe_unused]] const String & fragment , T & res )
2023-02-27 13:41:38 +00:00
{
2023-03-03 02:42:35 +00:00
if constexpr ( need_check_space = = NeedCheckSpace : : Yes )
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 4 , " readNumber4 requires size >= 4 " , fragment ) ;
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-20 16:24:06 +00:00
static void checkSpace ( Pos cur , Pos end , size_t len , const String & msg , const String & fragment )
2023-02-27 13:41:38 +00:00
{
2023-03-05 08:24:05 +00:00
if ( cur > end | | cur + len > end ) [[unlikely]]
2023-03-06 09:08:55 +00:00
throw Exception (
ErrorCodes : : NOT_ENOUGH_SPACE ,
2023-03-20 16:24:06 +00:00
" Unable to parse fragment {} from {} because {} " ,
fragment ,
2023-03-06 09:08:55 +00:00
std : : string_view ( cur , end - cur ) ,
msg ) ;
2023-02-27 13:41:38 +00:00
}
2023-02-22 10:05:53 +00:00
2023-03-03 02:42:35 +00:00
template < NeedCheckSpace need_check_space >
2023-03-20 16:24:06 +00:00
static Pos assertChar ( Pos cur , Pos end , char expected , const String & fragment )
2023-02-27 13:41:38 +00:00
{
2023-03-03 02:42:35 +00:00
if constexpr ( need_check_space = = NeedCheckSpace : : Yes )
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 1 , " assertChar requires size >= 1 " , fragment ) ;
2023-02-22 10:05:53 +00:00
2023-04-06 08:40:39 +00:00
if ( * cur ! = expected ) [[unlikely]]
2023-03-06 09:08:55 +00:00
throw Exception (
2023-03-07 06:35:50 +00:00
ErrorCodes : : CANNOT_PARSE_DATETIME ,
2023-03-20 16:24:06 +00:00
" Unable to parse fragment {} from {} because char {} is expected but {} provided " ,
fragment ,
2023-03-06 09:08:55 +00:00
std : : string_view ( cur , end - cur ) ,
2023-03-20 15:20:57 +00:00
String ( expected , 1 ) ,
2023-03-06 09:08:55 +00:00
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-04-06 08:40:39 +00:00
template < NeedCheckSpace need_check_space >
static Pos assertNumber ( Pos cur , Pos end , const String & fragment )
{
if constexpr ( need_check_space = = NeedCheckSpace : : Yes )
checkSpace ( cur , end , 1 , " assertChar requires size >= 1 " , fragment ) ;
if ( * cur < ' 0 ' | | * cur > ' 9 ' ) [[unlikely]]
throw Exception (
ErrorCodes : : CANNOT_PARSE_DATETIME ,
" Unable to parse fragment {} from {} because {} is not a number " ,
fragment ,
std : : string_view ( cur , end - cur ) ,
String ( * cur , 1 ) ) ;
+ + cur ;
return cur ;
}
2023-03-20 16:24:06 +00:00
static Pos mysqlDayOfWeekTextShort ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 3 , " mysqlDayOfWeekTextShort requires size >= 3 " , fragment ) ;
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-06 09:08:55 +00:00
throw Exception (
2023-03-07 06:35:50 +00:00
ErrorCodes : : CANNOT_PARSE_DATETIME ,
2023-03-20 16:24:06 +00:00
" Unable to parse fragment {} from {} because of unknown day of week short text {} " ,
fragment ,
2023-03-06 09:08:55 +00:00
std : : string_view ( cur , end - cur ) ,
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-20 16:24:06 +00:00
static Pos mysqlMonthOfYearTextShort ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 3 , " mysqlMonthOfYearTextShort requires size >= 3 " , fragment ) ;
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-06 09:08:55 +00:00
throw Exception (
2023-03-07 06:35:50 +00:00
ErrorCodes : : CANNOT_PARSE_DATETIME ,
2023-03-20 16:24:06 +00:00
" Unable to parse fragment {} from {} because of unknown month of year short text {} " ,
fragment ,
2023-03-06 09:08:55 +00:00
std : : string_view ( cur , end - cur ) ,
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-23 22:16:10 +00:00
static Pos mysqlMonthOfYearTextLong ( Pos cur , Pos end , const String & fragment , DateTime & date )
{
checkSpace ( cur , end , 3 , " mysqlMonthOfYearTextLong requires size >= 3 " , fragment ) ;
String text1 ( cur , 3 ) ;
boost : : to_lower ( text1 ) ;
auto it = monthMap . find ( text1 ) ;
if ( it = = monthMap . end ( ) )
throw Exception (
ErrorCodes : : CANNOT_PARSE_DATETIME ,
" Unable to parse first part of fragment {} from {} because of unknown month of year text: {} " ,
fragment ,
std : : string_view ( cur , end - cur ) ,
text1 ) ;
cur + = 3 ;
size_t expected_remaining_size = it - > second . first . size ( ) ;
checkSpace ( cur , end , expected_remaining_size , " mysqlMonthOfYearTextLong requires the second parg size >= " + std : : to_string ( expected_remaining_size ) , fragment ) ;
String text2 ( cur , expected_remaining_size ) ;
boost : : to_lower ( text2 ) ;
if ( text2 ! = it - > second . first )
throw Exception (
ErrorCodes : : CANNOT_PARSE_DATETIME ,
" Unable to parse second part of fragment {} from {} because of unknown month of year text: {} " ,
fragment ,
std : : string_view ( cur , end - cur ) ,
text1 + text2 ) ;
cur + = expected_remaining_size ;
date . setMonth ( it - > second . second ) ;
return cur ;
}
2023-03-20 16:24:06 +00:00
static Pos mysqlMonth ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-28 06:08:03 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 month ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : Yes > ( cur , end , fragment , 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-20 16:24:06 +00:00
static Pos mysqlCentury ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 century ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : Yes > ( cur , end , fragment , century ) ;
2023-03-03 02:42:35 +00:00
date . setCentury ( century ) ;
2023-02-27 13:41:38 +00:00
return cur ;
}
2023-02-22 10:05:53 +00:00
2023-03-20 16:24:06 +00:00
static Pos mysqlDayOfMonth ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 day_of_month ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : Yes > ( cur , end , fragment , day_of_month ) ;
2023-03-06 09:35:08 +00:00
date . setDayOfMonth ( day_of_month ) ;
2023-02-27 13:41:38 +00:00
return cur ;
}
2023-02-22 10:05:53 +00:00
2023-03-20 16:24:06 +00:00
static Pos mysqlAmericanDate ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 8 , " mysqlAmericanDate requires size >= 8 " , fragment ) ;
2023-03-01 09:15:29 +00:00
2023-02-28 15:02:02 +00:00
Int32 month ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : No > ( cur , end , fragment , month ) ;
cur = assertChar < NeedCheckSpace : : No > ( cur , end , ' / ' , fragment ) ;
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-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : No > ( cur , end , fragment , day ) ;
cur = assertChar < NeedCheckSpace : : No > ( cur , end , ' / ' , fragment ) ;
2023-03-06 09:35:08 +00:00
date . setDayOfMonth ( day ) ;
2023-02-22 10:05:53 +00:00
2023-02-28 15:02:02 +00:00
Int32 year ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : No > ( cur , end , fragment , 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-20 16:24:06 +00:00
static Pos mysqlDayOfMonthSpacePadded ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 2 , " mysqlDayOfMonthSpacePadded requires size >= 2 " , fragment ) ;
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-03-06 09:35:08 +00:00
date . setDayOfMonth ( day_of_month ) ;
2023-02-27 13:41:38 +00:00
return cur ;
}
2023-02-22 10:05:53 +00:00
2023-03-20 16:24:06 +00:00
static Pos mysqlISO8601Date ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 10 , " mysqlISO8601Date requires size >= 10 " , fragment ) ;
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-20 16:24:06 +00:00
cur = readNumber4 < Int32 , NeedCheckSpace : : No > ( cur , end , fragment , year ) ;
cur = assertChar < NeedCheckSpace : : No > ( cur , end , ' - ' , fragment ) ;
cur = readNumber2 < Int32 , NeedCheckSpace : : No > ( cur , end , fragment , month ) ;
cur = assertChar < NeedCheckSpace : : No > ( cur , end , ' - ' , fragment ) ;
cur = readNumber2 < Int32 , NeedCheckSpace : : No > ( cur , end , fragment , day ) ;
2023-03-01 09:15:29 +00:00
date . setYear ( year ) ;
date . setMonth ( month ) ;
2023-03-06 09:35:08 +00:00
date . setDayOfMonth ( day ) ;
2023-02-27 13:41:38 +00:00
return cur ;
}
2023-02-23 11:56:03 +00:00
2023-03-20 16:24:06 +00:00
static Pos mysqlISO8601Year2 ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 year2 ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : Yes > ( cur , end , fragment , 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-20 16:24:06 +00:00
static Pos mysqlISO8601Year4 ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 year ;
2023-03-20 16:24:06 +00:00
cur = readNumber4 < Int32 , NeedCheckSpace : : Yes > ( cur , end , fragment , 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-20 16:24:06 +00:00
static Pos mysqlDayOfYear ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 day_of_year ;
2023-03-20 16:24:06 +00:00
cur = readNumber3 < Int32 , NeedCheckSpace : : Yes > ( cur , end , fragment , day_of_year ) ;
2023-03-07 06:35:50 +00:00
date . setDayOfYear ( day_of_year ) ;
2023-02-27 13:41:38 +00:00
return cur ;
}
2023-02-23 11:56:03 +00:00
2023-03-20 16:24:06 +00:00
static Pos mysqlDayOfWeek ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 1 , " mysqlDayOfWeek requires size >= 1 " , fragment ) ;
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-20 16:24:06 +00:00
static Pos mysqlISO8601Week ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 week ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : Yes > ( cur , end , fragment , 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-20 16:24:06 +00:00
static Pos mysqlDayOfWeek0To6 ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 1 , " mysqlDayOfWeek requires size >= 1 " , fragment ) ;
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-20 16:24:06 +00:00
static Pos mysqlDayOfWeekTextLong ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-23 22:16:10 +00:00
checkSpace ( cur , end , 6 , " mysqlDayOfWeekTextLong requires size >= 6 " , fragment ) ;
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-06 09:08:55 +00:00
throw Exception (
2023-03-07 06:35:50 +00:00
ErrorCodes : : CANNOT_PARSE_DATETIME ,
2023-03-20 16:24:06 +00:00
" Unable to parse first part of fragment {} from {} because of unknown day of week text: {} " ,
fragment ,
2023-03-06 09:08:55 +00:00
std : : string_view ( cur , end - cur ) ,
text1 ) ;
2023-02-28 15:02:02 +00:00
cur + = 3 ;
2023-02-23 11:56:03 +00:00
2023-03-20 15:20:57 +00:00
size_t expected_remaining_size = it - > second . first . size ( ) ;
2023-03-23 22:16:10 +00:00
checkSpace ( cur , end , expected_remaining_size , " mysqlDayOfWeekTextLong requires the second parg size >= " + std : : to_string ( expected_remaining_size ) , fragment ) ;
2023-03-20 15:20:57 +00:00
String text2 ( cur , expected_remaining_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-06 09:08:55 +00:00
throw Exception (
2023-03-07 06:35:50 +00:00
ErrorCodes : : CANNOT_PARSE_DATETIME ,
2023-03-20 16:24:06 +00:00
" Unable to parse second part of fragment {} from {} because of unknown day of week text: {} " ,
fragment ,
2023-03-06 09:08:55 +00:00
std : : string_view ( cur , end - cur ) ,
text1 + text2 ) ;
2023-03-20 15:20:57 +00:00
cur + = expected_remaining_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-20 16:24:06 +00:00
static Pos mysqlYear2 ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 year2 ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : Yes > ( cur , end , fragment , year2 ) ;
2023-02-28 15:02:02 +00:00
date . setYear2 ( year2 ) ;
2023-02-27 13:41:38 +00:00
return cur ;
}
2023-03-20 16:24:06 +00:00
static Pos mysqlYear4 ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 year ;
2023-03-20 16:24:06 +00:00
cur = readNumber4 < Int32 , NeedCheckSpace : : Yes > ( cur , end , fragment , 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-20 16:24:06 +00:00
static Pos mysqlTimezoneOffset ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 5 , " mysqlTimezoneOffset requires size >= 5 " , fragment ) ;
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
2023-03-06 09:08:55 +00:00
throw Exception (
2023-03-07 06:35:50 +00:00
ErrorCodes : : CANNOT_PARSE_DATETIME ,
2023-03-20 16:24:06 +00:00
" Unable to parse fragment {} from {} because of unknown sign time zone offset: {} " ,
fragment ,
2023-03-06 09:08:55 +00:00
std : : string_view ( cur , end - cur ) ,
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-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : No > ( cur , end , fragment , hour ) ;
2023-02-22 12:42:59 +00:00
2023-02-27 13:41:38 +00:00
Int32 minute ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : No > ( cur , end , fragment , minute ) ;
2023-02-22 12:42:59 +00:00
2023-03-07 03:45:32 +00:00
date . has_time_zone_offset = true ;
date . time_zone_offset = sign * ( hour * 3600 + minute * 60 ) ;
2023-02-27 13:41:38 +00:00
return cur ;
}
2023-02-24 09:07:27 +00:00
2023-03-20 16:24:06 +00:00
static Pos mysqlMinute ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-28 06:08:03 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 minute ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : Yes > ( cur , end , fragment , 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-20 16:24:06 +00:00
static Pos mysqlAMPM ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-24 10:27:21 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 2 , " mysqlAMPM requires size >= 2 " , fragment ) ;
2023-02-27 13:41:38 +00:00
2023-02-28 15:02:02 +00:00
String text ( cur , 2 ) ;
2023-03-20 15:58:37 +00:00
boost : : to_lower ( text ) ;
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 10:27:21 +00:00
}
2023-02-24 09:07:27 +00:00
2023-03-20 16:24:06 +00:00
static Pos mysqlHHMM12 ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 8 , " mysqlHHMM12 requires size >= 8 " , fragment ) ;
2023-03-01 09:15:29 +00:00
2023-02-28 15:02:02 +00:00
Int32 hour ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : No > ( cur , end , fragment , hour ) ;
cur = assertChar < NeedCheckSpace : : No > ( cur , end , ' : ' , fragment ) ;
2023-02-28 15:02:02 +00:00
date . setHour ( hour , true , true ) ;
Int32 minute ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : No > ( cur , end , fragment , minute ) ;
cur = assertChar < NeedCheckSpace : : No > ( cur , end , ' ' , fragment ) ;
2023-02-28 15:02:02 +00:00
date . setMinute ( minute ) ;
2023-03-20 16:24:06 +00:00
cur = mysqlAMPM ( cur , end , fragment , date ) ;
2023-02-27 13:41:38 +00:00
return cur ;
}
2023-02-22 12:42:59 +00:00
2023-03-20 16:24:06 +00:00
static Pos mysqlHHMM24 ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 5 , " mysqlHHMM24 requires size >= 5 " , fragment ) ;
2023-03-01 09:15:29 +00:00
2023-02-28 15:02:02 +00:00
Int32 hour ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : No > ( cur , end , fragment , hour ) ;
cur = assertChar < NeedCheckSpace : : No > ( cur , end , ' : ' , fragment ) ;
2023-02-28 15:02:02 +00:00
date . setHour ( hour , false , false ) ;
Int32 minute ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : No > ( cur , end , fragment , 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-20 16:24:06 +00:00
static Pos mysqlSecond ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-28 06:08:03 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 second ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : Yes > ( cur , end , fragment , 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-04-05 08:26:00 +00:00
static Pos mysqlMicrosecond ( Pos cur , Pos end , const String & fragment , DateTime & /*date*/ )
{
2023-04-11 06:19:10 +00:00
checkSpace ( cur , end , 6 , " mysqlMicrosecond requires size >= 6 " , fragment ) ;
2023-04-06 08:40:39 +00:00
for ( size_t i = 0 ; i < 6 ; + + i )
2023-04-11 06:19:10 +00:00
cur = assertNumber < NeedCheckSpace : : No > ( cur , end , fragment ) ;
2023-04-05 08:26:00 +00:00
return cur ;
}
2023-03-20 16:24:06 +00:00
static Pos mysqlISO8601Time ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 8 , " mysqlISO8601Time requires size >= 8 " , fragment ) ;
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-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : No > ( cur , end , fragment , hour ) ;
cur = assertChar < NeedCheckSpace : : No > ( cur , end , ' : ' , fragment ) ;
cur = readNumber2 < Int32 , NeedCheckSpace : : No > ( cur , end , fragment , minute ) ;
cur = assertChar < NeedCheckSpace : : No > ( cur , end , ' : ' , fragment ) ;
cur = readNumber2 < Int32 , NeedCheckSpace : : No > ( cur , end , fragment , 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-20 16:24:06 +00:00
static Pos mysqlHour12 ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-22 12:42:59 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 hour ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : Yes > ( cur , end , fragment , 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-20 16:24:06 +00:00
static Pos mysqlHour24 ( Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 hour ;
2023-03-20 16:24:06 +00:00
cur = readNumber2 < Int32 , NeedCheckSpace : : Yes > ( cur , end , fragment , 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 ,
2023-03-26 20:00:04 +00:00
size_t repetitions ,
size_t max_digits_to_read ,
2023-03-20 16:24:06 +00:00
const String & fragment ,
2023-03-23 04:13:51 +00:00
Int32 & result )
2023-02-27 13:41:38 +00:00
{
2023-03-26 20:00:04 +00:00
2023-02-27 13:41:38 +00:00
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-03-23 04:13:51 +00:00
Int64 number = 0 ;
2023-03-20 15:20:57 +00:00
const Pos start = cur ;
2023-03-26 20:00:04 +00:00
/// Avoid integer overflow in (*)
if ( max_digits_to_read > = std : : numeric_limits < decltype ( number ) > : : digits10 ) [[unlikely]]
throw Exception (
ErrorCodes : : CANNOT_PARSE_DATETIME ,
" Unable to parse fragment {} from {} because max_digits_to_read is too big " ,
fragment ,
std : : string_view ( start , cur - start ) ) ;
2023-02-27 13:41:38 +00:00
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
2023-03-26 20:00:04 +00:00
size_t count = 0 ;
2023-03-20 15:20:57 +00:00
while ( cur < end & & cur < start + max_digits_to_read & & * cur > = ' 0 ' & & * cur < = ' 9 ' )
2023-02-27 13:41:38 +00:00
{
2023-03-26 20:00:04 +00:00
number = number * 10 + ( * cur - ' 0 ' ) ; /// (*)
2023-02-27 13:41:38 +00:00
+ + cur ;
+ + count ;
}
if ( count = = 2 )
{
if ( number > = 70 )
number + = 1900 ;
else if ( number > = 0 & & number < 70 )
number + = 2000 ;
}
else
{
2023-03-20 15:20:57 +00:00
while ( cur < end & & cur < start + max_digits_to_read & & * cur > = ' 0 ' & & * cur < = ' 9 ' )
2023-02-27 13:41:38 +00:00
{
2023-03-26 20:00:04 +00:00
number = number * 10 + ( * cur - ' 0 ' ) ; /// (*)
2023-02-27 13:41:38 +00:00
+ + cur ;
}
}
}
else
{
2023-03-20 15:20:57 +00:00
while ( cur < end & & cur < start + max_digits_to_read & & * cur > = ' 0 ' & & * cur < = ' 9 ' )
2023-02-27 13:41:38 +00:00
{
number = number * 10 + ( * cur - ' 0 ' ) ;
+ + cur ;
}
}
2023-02-24 09:07:27 +00:00
2023-03-26 20:00:04 +00:00
if ( negative )
number * = - 1 ;
2023-02-27 13:41:38 +00:00
/// Need to have read at least one digit.
2023-03-26 20:00:04 +00:00
if ( cur = = start ) [[unlikely]]
2023-03-06 09:08:55 +00:00
throw Exception (
2023-03-07 06:35:50 +00:00
ErrorCodes : : CANNOT_PARSE_DATETIME ,
2023-03-20 16:24:06 +00:00
" Unable to parse fragment {} from {} because read number failed " ,
fragment ,
2023-03-06 09:08:55 +00:00
std : : string_view ( cur , end - cur ) ) ;
2023-02-24 09:07:27 +00:00
2023-03-23 04:13:51 +00:00
/// Check if number exceeds the range of Int32
2023-03-26 20:00:04 +00:00
if ( number < std : : numeric_limits < Int32 > : : min ( ) | | number > std : : numeric_limits < Int32 > : : max ( ) ) [[unlikely]]
2023-03-23 04:13:51 +00:00
throw Exception (
ErrorCodes : : CANNOT_PARSE_DATETIME ,
" Unable to parse fragment {} from {} because number is out of range of Int32 " ,
fragment ,
std : : string_view ( start , cur - start ) ) ;
2023-03-26 20:00:04 +00:00
2023-03-23 04:13:51 +00:00
result = static_cast < Int32 > ( number ) ;
2023-02-27 13:41:38 +00:00
return cur ;
}
2023-02-24 09:07:27 +00:00
2023-03-20 16:24:06 +00:00
static Pos jodaEra ( int , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 2 , " jodaEra requires size >= 2 " , fragment ) ;
2023-02-28 12:42:16 +00:00
2023-02-28 15:02:02 +00:00
String era ( cur , 2 ) ;
2023-03-20 15:58:37 +00:00
boost : : to_lower ( era ) ;
2023-02-28 15:02:02 +00:00
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-26 20:00:04 +00:00
static Pos jodaCenturyOfEra ( size_t repetitions , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 century ;
2023-03-20 16:24:06 +00:00
cur = readNumberWithVariableLength ( cur , end , false , false , false , repetitions , repetitions , fragment , 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-26 20:00:04 +00:00
static Pos jodaYearOfEra ( size_t repetitions , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 year_of_era ;
2023-03-20 16:24:06 +00:00
cur = readNumberWithVariableLength ( cur , end , false , false , true , repetitions , repetitions , fragment , year_of_era ) ;
2023-02-28 15:02:02 +00:00
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-26 20:00:04 +00:00
static Pos jodaWeekYear ( size_t repetitions , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 week_year ;
2023-03-20 16:24:06 +00:00
cur = readNumberWithVariableLength ( cur , end , true , true , true , repetitions , repetitions , fragment , week_year ) ;
2023-02-28 15:02:02 +00:00
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-26 20:00:04 +00:00
static Pos jodaWeekOfWeekYear ( size_t repetitions , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 week ;
2023-03-26 20:00:04 +00:00
cur = readNumberWithVariableLength ( cur , end , false , false , false , repetitions , std : : max ( repetitions , 2u z ) , fragment , 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-26 20:00:04 +00:00
static Pos jodaDayOfWeek1Based ( size_t repetitions , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 day_of_week ;
2023-03-20 16:24:06 +00:00
cur = readNumberWithVariableLength ( cur , end , false , false , false , repetitions , repetitions , fragment , day_of_week ) ;
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-06 09:08:55 +00:00
static Pos
2023-03-20 16:24:06 +00:00
jodaDayOfWeekText ( size_t /*min_represent_digits*/ , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 3 , " jodaDayOfWeekText requires size >= 3 " , fragment ) ;
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-06 09:08:55 +00:00
throw Exception (
2023-03-07 06:35:50 +00:00
ErrorCodes : : CANNOT_PARSE_DATETIME ,
2023-03-20 16:24:06 +00:00
" Unable to parse fragment {} from {} because of unknown day of week text: {} " ,
fragment ,
2023-03-06 09:08:55 +00:00
std : : string_view ( cur , end - cur ) ,
text1 ) ;
2023-02-27 13:41:38 +00:00
cur + = 3 ;
2023-02-28 15:02:02 +00:00
date . setDayOfWeek ( it - > second . second ) ;
2023-03-20 15:20:57 +00:00
size_t expected_remaining_size = it - > second . first . size ( ) ;
if ( cur + expected_remaining_size < = end )
2023-02-27 13:41:38 +00:00
{
2023-03-20 15:20:57 +00:00
String text2 ( cur , expected_remaining_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-03-20 15:20:57 +00:00
cur + = expected_remaining_size ;
2023-02-27 13:41:38 +00:00
return cur ;
}
}
return cur ;
}
2023-02-24 09:07:27 +00:00
2023-03-26 20:00:04 +00:00
static Pos jodaYear ( size_t repetitions , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 year ;
2023-03-20 16:24:06 +00:00
cur = readNumberWithVariableLength ( cur , end , true , true , true , repetitions , repetitions , fragment , 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-26 20:00:04 +00:00
static Pos jodaDayOfYear ( size_t repetitions , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 day_of_year ;
2023-03-26 20:00:04 +00:00
cur = readNumberWithVariableLength ( cur , end , false , false , false , repetitions , std : : max ( repetitions , 3u z ) , fragment , day_of_year ) ;
2023-03-07 06:35:50 +00:00
date . setDayOfYear ( day_of_year ) ;
2023-02-27 13:41:38 +00:00
return cur ;
}
2023-02-24 09:07:27 +00:00
2023-03-26 20:00:04 +00:00
static Pos jodaMonthOfYear ( size_t repetitions , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 month ;
2023-03-20 16:24:06 +00:00
cur = readNumberWithVariableLength ( cur , end , false , false , false , repetitions , 2 , fragment , month ) ;
2023-02-28 15:02:02 +00:00
date . setMonth ( month ) ;
2023-02-27 13:41:38 +00:00
return cur ;
}
2023-02-24 09:07:27 +00:00
2023-03-20 16:24:06 +00:00
static Pos jodaMonthOfYearText ( int , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 3 , " jodaMonthOfYearText requires size >= 3 " , fragment ) ;
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-06 09:08:55 +00:00
throw Exception (
2023-03-07 06:35:50 +00:00
ErrorCodes : : CANNOT_PARSE_DATETIME ,
2023-03-20 16:24:06 +00:00
" Unable to parse fragment {} from {} because of unknown month of year text: {} " ,
fragment ,
2023-03-06 09:08:55 +00:00
std : : string_view ( cur , end - cur ) ,
text1 ) ;
2023-02-27 13:41:38 +00:00
cur + = 3 ;
2023-02-28 15:02:02 +00:00
date . setMonth ( it - > second . second ) ;
2023-03-20 15:20:57 +00:00
size_t expected_remaining_size = it - > second . first . size ( ) ;
if ( cur + expected_remaining_size < = end )
2023-02-27 13:41:38 +00:00
{
2023-03-20 15:20:57 +00:00
String text2 ( cur , expected_remaining_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-03-20 15:20:57 +00:00
cur + = expected_remaining_size ;
2023-02-27 13:41:38 +00:00
return cur ;
}
}
return cur ;
}
2023-02-24 09:07:27 +00:00
2023-03-26 20:00:04 +00:00
static Pos jodaDayOfMonth ( size_t repetitions , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 day_of_month ;
2023-03-06 09:08:55 +00:00
cur = readNumberWithVariableLength (
2023-03-26 20:00:04 +00:00
cur , end , false , false , false , repetitions , std : : max ( repetitions , 2u z ) , fragment , day_of_month ) ;
2023-03-06 09:35:08 +00:00
date . setDayOfMonth ( day_of_month ) ;
2023-02-27 13:41:38 +00:00
return cur ;
}
2023-02-24 09:07:27 +00:00
2023-03-20 16:24:06 +00:00
static Pos jodaHalfDayOfDay ( int , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-03-20 16:24:06 +00:00
checkSpace ( cur , end , 2 , " jodaHalfDayOfDay requires size >= 2 " , fragment ) ;
2023-02-27 13:41:38 +00:00
String text ( cur , 2 ) ;
2023-03-20 15:58:37 +00:00
boost : : to_lower ( text ) ;
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-26 20:00:04 +00:00
static Pos jodaHourOfHalfDay ( size_t repetitions , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 hour ;
2023-03-26 20:00:04 +00:00
cur = readNumberWithVariableLength ( cur , end , false , false , false , repetitions , std : : max ( repetitions , 2u z ) , fragment , hour ) ;
2023-02-28 15:02:02 +00:00
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-26 20:00:04 +00:00
static Pos jodaClockHourOfHalfDay ( size_t repetitions , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 hour ;
2023-03-26 20:00:04 +00:00
cur = readNumberWithVariableLength ( cur , end , false , false , false , repetitions , std : : max ( repetitions , 2u z ) , fragment , 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-24 09:07:27 +00:00
2023-03-26 20:00:04 +00:00
static Pos jodaHourOfDay ( size_t repetitions , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 hour ;
2023-03-26 20:00:04 +00:00
cur = readNumberWithVariableLength ( cur , end , false , false , false , repetitions , std : : max ( repetitions , 2u z ) , fragment , 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-24 09:07:27 +00:00
2023-03-26 20:00:04 +00:00
static Pos jodaClockHourOfDay ( size_t repetitions , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 hour ;
2023-03-26 20:00:04 +00:00
cur = readNumberWithVariableLength ( cur , end , false , false , false , repetitions , std : : max ( repetitions , 2u z ) , fragment , hour ) ;
2023-02-28 15:02:02 +00:00
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-26 20:00:04 +00:00
static Pos jodaMinuteOfHour ( size_t repetitions , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 minute ;
2023-03-26 20:00:04 +00:00
cur = readNumberWithVariableLength ( cur , end , false , false , false , repetitions , std : : max ( repetitions , 2u z ) , fragment , minute ) ;
2023-02-28 15:02:02 +00:00
date . setMinute ( minute ) ;
2023-02-27 13:41:38 +00:00
return cur ;
}
2023-02-24 09:07:27 +00:00
2023-03-26 20:00:04 +00:00
static Pos jodaSecondOfMinute ( size_t repetitions , Pos cur , Pos end , const String & fragment , DateTime & date )
2023-02-27 13:41:38 +00:00
{
2023-02-28 15:02:02 +00:00
Int32 second ;
2023-03-26 20:00:04 +00:00
cur = readNumberWithVariableLength ( cur , end , false , false , false , repetitions , std : : max ( repetitions , 2u z ) , fragment , second ) ;
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-06 09:08:55 +00:00
std : : vector < Instruction > parseFormat ( const String & format ) const
2023-02-27 13:41:38 +00:00
{
2023-03-06 09:08:55 +00:00
static_assert (
2023-03-20 15:58:37 +00:00
parse_syntax = = ParseSyntax : : MySQL | | parse_syntax = = ParseSyntax : : Joda ,
2023-03-06 09:08:55 +00:00
" parse syntax must be one of MySQL or Joda " ) ;
2023-03-20 15:58:37 +00:00
if constexpr ( parse_syntax = = ParseSyntax : : MySQL )
2023-03-06 09:08:55 +00:00
return parseMysqlFormat ( format ) ;
2023-02-27 13:41:38 +00:00
else
2023-03-06 09:08:55 +00:00
return parseJodaFormat ( format ) ;
2023-02-27 13:41:38 +00:00
}
2023-02-24 09:07:27 +00:00
2023-03-06 09:08:55 +00:00
std : : vector < Instruction > parseMysqlFormat ( const String & format ) const
2023-02-27 13:41:38 +00:00
{
2023-03-06 09:08:55 +00:00
# define ACTION_ARGS(func) &(func), #func, std::string_view(pos - 1, 2)
2023-02-22 10:05:53 +00:00
2023-02-27 13:41:38 +00:00
Pos pos = format . data ( ) ;
2023-03-06 09:08:55 +00:00
Pos end = format . data ( ) + format . size ( ) ;
std : : vector < Instruction > instructions ;
2023-02-27 13:41:38 +00:00
while ( true )
{
2023-03-05 08:24:05 +00:00
Pos next_percent_pos = find_first_symbols < ' % ' > ( pos , end ) ;
2023-03-06 09:08:55 +00:00
2023-03-05 08:24:05 +00:00
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 )
2023-03-20 15:58:37 +00:00
throw Exception (
ErrorCodes : : BAD_ARGUMENTS , " '%' must not be the last character in the format string, use '%%' instead " ) ;
2023-02-27 13:41:38 +00:00
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 ;
2023-04-05 08:26:00 +00:00
// Fractional seconds
case ' f ' :
instructions . emplace_back ( ACTION_ARGS ( Instruction : : mysqlMicrosecond ) ) ;
break ;
2023-02-27 13:41:38 +00:00
// 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 ;
2023-03-20 15:58:37 +00:00
// Weekday as a integer number with Sunday as 0 (0-6) 4
2023-02-27 13:41:38 +00:00
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 ;
2023-04-05 12:25:51 +00:00
// Depending on a setting
// - Full month [January...December]
// - Minute (00-59) OR
2023-02-27 13:41:38 +00:00
case ' M ' :
2023-04-05 12:25:51 +00:00
if ( mysql_M_is_month_name )
instructions . emplace_back ( ACTION_ARGS ( Instruction : : mysqlMonthOfYearTextLong ) ) ;
else
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 ' % ' :
2023-03-20 15:20:57 +00:00
instructions . emplace_back ( " % " ) ;
2023-02-27 13:41:38 +00:00
break ;
2023-03-20 15:20:57 +00:00
/// Unimplemented
/// Fractional seconds
2023-02-27 13:41:38 +00:00
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
{
2023-03-20 15:58:37 +00:00
/// Handle characters after last %
2023-02-27 13:41:38 +00:00
if ( pos < end )
instructions . emplace_back ( String ( pos , end - pos ) ) ;
break ;
}
2023-02-24 09:07:27 +00:00
}
2023-03-06 09:08:55 +00:00
return instructions ;
2023-02-27 13:41:38 +00:00
# undef ACTION_ARGS
}
2023-03-06 09:08:55 +00:00
std : : vector < Instruction > parseJodaFormat ( const String & format ) const
2023-02-27 13:41:38 +00:00
{
2023-03-06 09:08:55 +00:00
# define ACTION_ARGS_WITH_BIND(func, arg) std::bind_front(&(func), (arg)), #func, std::string_view(cur_token, repetitions)
2023-02-27 13:41:38 +00:00
2023-03-20 15:20:57 +00:00
Pos pos = format . data ( ) ;
Pos end = format . data ( ) + format . size ( ) ;
2023-02-27 13:41:38 +00:00
2023-03-06 09:08:55 +00:00
std : : vector < Instruction > instructions ;
2023-02-27 13:41:38 +00:00
while ( pos < end )
2023-02-22 10:05:53 +00:00
{
2023-03-20 15:20:57 +00:00
Pos cur_token = pos ;
2023-02-27 13:41:38 +00:00
// 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 ) ) ;
if ( * ( cur_token + i ) = = ' \' ' )
i + = 1 ;
}
pos + = count + 2 ;
}
}
}
else
{
2023-03-26 20:00:04 +00:00
size_t repetitions = 1 ;
2023-02-27 13:41:38 +00:00
+ + 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 " ) ;
case ' z ' :
throw Exception ( ErrorCodes : : NOT_IMPLEMENTED , " format is not supported for timezone " ) ;
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-03-06 09:08:55 +00:00
return instructions ;
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
{
const auto * format_column = checkAndGetColumnConst < ColumnString > ( arguments [ 1 ] . column . get ( ) ) ;
2023-03-22 08:45:46 +00:00
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 ( ) ) ;
2023-02-27 13:41:38 +00:00
return format_column - > getValue < String > ( ) ;
}
2023-03-06 09:08:55 +00:00
const DateLUTImpl & 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 )
2023-03-06 09:08:55 +00:00
return DateLUT : : instance ( ) ;
2023-02-27 13:41:38 +00:00
const auto * col = checkAndGetColumnConst < ColumnString > ( arguments [ 2 ] . column . get ( ) ) ;
if ( ! col )
throw Exception (
ErrorCodes : : ILLEGAL_COLUMN ,
2023-03-20 15:58:37 +00:00
" Illegal column {} of third ('timezone') argument of function {}. Must be constant String. " ,
2023-02-27 13:41:38 +00:00
arguments [ 2 ] . column - > getName ( ) ,
getName ( ) ) ;
String time_zone = col - > getValue < String > ( ) ;
2023-03-06 09:08:55 +00:00
return DateLUT : : instance ( 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-03-26 13:43:15 +00:00
struct NameParseDateTimeOrZero
{
static constexpr auto name = " parseDateTimeOrZero " ;
} ;
struct NameParseDateTimeOrNull
{
static constexpr auto name = " parseDateTimeOrNull " ;
} ;
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-03-26 13:43:15 +00:00
struct NameParseDateTimeInJodaSyntaxOrZero
{
static constexpr auto name = " parseDateTimeInJodaSyntaxOrZero " ;
} ;
struct NameParseDateTimeInJodaSyntaxOrNull
{
static constexpr auto name = " parseDateTimeInJodaSyntaxOrNull " ;
} ;
2023-02-22 10:05:53 +00:00
2023-03-26 13:43:15 +00:00
using FunctionParseDateTime = FunctionParseDateTimeImpl < NameParseDateTime , ParseSyntax : : MySQL , ErrorHandling : : Exception > ;
using FunctionParseDateTimeOrZero = FunctionParseDateTimeImpl < NameParseDateTimeOrZero , ParseSyntax : : MySQL , ErrorHandling : : Zero > ;
using FunctionParseDateTimeOrNull = FunctionParseDateTimeImpl < NameParseDateTimeOrNull , ParseSyntax : : MySQL , ErrorHandling : : Null > ;
using FunctionParseDateTimeInJodaSyntax = FunctionParseDateTimeImpl < NameParseDateTimeInJodaSyntax , ParseSyntax : : Joda , ErrorHandling : : Exception > ;
using FunctionParseDateTimeInJodaSyntaxOrZero = FunctionParseDateTimeImpl < NameParseDateTimeInJodaSyntaxOrZero , ParseSyntax : : Joda , ErrorHandling : : Zero > ;
using FunctionParseDateTimeInJodaSyntaxOrNull = FunctionParseDateTimeImpl < NameParseDateTimeInJodaSyntaxOrNull , ParseSyntax : : Joda , ErrorHandling : : Null > ;
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 > ( ) ;
2023-03-20 15:58:37 +00:00
factory . registerAlias ( " TO_UNIXTIME " , FunctionParseDateTime : : name ) ;
2023-03-26 13:43:15 +00:00
factory . registerFunction < FunctionParseDateTimeOrZero > ( ) ;
factory . registerFunction < FunctionParseDateTimeOrNull > ( ) ;
2023-03-30 12:03:52 +00:00
factory . registerAlias ( " str_to_date " , FunctionParseDateTimeOrNull : : name , FunctionFactory : : CaseInsensitive ) ;
2023-02-27 13:41:38 +00:00
factory . registerFunction < FunctionParseDateTimeInJodaSyntax > ( ) ;
2023-03-26 13:43:15 +00:00
factory . registerFunction < FunctionParseDateTimeInJodaSyntaxOrZero > ( ) ;
factory . registerFunction < FunctionParseDateTimeInJodaSyntaxOrNull > ( ) ;
2023-02-22 10:05:53 +00:00
}
2023-02-24 09:07:27 +00:00
2023-02-22 10:05:53 +00:00
}