[Enhancement] Optimize trunc_to_month and trunc_to_quarter for date (#63245)
## Why I'm doing: Given a Julian date, our current approach to obtaining `trunc_to_quarter` or `trunc_to_month` is: 1. First decode it into year, month, and day. 2. Then, we adjust the corresponding month and day, 3. and finally compute the new Julian date from the adjusted year, month, and day. However, in practice, it is unnecessary to compute a new Julian date from the adjusted values. Instead, we can directly calculate the offset between the target Julian and the current Julian based on the decoded year, month, and day. benchmark: speedup around 50% - trunc_to_month: https://quick-bench.com/q/fZiRaC7U1pHODHsWQuO1cvFN2qM - trunc_to_quarter: https://quick-bench.com/q/Idw7wLB74k-5PNW2u4xcXNsoeKA Signed-off-by: zihe.liu <ziheliu1024@gmail.com>
This commit is contained in:
parent
5c2aaaf717
commit
1ffaebab6f
|
|
@ -518,6 +518,10 @@ bool date::is_leap(int year) {
|
|||
return ((year % 4) == 0) && ((year % 100 != 0) || ((year % 400) == 0 && year));
|
||||
}
|
||||
|
||||
bool date::is_leap_for_julian(int year) {
|
||||
return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
|
||||
}
|
||||
|
||||
void date::to_string(int year, int month, int day, char* to) {
|
||||
uint32_t temp;
|
||||
temp = year / 100;
|
||||
|
|
|
|||
|
|
@ -112,6 +112,10 @@ public:
|
|||
static int get_days_after_monday(JulianDate julian);
|
||||
|
||||
static bool is_leap(int year);
|
||||
// For historical reasons, our `is_leap` function treats year 0 as a non-leap year. However, in mathematics and
|
||||
// in the Julian representation, year 0 is considered a leap year. Therefore, when performing day shifts based on
|
||||
// the Julian representation, year 0 should be treated as a leap year.
|
||||
static bool is_leap_for_julian(int year);
|
||||
|
||||
static int64_t standardize_date(int64_t value);
|
||||
|
||||
|
|
|
|||
|
|
@ -24,11 +24,19 @@ static const std::string s_day_name[] = {"Sunday", "Monday", "Tuesday", "Wednesd
|
|||
static const char* s_month_name[] = {"", "January", "February", "March", "April", "May", "June",
|
||||
"July", "August", "September", "October", "November", "December", nullptr};
|
||||
|
||||
static int month_to_quarter[13] = {0, 1, 1, 1, 4, 4, 4, 7, 7, 7, 10, 10, 10};
|
||||
static int day_to_first[8] = {0 /*never use*/, 6, 0, 1, 2, 3, 4, 5};
|
||||
static constexpr int s_days_in_month[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
static int month_to_quarter_end[13] = {0, 3, 3, 3, 6, 6, 6, 9, 9, 9, 12, 12, 12};
|
||||
|
||||
// Stores the number of days from the beginning of the quarter up to the 1st day of month i (exclusive).
|
||||
static constexpr int quarter_month_day_offset[13] = {
|
||||
0, // placeholder
|
||||
0, s_days_in_month[1], s_days_in_month[1] + s_days_in_month[2], // quarter 1
|
||||
0, s_days_in_month[4], s_days_in_month[4] + s_days_in_month[5], // quarter 2
|
||||
0, s_days_in_month[7], s_days_in_month[7] + s_days_in_month[8], // quarter 3
|
||||
0, s_days_in_month[10], s_days_in_month[10] + s_days_in_month[11] // quarter 4
|
||||
};
|
||||
|
||||
const DateValue DateValue::MAX_DATE_VALUE{date::MAX_DATE};
|
||||
const DateValue DateValue::MIN_DATE_VALUE{date::MIN_DATE};
|
||||
|
||||
|
|
@ -130,7 +138,7 @@ void DateValue::trunc_to_day() {}
|
|||
void DateValue::trunc_to_month() {
|
||||
int year, month, day;
|
||||
date::to_date_with_cache(_julian, &year, &month, &day);
|
||||
_julian = date::from_date(year, month, 1);
|
||||
_julian -= (day - 1);
|
||||
}
|
||||
|
||||
void DateValue::trunc_to_year() {
|
||||
|
|
@ -148,7 +156,9 @@ void DateValue::trunc_to_week() {
|
|||
void DateValue::trunc_to_quarter() {
|
||||
int year, month, day;
|
||||
date::to_date_with_cache(_julian, &year, &month, &day);
|
||||
_julian = date::from_date(year, month_to_quarter[month], 1);
|
||||
// Only March needs to add the full number of days in February,
|
||||
// so an extra day should be added only in leap years when the month is March.
|
||||
_julian -= quarter_month_day_offset[month] + (day - 1) + (month == 3 && date::is_leap_for_julian(year));
|
||||
}
|
||||
|
||||
void DateValue::set_end_of_month() {
|
||||
|
|
|
|||
|
|
@ -284,4 +284,38 @@ TEST(DateValueTest, weekday) {
|
|||
ASSERT_EQ(0, dv.weekday()); // Sunday
|
||||
}
|
||||
|
||||
TEST(DateValueTest, test_trunc_to_month) {
|
||||
for (DateValue date = DateValue::MIN_DATE_VALUE; date <= DateValue::MAX_DATE_VALUE; date._julian++) {
|
||||
DateValue res_date = date;
|
||||
res_date.trunc_to_month();
|
||||
|
||||
int year0, month0, day0;
|
||||
date::to_date_with_cache(date.julian(), &year0, &month0, &day0);
|
||||
int year1, month1, day1;
|
||||
date::to_date_with_cache(res_date.julian(), &year1, &month1, &day1);
|
||||
|
||||
ASSERT_EQ(year0, year1);
|
||||
ASSERT_EQ(month0, month1);
|
||||
ASSERT_EQ(1, day1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DateValueTest, test_trunc_to_quarter) {
|
||||
static int month_to_quarter[13] = {0, 1, 1, 1, 4, 4, 4, 7, 7, 7, 10, 10, 10};
|
||||
|
||||
for (DateValue date = DateValue::MIN_DATE_VALUE; date <= DateValue::MAX_DATE_VALUE; date._julian++) {
|
||||
DateValue res_date = date;
|
||||
res_date.trunc_to_quarter();
|
||||
|
||||
int year0, month0, day0;
|
||||
date::to_date_with_cache(date.julian(), &year0, &month0, &day0);
|
||||
int year1, month1, day1;
|
||||
date::to_date_with_cache(res_date.julian(), &year1, &month1, &day1);
|
||||
|
||||
ASSERT_EQ(year0, year1);
|
||||
ASSERT_EQ(month_to_quarter[month0], month1);
|
||||
ASSERT_EQ(1, day1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace starrocks
|
||||
|
|
|
|||
Loading…
Reference in New Issue