Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(query): migrate datetime rounder functions to v2. #8281

Merged
merged 5 commits into from
Oct 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,12 @@ to_monday( <expr> )

## Return Type

`UInt16` datatype.
Datetime object, returns date in “YYYY-MM-DD” format.

## Examples

```sql
SELECT to_monday(now());
+------------------+
| to_monday(now()) |
+------------------+
| 19079 |
+------------------+

SELECT to_date(to_monday(now()));
+---------------------------+
| to_date(to_monday(now())) |
+---------------------------+
Expand All @@ -42,6 +35,6 @@ SELECT to_monday(to_timestamp(1630812366));
+-------------------------------------+
| to_monday(to_timestamp(1630812366)) |
+-------------------------------------+
| 18869 |
| 2021-08-30 |
+-------------------------------------+
```
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
title: TO_START_OF_WEEK
---

Returns the first day of the year for a date or a date with time (timestamp/datetime).
Returns the first day of the week for a date or a date with time (timestamp/datetime).
The first day of a week can be Sunday or Monday, which is specified by the argument `mode`.

## Syntax

Expand All @@ -15,6 +16,7 @@ to_start_of_week(expr)
| Arguments | Description |
| ----------- | ----------- |
| expr | date/timestamp |
| mode | Optional. If it is 0, the result is Sunday, otherwise, the result is Monday. The default value is 0 |

## Return Type

Expand Down
127 changes: 127 additions & 0 deletions src/query/expression/src/date_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,130 @@ impl ToNumber<u8> for ToSecond {
dt.second() as u8
}
}

#[derive(Clone, Copy)]
pub enum Round {
Second,
Minute,
FiveMinutes,
TenMinutes,
FifteenMinutes,
TimeSlot,
Hour,
Day,
}

pub fn round_timestamp(ts: i64, tz: &Tz, round: Round) -> i64 {
let dt = tz.timestamp(ts / 1_000_000, 0_u32);
let res = match round {
Round::Second => dt,
Round::Minute => {
tz.ymd(dt.year(), dt.month(), dt.day())
.and_hms_micro(dt.hour(), dt.minute(), 0, 0)
}
Round::FiveMinutes => tz.ymd(dt.year(), dt.month(), dt.day()).and_hms_micro(
dt.hour(),
dt.minute() / 5 * 5,
0,
0,
),
Round::TenMinutes => tz.ymd(dt.year(), dt.month(), dt.day()).and_hms_micro(
dt.hour(),
dt.minute() / 10 * 10,
0,
0,
),
Round::FifteenMinutes => tz.ymd(dt.year(), dt.month(), dt.day()).and_hms_micro(
dt.hour(),
dt.minute() / 15 * 15,
0,
0,
),
Round::TimeSlot => tz.ymd(dt.year(), dt.month(), dt.day()).and_hms_micro(
dt.hour(),
dt.minute() / 30 * 30,
0,
0,
),
Round::Hour => tz
.ymd(dt.year(), dt.month(), dt.day())
.and_hms_micro(dt.hour(), 0, 0, 0),
Round::Day => tz
.ymd(dt.year(), dt.month(), dt.day())
.and_hms_micro(0, 0, 0, 0),
};
res.timestamp_micros()
}

pub struct DateRounder;

impl DateRounder {
pub fn eval_timestamp<T: ToNumber<i32>>(ts: i64, tz: &Tz) -> i32 {
let dt = ts.to_timestamp(tz);
T::to_number(&dt)
}

pub fn eval_date<T: ToNumber<i32>>(date: i32, tz: &Tz) -> i32 {
let dt = date.to_date(tz).and_hms(0, 0, 0);
T::to_number(&dt)
}
}

/// Convert `chrono::DateTime` to `i32` in `Scalar::Date(i32)` for `DateType`.
///
/// It's the days since 1970-01-01.
#[inline]
fn datetime_to_date_inner_number(date: &DateTime<Tz>) -> i32 {
date.naive_utc()
.signed_duration_since(NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0))
.num_days() as i32
}

pub struct ToLastMonday;
pub struct ToLastSunday;
pub struct ToStartOfMonth;
pub struct ToStartOfQuarter;
pub struct ToStartOfYear;
pub struct ToStartOfISOYear;

impl ToNumber<i32> for ToLastMonday {
fn to_number(dt: &DateTime<Tz>) -> i32 {
datetime_to_date_inner_number(dt) - dt.weekday().num_days_from_monday() as i32
}
}

impl ToNumber<i32> for ToLastSunday {
fn to_number(dt: &DateTime<Tz>) -> i32 {
datetime_to_date_inner_number(dt) - dt.weekday().num_days_from_sunday() as i32
}
}

impl ToNumber<i32> for ToStartOfMonth {
fn to_number(dt: &DateTime<Tz>) -> i32 {
datetime_to_date_inner_number(&dt.with_day(1).unwrap())
}
}

impl ToNumber<i32> for ToStartOfQuarter {
fn to_number(dt: &DateTime<Tz>) -> i32 {
let new_month = dt.month0() / 3 * 3 + 1;
datetime_to_date_inner_number(&dt.with_month(new_month).unwrap().with_day(1).unwrap())
}
}

impl ToNumber<i32> for ToStartOfYear {
fn to_number(dt: &DateTime<Tz>) -> i32 {
datetime_to_date_inner_number(&dt.with_month(1).unwrap().with_day(1).unwrap())
}
}

impl ToNumber<i32> for ToStartOfISOYear {
fn to_number(dt: &DateTime<Tz>) -> i32 {
let iso_year = dt.iso_week().year();
let iso_dt = dt
.timezone()
.isoywd(iso_year, 1, chrono::Weekday::Mon)
.and_hms(0, 0, 0);
datetime_to_date_inner_number(&iso_dt)
}
}
200 changes: 200 additions & 0 deletions src/query/functions-v2/src/scalars/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ use common_expression::types::NullableType;
use common_expression::types::StringType;
use common_expression::types::TimestampType;
use common_expression::vectorize_1_arg;
use common_expression::vectorize_2_arg;
use common_expression::vectorize_with_builder_1_arg;
use common_expression::vectorize_with_builder_2_arg;
use common_expression::FunctionProperty;
Expand All @@ -55,10 +56,14 @@ pub fn register(registry: &mut FunctionRegistry) {
register_add_functions(registry);
register_sub_functions(registry);

// now, today, yesterday, tomorrow
register_real_time_functions(registry);

// to_*([date | timestamp]) -> number
register_to_number_functions(registry);

// to_*([date | timestamp]) -> [date | timestamp]
register_rounder_functions(registry);
}

fn number_domain_to_timestamp_domain<T: AsPrimitive<i64>>(
Expand Down Expand Up @@ -599,3 +604,198 @@ fn register_to_number_functions(registry: &mut FunctionRegistry) {
}),
);
}

fn register_rounder_functions(registry: &mut FunctionRegistry) {
// timestamp -> timestamp
registry.register_passthrough_nullable_1_arg::<TimestampType, TimestampType, _, _>(
"to_start_of_second",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<TimestampType, TimestampType>(|val, ctx| {
round_timestamp(val, &ctx.tz, Round::Second)
}),
);
registry.register_passthrough_nullable_1_arg::<TimestampType, TimestampType, _, _>(
"to_start_of_minute",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<TimestampType, TimestampType>(|val, ctx| {
round_timestamp(val, &ctx.tz, Round::Minute)
}),
);
registry.register_passthrough_nullable_1_arg::<TimestampType, TimestampType, _, _>(
"to_start_of_five_minutes",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<TimestampType, TimestampType>(|val, ctx| {
round_timestamp(val, &ctx.tz, Round::FiveMinutes)
}),
);
registry.register_passthrough_nullable_1_arg::<TimestampType, TimestampType, _, _>(
"to_start_of_ten_minutes",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<TimestampType, TimestampType>(|val, ctx| {
round_timestamp(val, &ctx.tz, Round::TenMinutes)
}),
);
registry.register_passthrough_nullable_1_arg::<TimestampType, TimestampType, _, _>(
"to_start_of_fifteen_minutes",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<TimestampType, TimestampType>(|val, ctx| {
round_timestamp(val, &ctx.tz, Round::FifteenMinutes)
}),
);
registry.register_passthrough_nullable_1_arg::<TimestampType, TimestampType, _, _>(
"to_start_of_hour",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<TimestampType, TimestampType>(|val, ctx| {
round_timestamp(val, &ctx.tz, Round::Hour)
}),
);
registry.register_passthrough_nullable_1_arg::<TimestampType, TimestampType, _, _>(
"to_start_of_day",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<TimestampType, TimestampType>(|val, ctx| {
round_timestamp(val, &ctx.tz, Round::Day)
}),
);
registry.register_passthrough_nullable_1_arg::<TimestampType, TimestampType, _, _>(
"time_slot",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<TimestampType, TimestampType>(|val, ctx| {
round_timestamp(val, &ctx.tz, Round::TimeSlot)
}),
);

// date | timestamp -> date
registry.register_passthrough_nullable_1_arg::<TimestampType, DateType, _, _>(
"to_monday",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<TimestampType, DateType>(|val, ctx| {
DateRounder::eval_timestamp::<ToLastMonday>(val, &ctx.tz)
}),
);
registry.register_passthrough_nullable_1_arg::<DateType, DateType, _, _>(
"to_monday",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<DateType, DateType>(|val, ctx| {
DateRounder::eval_date::<ToLastMonday>(val, &ctx.tz)
}),
);

registry.register_passthrough_nullable_1_arg::<TimestampType, DateType, _, _>(
"to_start_of_week",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<TimestampType, DateType>(|val, ctx| {
DateRounder::eval_timestamp::<ToLastSunday>(val, &ctx.tz)
}),
);
registry.register_passthrough_nullable_1_arg::<DateType, DateType, _, _>(
"to_start_of_week",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<DateType, DateType>(|val, ctx| {
DateRounder::eval_date::<ToLastSunday>(val, &ctx.tz)
}),
);
registry.register_passthrough_nullable_2_arg::<TimestampType, Int64Type, DateType, _, _>(
"to_start_of_week",
FunctionProperty::default(),
|_, _| None,
vectorize_2_arg::<TimestampType, Int64Type, DateType>(|val, mode, ctx| {
if mode == 0 {
DateRounder::eval_timestamp::<ToLastSunday>(val, &ctx.tz)
} else {
DateRounder::eval_timestamp::<ToLastMonday>(val, &ctx.tz)
}
}),
);
registry.register_passthrough_nullable_2_arg::<DateType, Int64Type, DateType, _, _>(
"to_start_of_week",
FunctionProperty::default(),
|_, _| None,
vectorize_2_arg::<DateType, Int64Type, DateType>(|val, mode, ctx| {
if mode == 0 {
DateRounder::eval_date::<ToLastSunday>(val, &ctx.tz)
} else {
DateRounder::eval_date::<ToLastMonday>(val, &ctx.tz)
}
}),
);

registry.register_passthrough_nullable_1_arg::<TimestampType, DateType, _, _>(
"to_start_of_month",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<TimestampType, DateType>(|val, ctx| {
DateRounder::eval_timestamp::<ToStartOfMonth>(val, &ctx.tz)
}),
);
registry.register_passthrough_nullable_1_arg::<DateType, DateType, _, _>(
"to_start_of_month",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<DateType, DateType>(|val, ctx| {
DateRounder::eval_date::<ToStartOfMonth>(val, &ctx.tz)
}),
);

registry.register_passthrough_nullable_1_arg::<TimestampType, DateType, _, _>(
"to_start_of_quarter",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<TimestampType, DateType>(|val, ctx| {
DateRounder::eval_timestamp::<ToStartOfQuarter>(val, &ctx.tz)
}),
);
registry.register_passthrough_nullable_1_arg::<DateType, DateType, _, _>(
"to_start_of_quarter",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<DateType, DateType>(|val, ctx| {
DateRounder::eval_date::<ToStartOfQuarter>(val, &ctx.tz)
}),
);

registry.register_passthrough_nullable_1_arg::<TimestampType, DateType, _, _>(
"to_start_of_year",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<TimestampType, DateType>(|val, ctx| {
DateRounder::eval_timestamp::<ToStartOfYear>(val, &ctx.tz)
}),
);
registry.register_passthrough_nullable_1_arg::<DateType, DateType, _, _>(
"to_start_of_year",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<DateType, DateType>(|val, ctx| {
DateRounder::eval_date::<ToStartOfYear>(val, &ctx.tz)
}),
);

registry.register_passthrough_nullable_1_arg::<TimestampType, DateType, _, _>(
"to_start_of_iso_year",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<TimestampType, DateType>(|val, ctx| {
DateRounder::eval_timestamp::<ToStartOfISOYear>(val, &ctx.tz)
}),
);
registry.register_passthrough_nullable_1_arg::<DateType, DateType, _, _>(
"to_start_of_iso_year",
FunctionProperty::default(),
|_| None,
vectorize_1_arg::<DateType, DateType>(|val, ctx| {
DateRounder::eval_date::<ToStartOfISOYear>(val, &ctx.tz)
}),
);
}
Loading