mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-17 08:34:19 +01:00
core(datetime): added implementation of ceiling modifier to datetime
This commit is contained in:
@@ -62,24 +62,35 @@ fn exec_datetime(values: &[Register], output_type: DateTimeOutput) -> Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn modify_dt(dt: &mut NaiveDateTime, mods: &[Register], output_type: DateTimeOutput) -> Value {
|
fn modify_dt(dt: &mut NaiveDateTime, mods: &[Register], output_type: DateTimeOutput) -> Value {
|
||||||
|
let mut n_floor: i64 = 0;
|
||||||
let mut subsec_requested = false;
|
let mut subsec_requested = false;
|
||||||
|
|
||||||
for modifier in mods {
|
for modifier in mods {
|
||||||
if let Value::Text(ref text_rc) = modifier.get_value() {
|
if let Value::Text(ref text_rc) = modifier.get_value() {
|
||||||
// TODO: to prevent double conversion and properly support 'utc'/'localtime', we also
|
// TODO: to prevent double conversion and properly support 'utc'/'localtime', we also
|
||||||
// need to keep track of the current timezone and apply it to the modifier.
|
// need to keep track of the current timezone and apply it to the modifier.
|
||||||
match apply_modifier(dt, text_rc.as_str()) {
|
let parsed = parse_modifier(text_rc.as_str());
|
||||||
|
if !matches!(parsed, Ok(Modifier::Floor) | Ok(Modifier::Ceiling)) {
|
||||||
|
n_floor = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
match apply_modifier(dt, text_rc.as_str(), &mut n_floor) {
|
||||||
Ok(true) => subsec_requested = true,
|
Ok(true) => subsec_requested = true,
|
||||||
Ok(false) => {}
|
Ok(false) => {}
|
||||||
Err(_) => return Value::build_text(""),
|
Err(_) => return Value::build_text(""),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if matches!(parsed, Ok(Modifier::Floor) | Ok(Modifier::Ceiling)) {
|
||||||
|
n_floor = 0;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return Value::build_text("");
|
return Value::build_text("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_leap_second(dt) || *dt > get_max_datetime_exclusive() {
|
if is_leap_second(dt) || *dt > get_max_datetime_exclusive() {
|
||||||
return Value::build_text("");
|
return Value::build_text("");
|
||||||
}
|
}
|
||||||
|
|
||||||
format_dt(*dt, output_type, subsec_requested)
|
format_dt(*dt, output_type, subsec_requested)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +106,7 @@ fn format_dt(dt: NaiveDateTime, output_type: DateTimeOutput, subsec: bool) -> Va
|
|||||||
Value::from_text(t.as_str())
|
Value::from_text(t.as_str())
|
||||||
}
|
}
|
||||||
DateTimeOutput::DateTime => {
|
DateTimeOutput::DateTime => {
|
||||||
let t = if subsec {
|
let t = if subsec && dt.nanosecond() != 0 {
|
||||||
dt.format("%Y-%m-%d %H:%M:%S%.3f").to_string()
|
dt.format("%Y-%m-%d %H:%M:%S%.3f").to_string()
|
||||||
} else {
|
} else {
|
||||||
dt.format("%Y-%m-%d %H:%M:%S").to_string()
|
dt.format("%Y-%m-%d %H:%M:%S").to_string()
|
||||||
@@ -134,9 +145,7 @@ fn strftime_format(dt: &NaiveDateTime, format_str: &str) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// to prevent stripping the modifier string and comparing multiple times, this returns
|
fn apply_modifier(dt: &mut NaiveDateTime, modifier: &str, n_floor: &mut i64) -> Result<bool> {
|
||||||
// whether the modifier was a subsec modifier because it impacts the format string
|
|
||||||
fn apply_modifier(dt: &mut NaiveDateTime, modifier: &str) -> Result<bool> {
|
|
||||||
let parsed_modifier = parse_modifier(modifier)?;
|
let parsed_modifier = parse_modifier(modifier)?;
|
||||||
|
|
||||||
match parsed_modifier {
|
match parsed_modifier {
|
||||||
@@ -148,10 +157,10 @@ fn apply_modifier(dt: &mut NaiveDateTime, modifier: &str) -> Result<bool> {
|
|||||||
// Convert months to years + leftover months
|
// Convert months to years + leftover months
|
||||||
let years = m / 12;
|
let years = m / 12;
|
||||||
let leftover = m % 12;
|
let leftover = m % 12;
|
||||||
add_years_and_months(dt, years, leftover)?;
|
add_years_and_months(dt, years, leftover, n_floor)?;
|
||||||
}
|
}
|
||||||
Modifier::Years(y) => {
|
Modifier::Years(y) => {
|
||||||
add_years_and_months(dt, y, 0)?;
|
add_years_and_months(dt, y, 0, n_floor)?;
|
||||||
}
|
}
|
||||||
Modifier::TimeOffset(offset) => *dt += offset,
|
Modifier::TimeOffset(offset) => *dt += offset,
|
||||||
Modifier::DateOffset {
|
Modifier::DateOffset {
|
||||||
@@ -159,9 +168,7 @@ fn apply_modifier(dt: &mut NaiveDateTime, modifier: &str) -> Result<bool> {
|
|||||||
months,
|
months,
|
||||||
days,
|
days,
|
||||||
} => {
|
} => {
|
||||||
*dt = dt
|
add_years_and_months(dt, years, months, n_floor)?;
|
||||||
.checked_add_months(chrono::Months::new((years * 12 + months) as u32))
|
|
||||||
.ok_or_else(|| InvalidModifier("Invalid date offset".to_string()))?;
|
|
||||||
*dt += TimeDelta::days(days as i64);
|
*dt += TimeDelta::days(days as i64);
|
||||||
}
|
}
|
||||||
Modifier::DateTimeOffset {
|
Modifier::DateTimeOffset {
|
||||||
@@ -170,12 +177,20 @@ fn apply_modifier(dt: &mut NaiveDateTime, modifier: &str) -> Result<bool> {
|
|||||||
days,
|
days,
|
||||||
seconds,
|
seconds,
|
||||||
} => {
|
} => {
|
||||||
add_years_and_months(dt, years, months)?;
|
add_years_and_months(dt, years, months, n_floor)?;
|
||||||
*dt += chrono::Duration::days(days as i64);
|
*dt += chrono::Duration::days(days as i64);
|
||||||
*dt += chrono::Duration::seconds(seconds.into());
|
*dt += chrono::Duration::seconds(seconds.into());
|
||||||
}
|
}
|
||||||
Modifier::Ceiling => todo!(),
|
Modifier::Floor => {
|
||||||
Modifier::Floor => todo!(),
|
if *n_floor <= 0 {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
*dt -= TimeDelta::days(*n_floor);
|
||||||
|
}
|
||||||
|
Modifier::Ceiling => {
|
||||||
|
*n_floor = 0;
|
||||||
|
}
|
||||||
Modifier::StartOfMonth => {
|
Modifier::StartOfMonth => {
|
||||||
*dt = NaiveDate::from_ymd_opt(dt.year(), dt.month(), 1)
|
*dt = NaiveDate::from_ymd_opt(dt.year(), dt.month(), 1)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -222,16 +237,22 @@ fn is_julian_day_value(value: f64) -> bool {
|
|||||||
(0.0..5373484.5).contains(&value)
|
(0.0..5373484.5).contains(&value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_years_and_months(dt: &mut NaiveDateTime, years: i32, months: i32) -> Result<()> {
|
fn add_years_and_months(
|
||||||
add_whole_years(dt, years)?;
|
dt: &mut NaiveDateTime,
|
||||||
add_months_in_increments(dt, months)?;
|
years: i32,
|
||||||
|
months: i32,
|
||||||
|
n_floor: &mut i64,
|
||||||
|
) -> Result<()> {
|
||||||
|
add_whole_years(dt, years, n_floor)?;
|
||||||
|
add_months_in_increments(dt, months, n_floor)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_whole_years(dt: &mut NaiveDateTime, years: i32) -> Result<()> {
|
fn add_whole_years(dt: &mut NaiveDateTime, years: i32, n_floor: &mut i64) -> Result<()> {
|
||||||
if years == 0 {
|
if years == 0 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let target_year = dt.year() + years;
|
let target_year = dt.year() + years;
|
||||||
let (m, d, hh, mm, ss) = (dt.month(), dt.day(), dt.hour(), dt.minute(), dt.second());
|
let (m, d, hh, mm, ss) = (dt.month(), dt.day(), dt.hour(), dt.minute(), dt.second());
|
||||||
|
|
||||||
@@ -255,16 +276,17 @@ fn add_whole_years(dt: &mut NaiveDateTime, years: i32) -> Result<()> {
|
|||||||
.ok_or_else(|| InvalidModifier("Invalid time format".to_string()))?;
|
.ok_or_else(|| InvalidModifier("Invalid time format".to_string()))?;
|
||||||
|
|
||||||
*dt = base_date + chrono::Duration::days(leftover as i64);
|
*dt = base_date + chrono::Duration::days(leftover as i64);
|
||||||
|
*n_floor += leftover as i64;
|
||||||
} else {
|
} else {
|
||||||
// do we fall back here?
|
// do we fall back here?
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_months_in_increments(dt: &mut NaiveDateTime, months: i32) -> Result<()> {
|
fn add_months_in_increments(dt: &mut NaiveDateTime, months: i32, n_floor: &mut i64) -> Result<()> {
|
||||||
let step = if months >= 0 { 1 } else { -1 };
|
let step = if months >= 0 { 1 } else { -1 };
|
||||||
for _ in 0..months.abs() {
|
for _ in 0..months.abs() {
|
||||||
add_one_month(dt, step)?;
|
add_one_month(dt, step, n_floor)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -275,7 +297,7 @@ fn add_months_in_increments(dt: &mut NaiveDateTime, months: i32) -> Result<()> {
|
|||||||
//
|
//
|
||||||
// the modifiers 'ceiling' and 'floor' will determine behavior, so we'll need to eagerly
|
// the modifiers 'ceiling' and 'floor' will determine behavior, so we'll need to eagerly
|
||||||
// evaluate modifiers in the future to support those, and 'julianday'/'unixepoch'
|
// evaluate modifiers in the future to support those, and 'julianday'/'unixepoch'
|
||||||
fn add_one_month(dt: &mut NaiveDateTime, step: i32) -> Result<()> {
|
fn add_one_month(dt: &mut NaiveDateTime, step: i32, n_floor: &mut i64) -> Result<()> {
|
||||||
let (y0, m0, d0) = (dt.year(), dt.month(), dt.day());
|
let (y0, m0, d0) = (dt.year(), dt.month(), dt.day());
|
||||||
let (hh, mm, ss) = (dt.hour(), dt.minute(), dt.second());
|
let (hh, mm, ss) = (dt.hour(), dt.minute(), dt.second());
|
||||||
|
|
||||||
@@ -304,6 +326,7 @@ fn add_one_month(dt: &mut NaiveDateTime, step: i32) -> Result<()> {
|
|||||||
.ok_or_else(|| InvalidModifier("Invalid Auto format".to_string()))?;
|
.ok_or_else(|| InvalidModifier("Invalid Auto format".to_string()))?;
|
||||||
|
|
||||||
*dt = base_date + chrono::Duration::days(leftover as i64);
|
*dt = base_date + chrono::Duration::days(leftover as i64);
|
||||||
|
*n_floor += leftover as i64;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1143,6 +1166,12 @@ mod tests {
|
|||||||
assert_eq!(parse_modifier("WEEKDAY 6").unwrap(), Modifier::Weekday(6));
|
assert_eq!(parse_modifier("WEEKDAY 6").unwrap(), Modifier::Weekday(6));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_ceiling_modifier() {
|
||||||
|
assert_eq!(parse_modifier("ceiling").unwrap(), Modifier::Ceiling);
|
||||||
|
assert_eq!(parse_modifier("CEILING").unwrap(), Modifier::Ceiling);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_other_modifiers() {
|
fn test_parse_other_modifiers() {
|
||||||
assert_eq!(parse_modifier("unixepoch").unwrap(), Modifier::UnixEpoch);
|
assert_eq!(parse_modifier("unixepoch").unwrap(), Modifier::UnixEpoch);
|
||||||
@@ -1191,89 +1220,106 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_apply_modifier_days() {
|
fn test_apply_modifier_days() {
|
||||||
let mut dt = setup_datetime();
|
let mut dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "5 days").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "5 days", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 6, 20, 12, 30, 45));
|
assert_eq!(dt, create_datetime(2023, 6, 20, 12, 30, 45));
|
||||||
|
|
||||||
dt = setup_datetime();
|
dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "-3 days").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "-3 days", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 6, 12, 12, 30, 45));
|
assert_eq!(dt, create_datetime(2023, 6, 12, 12, 30, 45));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_apply_modifier_hours() {
|
fn test_apply_modifier_hours() {
|
||||||
let mut dt = setup_datetime();
|
let mut dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "6 hours").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "6 hours", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 6, 15, 18, 30, 45));
|
assert_eq!(dt, create_datetime(2023, 6, 15, 18, 30, 45));
|
||||||
|
|
||||||
dt = setup_datetime();
|
dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "-2 hours").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "-2 hours", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 6, 15, 10, 30, 45));
|
assert_eq!(dt, create_datetime(2023, 6, 15, 10, 30, 45));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_apply_modifier_minutes() {
|
fn test_apply_modifier_minutes() {
|
||||||
let mut dt = setup_datetime();
|
let mut dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "45 minutes").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "45 minutes", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 6, 15, 13, 15, 45));
|
assert_eq!(dt, create_datetime(2023, 6, 15, 13, 15, 45));
|
||||||
|
|
||||||
dt = setup_datetime();
|
dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "-15 minutes").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "-15 minutes", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 6, 15, 12, 15, 45));
|
assert_eq!(dt, create_datetime(2023, 6, 15, 12, 15, 45));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_apply_modifier_seconds() {
|
fn test_apply_modifier_seconds() {
|
||||||
let mut dt = setup_datetime();
|
let mut dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "30 seconds").unwrap();
|
|
||||||
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "30 seconds", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 6, 15, 12, 31, 15));
|
assert_eq!(dt, create_datetime(2023, 6, 15, 12, 31, 15));
|
||||||
|
|
||||||
dt = setup_datetime();
|
dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "-20 seconds").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "-20 seconds", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 6, 15, 12, 30, 25));
|
assert_eq!(dt, create_datetime(2023, 6, 15, 12, 30, 25));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_apply_modifier_time_offset() {
|
fn test_apply_modifier_time_offset() {
|
||||||
let mut dt = setup_datetime();
|
let mut dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "+01:30").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "+01:30", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 6, 15, 14, 0, 45));
|
assert_eq!(dt, create_datetime(2023, 6, 15, 14, 0, 45));
|
||||||
|
|
||||||
dt = setup_datetime();
|
dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "-00:45").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "-00:45", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 6, 15, 11, 45, 45));
|
assert_eq!(dt, create_datetime(2023, 6, 15, 11, 45, 45));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_apply_modifier_date_time_offset() {
|
fn test_apply_modifier_date_time_offset() {
|
||||||
let mut dt = setup_datetime();
|
let mut dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "+0001-01-01 01:01").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "+0001-01-01 01:01", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2024, 7, 16, 13, 31, 45));
|
assert_eq!(dt, create_datetime(2024, 7, 16, 13, 31, 45));
|
||||||
|
|
||||||
dt = setup_datetime();
|
dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "-0001-01-01 01:01").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "-0001-01-01 01:01", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2022, 5, 14, 11, 29, 45));
|
assert_eq!(dt, create_datetime(2022, 5, 14, 11, 29, 45));
|
||||||
|
|
||||||
// Test with larger offsets
|
// Test with larger offsets
|
||||||
dt = setup_datetime();
|
dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "+0002-03-04 05:06").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "+0002-03-04 05:06", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2025, 9, 19, 17, 36, 45));
|
assert_eq!(dt, create_datetime(2025, 9, 19, 17, 36, 45));
|
||||||
|
|
||||||
dt = setup_datetime();
|
dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "-0002-03-04 05:06").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "-0002-03-04 05:06", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2021, 3, 11, 7, 24, 45));
|
assert_eq!(dt, create_datetime(2021, 3, 11, 7, 24, 45));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_apply_modifier_start_of_year() {
|
fn test_apply_modifier_start_of_year() {
|
||||||
let mut dt = setup_datetime();
|
let mut dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "start of year").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "start of year", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 1, 1, 0, 0, 0));
|
assert_eq!(dt, create_datetime(2023, 1, 1, 0, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_apply_modifier_start_of_day() {
|
fn test_apply_modifier_start_of_day() {
|
||||||
let mut dt = setup_datetime();
|
let mut dt = setup_datetime();
|
||||||
apply_modifier(&mut dt, "start of day").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "start of day", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 6, 15, 0, 0, 0));
|
assert_eq!(dt, create_datetime(2023, 6, 15, 0, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1452,7 +1498,8 @@ mod tests {
|
|||||||
fn test_already_on_weekday_no_change() {
|
fn test_already_on_weekday_no_change() {
|
||||||
// 2023-01-01 is a Sunday => weekday 0
|
// 2023-01-01 is a Sunday => weekday 0
|
||||||
let mut dt = create_datetime(2023, 1, 1, 12, 0, 0);
|
let mut dt = create_datetime(2023, 1, 1, 12, 0, 0);
|
||||||
apply_modifier(&mut dt, "weekday 0").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "weekday 0", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 1, 1, 12, 0, 0));
|
assert_eq!(dt, create_datetime(2023, 1, 1, 12, 0, 0));
|
||||||
assert_eq!(weekday_sunday_based(&dt), 0);
|
assert_eq!(weekday_sunday_based(&dt), 0);
|
||||||
}
|
}
|
||||||
@@ -1462,14 +1509,16 @@ mod tests {
|
|||||||
// 2023-01-01 is a Sunday => weekday 0
|
// 2023-01-01 is a Sunday => weekday 0
|
||||||
// "weekday 1" => next Monday => 2023-01-02
|
// "weekday 1" => next Monday => 2023-01-02
|
||||||
let mut dt = create_datetime(2023, 1, 1, 12, 0, 0);
|
let mut dt = create_datetime(2023, 1, 1, 12, 0, 0);
|
||||||
apply_modifier(&mut dt, "weekday 1").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "weekday 1", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 1, 2, 12, 0, 0));
|
assert_eq!(dt, create_datetime(2023, 1, 2, 12, 0, 0));
|
||||||
assert_eq!(weekday_sunday_based(&dt), 1);
|
assert_eq!(weekday_sunday_based(&dt), 1);
|
||||||
|
|
||||||
// 2023-01-03 is a Tuesday => weekday 2
|
// 2023-01-03 is a Tuesday => weekday 2
|
||||||
// "weekday 5" => next Friday => 2023-01-06
|
// "weekday 5" => next Friday => 2023-01-06
|
||||||
let mut dt = create_datetime(2023, 1, 3, 12, 0, 0);
|
let mut dt = create_datetime(2023, 1, 3, 12, 0, 0);
|
||||||
apply_modifier(&mut dt, "weekday 5").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "weekday 5", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 1, 6, 12, 0, 0));
|
assert_eq!(dt, create_datetime(2023, 1, 6, 12, 0, 0));
|
||||||
assert_eq!(weekday_sunday_based(&dt), 5);
|
assert_eq!(weekday_sunday_based(&dt), 5);
|
||||||
}
|
}
|
||||||
@@ -1479,12 +1528,13 @@ mod tests {
|
|||||||
// 2023-01-06 is a Friday => weekday 5
|
// 2023-01-06 is a Friday => weekday 5
|
||||||
// "weekday 0" => next Sunday => 2023-01-08
|
// "weekday 0" => next Sunday => 2023-01-08
|
||||||
let mut dt = create_datetime(2023, 1, 6, 12, 0, 0);
|
let mut dt = create_datetime(2023, 1, 6, 12, 0, 0);
|
||||||
apply_modifier(&mut dt, "weekday 0").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "weekday 0", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 1, 8, 12, 0, 0));
|
assert_eq!(dt, create_datetime(2023, 1, 8, 12, 0, 0));
|
||||||
assert_eq!(weekday_sunday_based(&dt), 0);
|
assert_eq!(weekday_sunday_based(&dt), 0);
|
||||||
|
|
||||||
// Now confirm that being on Sunday (weekday 0) and asking for "weekday 0" stays put
|
// Now confirm that being on Sunday (weekday 0) and asking for "weekday 0" stays put
|
||||||
apply_modifier(&mut dt, "weekday 0").unwrap();
|
apply_modifier(&mut dt, "weekday 0", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 1, 8, 12, 0, 0));
|
assert_eq!(dt, create_datetime(2023, 1, 8, 12, 0, 0));
|
||||||
assert_eq!(weekday_sunday_based(&dt), 0);
|
assert_eq!(weekday_sunday_based(&dt), 0);
|
||||||
}
|
}
|
||||||
@@ -1494,7 +1544,8 @@ mod tests {
|
|||||||
// 2023-01-05 is a Thursday => weekday 4
|
// 2023-01-05 is a Thursday => weekday 4
|
||||||
// Asking for weekday 4 => no change
|
// Asking for weekday 4 => no change
|
||||||
let mut dt = create_datetime(2023, 1, 5, 12, 0, 0);
|
let mut dt = create_datetime(2023, 1, 5, 12, 0, 0);
|
||||||
apply_modifier(&mut dt, "weekday 4").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "weekday 4", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 1, 5, 12, 0, 0));
|
assert_eq!(dt, create_datetime(2023, 1, 5, 12, 0, 0));
|
||||||
assert_eq!(weekday_sunday_based(&dt), 4);
|
assert_eq!(weekday_sunday_based(&dt), 4);
|
||||||
}
|
}
|
||||||
@@ -1504,7 +1555,8 @@ mod tests {
|
|||||||
// 2023-01-06 is a Friday => weekday 5
|
// 2023-01-06 is a Friday => weekday 5
|
||||||
// Asking for weekday 5 => no change if already on Friday
|
// Asking for weekday 5 => no change if already on Friday
|
||||||
let mut dt = create_datetime(2023, 1, 6, 12, 0, 0);
|
let mut dt = create_datetime(2023, 1, 6, 12, 0, 0);
|
||||||
apply_modifier(&mut dt, "weekday 5").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "weekday 5", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 1, 6, 12, 0, 0));
|
assert_eq!(dt, create_datetime(2023, 1, 6, 12, 0, 0));
|
||||||
assert_eq!(weekday_sunday_based(&dt), 5);
|
assert_eq!(weekday_sunday_based(&dt), 5);
|
||||||
}
|
}
|
||||||
@@ -1526,7 +1578,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_apply_modifier_start_of_month() {
|
fn test_apply_modifier_start_of_month() {
|
||||||
let mut dt = create_datetime(2023, 6, 15, 12, 30, 45);
|
let mut dt = create_datetime(2023, 6, 15, 12, 30, 45);
|
||||||
apply_modifier(&mut dt, "start of month").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "start of month", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 6, 1, 0, 0, 0));
|
assert_eq!(dt, create_datetime(2023, 6, 1, 0, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1535,15 +1588,48 @@ mod tests {
|
|||||||
let mut dt = create_datetime(2023, 6, 15, 12, 30, 45);
|
let mut dt = create_datetime(2023, 6, 15, 12, 30, 45);
|
||||||
let dt_with_nanos = dt.with_nanosecond(123_456_789).unwrap();
|
let dt_with_nanos = dt.with_nanosecond(123_456_789).unwrap();
|
||||||
dt = dt_with_nanos;
|
dt = dt_with_nanos;
|
||||||
apply_modifier(&mut dt, "subsec").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "subsec", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, dt_with_nanos);
|
assert_eq!(dt, dt_with_nanos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_apply_modifier_floor_modifier_n_floor_gt_0() {
|
||||||
|
let mut dt = create_datetime(2023, 6, 15, 12, 30, 45);
|
||||||
|
let mut n_floor = 3;
|
||||||
|
|
||||||
|
apply_modifier(&mut dt, "floor", &mut n_floor).unwrap();
|
||||||
|
assert_eq!(dt, create_datetime(2023, 6, 12, 12, 30, 45));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_apply_modifier_floor_modifier_n_floor_le_0() {
|
||||||
|
let mut dt = create_datetime(2023, 6, 15, 12, 30, 45);
|
||||||
|
let mut n_floor = 0;
|
||||||
|
|
||||||
|
apply_modifier(&mut dt, "floor", &mut n_floor).unwrap();
|
||||||
|
assert_eq!(dt, create_datetime(2023, 6, 15, 12, 30, 45));
|
||||||
|
|
||||||
|
n_floor = 2;
|
||||||
|
apply_modifier(&mut dt, "floor", &mut n_floor).unwrap();
|
||||||
|
assert_eq!(dt, create_datetime(2023, 6, 13, 12, 30, 45));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_apply_modifier_ceiling_modifier_sets_n_floor_to_zero() {
|
||||||
|
let mut dt = create_datetime(2023, 6, 15, 12, 30, 45);
|
||||||
|
let mut n_floor = 5;
|
||||||
|
|
||||||
|
apply_modifier(&mut dt, "ceiling", &mut n_floor).unwrap();
|
||||||
|
assert_eq!(n_floor, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_apply_modifier_start_of_month_basic() {
|
fn test_apply_modifier_start_of_month_basic() {
|
||||||
// Basic check: from mid-month to the 1st at 00:00:00.
|
// Basic check: from mid-month to the 1st at 00:00:00.
|
||||||
let mut dt = create_datetime(2023, 6, 15, 12, 30, 45);
|
let mut dt = create_datetime(2023, 6, 15, 12, 30, 45);
|
||||||
apply_modifier(&mut dt, "start of month").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "start of month", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 6, 1, 0, 0, 0));
|
assert_eq!(dt, create_datetime(2023, 6, 1, 0, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1551,7 +1637,8 @@ mod tests {
|
|||||||
fn test_apply_modifier_start_of_month_already_at_first() {
|
fn test_apply_modifier_start_of_month_already_at_first() {
|
||||||
// If we're already at the start of the month, no change.
|
// If we're already at the start of the month, no change.
|
||||||
let mut dt = create_datetime(2023, 6, 1, 0, 0, 0);
|
let mut dt = create_datetime(2023, 6, 1, 0, 0, 0);
|
||||||
apply_modifier(&mut dt, "start of month").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "start of month", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 6, 1, 0, 0, 0));
|
assert_eq!(dt, create_datetime(2023, 6, 1, 0, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1559,7 +1646,8 @@ mod tests {
|
|||||||
fn test_apply_modifier_start_of_month_edge_case() {
|
fn test_apply_modifier_start_of_month_edge_case() {
|
||||||
// edge case: month boundary. 2023-07-31 -> start of July.
|
// edge case: month boundary. 2023-07-31 -> start of July.
|
||||||
let mut dt = create_datetime(2023, 7, 31, 23, 59, 59);
|
let mut dt = create_datetime(2023, 7, 31, 23, 59, 59);
|
||||||
apply_modifier(&mut dt, "start of month").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "start of month", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, create_datetime(2023, 7, 1, 0, 0, 0));
|
assert_eq!(dt, create_datetime(2023, 7, 1, 0, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1568,7 +1656,8 @@ mod tests {
|
|||||||
let mut dt = create_datetime(2023, 6, 15, 12, 30, 45);
|
let mut dt = create_datetime(2023, 6, 15, 12, 30, 45);
|
||||||
let dt_with_nanos = dt.with_nanosecond(123_456_789).unwrap();
|
let dt_with_nanos = dt.with_nanosecond(123_456_789).unwrap();
|
||||||
dt = dt_with_nanos;
|
dt = dt_with_nanos;
|
||||||
apply_modifier(&mut dt, "subsec").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "subsec", &mut n_floor).unwrap();
|
||||||
assert_eq!(dt, dt_with_nanos);
|
assert_eq!(dt, dt_with_nanos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1577,7 +1666,8 @@ mod tests {
|
|||||||
let mut dt = create_datetime(2025, 1, 2, 4, 12, 21)
|
let mut dt = create_datetime(2025, 1, 2, 4, 12, 21)
|
||||||
.with_nanosecond(891_000_000) // 891 milliseconds
|
.with_nanosecond(891_000_000) // 891 milliseconds
|
||||||
.unwrap();
|
.unwrap();
|
||||||
apply_modifier(&mut dt, "subsec").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "subsec", &mut n_floor).unwrap();
|
||||||
|
|
||||||
let formatted = dt.format("%Y-%m-%d %H:%M:%S%.3f").to_string();
|
let formatted = dt.format("%Y-%m-%d %H:%M:%S%.3f").to_string();
|
||||||
assert_eq!(formatted, "2025-01-02 04:12:21.891");
|
assert_eq!(formatted, "2025-01-02 04:12:21.891");
|
||||||
@@ -1586,7 +1676,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_apply_modifier_subsec_no_fractional_seconds() {
|
fn test_apply_modifier_subsec_no_fractional_seconds() {
|
||||||
let mut dt = create_datetime(2025, 1, 2, 4, 12, 21);
|
let mut dt = create_datetime(2025, 1, 2, 4, 12, 21);
|
||||||
apply_modifier(&mut dt, "subsec").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "subsec", &mut n_floor).unwrap();
|
||||||
|
|
||||||
let formatted = dt.format("%Y-%m-%d %H:%M:%S%.3f").to_string();
|
let formatted = dt.format("%Y-%m-%d %H:%M:%S%.3f").to_string();
|
||||||
assert_eq!(formatted, "2025-01-02 04:12:21.000");
|
assert_eq!(formatted, "2025-01-02 04:12:21.000");
|
||||||
@@ -1597,7 +1688,8 @@ mod tests {
|
|||||||
let mut dt = create_datetime(2025, 1, 2, 4, 12, 21)
|
let mut dt = create_datetime(2025, 1, 2, 4, 12, 21)
|
||||||
.with_nanosecond(891_123_456)
|
.with_nanosecond(891_123_456)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
apply_modifier(&mut dt, "subsec").unwrap();
|
let mut n_floor = 0;
|
||||||
|
apply_modifier(&mut dt, "subsec", &mut n_floor).unwrap();
|
||||||
|
|
||||||
let formatted = dt.format("%Y-%m-%d %H:%M:%S%.3f").to_string();
|
let formatted = dt.format("%Y-%m-%d %H:%M:%S%.3f").to_string();
|
||||||
assert_eq!(formatted, "2025-01-02 04:12:21.891");
|
assert_eq!(formatted, "2025-01-02 04:12:21.891");
|
||||||
|
|||||||
@@ -251,6 +251,46 @@ do_execsql_test date-with-modifier-add-months {
|
|||||||
SELECT date('2023-05-18', '+2 months');
|
SELECT date('2023-05-18', '+2 months');
|
||||||
} {2023-07-18}
|
} {2023-07-18}
|
||||||
|
|
||||||
|
do_execsql_test datetime-default-ceiling {
|
||||||
|
SELECT date('2024-01-31', '+1 month'); -- default ceiling
|
||||||
|
} {2024-03-02}
|
||||||
|
|
||||||
|
do_execsql_test datetime-floor-keeps-time {
|
||||||
|
SELECT datetime('2024-01-31 10:20:30', '+1 month', 'floor');
|
||||||
|
} {{2024-02-29 10:20:30}}
|
||||||
|
|
||||||
|
do_execsql_test datetime-ceiling-keeps-time {
|
||||||
|
SELECT datetime('2024-01-31 10:20:30', '+1 month', 'ceiling');
|
||||||
|
} {{2024-03-02 10:20:30}}
|
||||||
|
|
||||||
|
do_execsql_test date-ceiling-floor-2 {
|
||||||
|
SELECT date('2024-01-31', '+1 month', 'floor');
|
||||||
|
} {2024-02-29}
|
||||||
|
|
||||||
|
do_execsql_test date-ceiling-floor-3 {
|
||||||
|
SELECT date('2023-01-31', '+1 month', 'floor');
|
||||||
|
} {2023-02-28}
|
||||||
|
|
||||||
|
do_execsql_test date-ceiling-floor-4 {
|
||||||
|
SELECT date('2024-03-31', '-1 month', 'floor');
|
||||||
|
} {2024-02-29}
|
||||||
|
|
||||||
|
do_execsql_test date-ceiling-floor-5 {
|
||||||
|
SELECT date('2024-01-31', '+1 month', '+1 month', 'floor');
|
||||||
|
} {2024-04-02}
|
||||||
|
|
||||||
|
do_execsql_test date-ceiling-floor-6 {
|
||||||
|
SELECT date('2024-01-31', '+1 month', 'ceiling');
|
||||||
|
} {2024-03-02}
|
||||||
|
|
||||||
|
do_execsql_test date-ceiling-floor-7 {
|
||||||
|
SELECT date('2024-01-31', '+1 month', 'floor', 'ceiling');
|
||||||
|
} {2024-02-29}
|
||||||
|
|
||||||
|
do_execsql_test date-ceiling-floor-8 {
|
||||||
|
SELECT date('2024-01-31', '+1 month', '+1 day', 'floor');
|
||||||
|
} {2024-03-03}
|
||||||
|
|
||||||
do_execsql_test date-with-modifier-subtract-months {
|
do_execsql_test date-with-modifier-subtract-months {
|
||||||
SELECT date('2023-05-18', '-3 months');
|
SELECT date('2023-05-18', '-3 months');
|
||||||
} {2023-02-18}
|
} {2023-02-18}
|
||||||
@@ -371,6 +411,26 @@ do_execsql_test datetime-with-multiple-modifiers {
|
|||||||
select datetime('2024-01-31', '+1 month', '+13 hours', '+5 minutes', '+62 seconds');
|
select datetime('2024-01-31', '+1 month', '+13 hours', '+5 minutes', '+62 seconds');
|
||||||
} {{2024-03-02 13:06:02}}
|
} {{2024-03-02 13:06:02}}
|
||||||
|
|
||||||
|
do_execsql_test datetime-with-modifier-ceiling {
|
||||||
|
SELECT datetime('2023-05-18 15:30:45', 'ceiling');
|
||||||
|
} {{2023-05-18 15:30:45}}
|
||||||
|
|
||||||
|
do_execsql_test datetime-with-modifier-ceiling-already-ceiled {
|
||||||
|
SELECT datetime('2023-05-18 23:59:59', 'ceiling');
|
||||||
|
} {{2023-05-18 23:59:59}}
|
||||||
|
|
||||||
|
do_execsql_test datetime-with-ceiling-modifier-invalid-input {
|
||||||
|
SELECT datetime('not-a-date', 'ceiling');
|
||||||
|
} {{}}
|
||||||
|
|
||||||
|
do_execsql_test datetime-with-ceiling-modifier-stacked {
|
||||||
|
SELECT datetime('2023-05-18 15:30:45', '+1 day', 'ceiling');
|
||||||
|
} {{2023-05-19 15:30:45}}
|
||||||
|
|
||||||
|
do_execsql_test date-with-ceiling-modifier-basic {
|
||||||
|
SELECT date('2023-05-18 15:30:45', 'ceiling');
|
||||||
|
} {2023-05-18}
|
||||||
|
|
||||||
do_execsql_test datetime-with-weekday {
|
do_execsql_test datetime-with-weekday {
|
||||||
SELECT datetime('2023-05-18', 'weekday 3');
|
SELECT datetime('2023-05-18', 'weekday 3');
|
||||||
} {{2023-05-24 00:00:00}}
|
} {{2023-05-24 00:00:00}}
|
||||||
@@ -666,4 +726,4 @@ do_execsql_test timediff-julian-day {
|
|||||||
|
|
||||||
do_execsql_test timediff-different-time-formats {
|
do_execsql_test timediff-different-time-formats {
|
||||||
SELECT timediff('23:59:59', '00:00:00');
|
SELECT timediff('23:59:59', '00:00:00');
|
||||||
} {"+0000-00-00 23:59:59.000"}
|
} {"+0000-00-00 23:59:59.000"}
|
||||||
|
|||||||
Reference in New Issue
Block a user