mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-28 21:44:21 +01:00
Move the function so that it's callers are above the function definition for smoother flow when reading the code.
648 lines
24 KiB
Rust
648 lines
24 KiB
Rust
use crate::types::OwnedValue;
|
|
use crate::Result;
|
|
use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc};
|
|
|
|
pub fn get_date_from_time_value(time_value: &OwnedValue) -> Result<String> {
|
|
let dt = parse_naive_date_time(time_value);
|
|
match dt {
|
|
Some(dt) => Ok(get_date_from_naive_datetime(dt)),
|
|
None => Ok(String::new()),
|
|
}
|
|
}
|
|
|
|
pub fn get_time_from_datetime_value(time_value: &OwnedValue) -> Result<String> {
|
|
let dt = parse_naive_date_time(time_value);
|
|
match dt {
|
|
Some(dt) => Ok(get_time_from_naive_datetime(dt)),
|
|
None => Ok(String::new()),
|
|
}
|
|
}
|
|
|
|
fn parse_naive_date_time(time_value: &OwnedValue) -> Option<NaiveDateTime> {
|
|
match time_value {
|
|
OwnedValue::Text(s) => get_date_time_from_time_value_string(s),
|
|
OwnedValue::Integer(i) => get_date_time_from_time_value_integer(*i),
|
|
OwnedValue::Float(f) => get_date_time_from_time_value_float(*f),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn get_date_time_from_time_value_string(value: &str) -> Option<NaiveDateTime> {
|
|
// Time-value formats:
|
|
// 1-7. YYYY-MM-DD[THH:MM[:SS[.SSS]]]
|
|
// 8-10. HH:MM[:SS[.SSS]]
|
|
// 11. 'now'
|
|
// 12. DDDDDDDDDD (Julian day number as integer or float)
|
|
//
|
|
// Ref: https://sqlite.org/lang_datefunc.html#tmval
|
|
|
|
// Check for 'now'
|
|
if value.trim().eq_ignore_ascii_case("now") {
|
|
return Some(chrono::Local::now().to_utc().naive_utc());
|
|
}
|
|
|
|
// Check for Julian day number (integer or float)
|
|
if let Ok(julian_day) = value.parse::<f64>() {
|
|
return get_date_time_from_time_value_float(julian_day);
|
|
}
|
|
|
|
// Attempt to parse with various formats
|
|
let date_only_format = "%Y-%m-%d";
|
|
let datetime_formats: [&str; 9] = [
|
|
"%Y-%m-%d %H:%M",
|
|
"%Y-%m-%d %H:%M:%S",
|
|
"%Y-%m-%d %H:%M:%S%.f",
|
|
"%Y-%m-%dT%H:%M",
|
|
"%Y-%m-%dT%H:%M:%S",
|
|
"%Y-%m-%dT%H:%M:%S%.f",
|
|
"%H:%M",
|
|
"%H:%M:%S",
|
|
"%H:%M:%S%.f",
|
|
];
|
|
|
|
// First, try to parse as date-only format
|
|
if let Ok(date) = NaiveDate::parse_from_str(value, date_only_format) {
|
|
return Some(date.and_time(NaiveTime::from_hms_opt(0, 0, 0).unwrap()));
|
|
}
|
|
|
|
for format in &datetime_formats {
|
|
if let Some(dt) = if format.starts_with("%H") {
|
|
// For time-only formats, assume date 2000-01-01
|
|
// Ref: https://sqlite.org/lang_datefunc.html#tmval
|
|
parse_datetime_with_optional_tz(
|
|
&format!("2000-01-01 {}", value),
|
|
&format!("%Y-%m-%d {}", format),
|
|
)
|
|
} else {
|
|
parse_datetime_with_optional_tz(value, format)
|
|
} {
|
|
return Some(dt);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
fn parse_datetime_with_optional_tz(value: &str, format: &str) -> Option<NaiveDateTime> {
|
|
// Try parsing with timezone
|
|
let with_tz_format = format.to_owned() + "%:z";
|
|
if let Ok(dt) = DateTime::parse_from_str(value, &with_tz_format) {
|
|
return Some(dt.with_timezone(&Utc).naive_utc());
|
|
}
|
|
|
|
let mut value_without_tz = value;
|
|
if value.ends_with("Z") {
|
|
value_without_tz = &value[0..value.len() - 1];
|
|
}
|
|
|
|
// Parse without timezone
|
|
if let Ok(dt) = NaiveDateTime::parse_from_str(value_without_tz, format) {
|
|
return Some(dt);
|
|
}
|
|
None
|
|
}
|
|
|
|
fn get_date_time_from_time_value_integer(value: i64) -> Option<NaiveDateTime> {
|
|
i32::try_from(value).map_or_else(
|
|
|_| None,
|
|
|value| get_date_time_from_time_value_float(value as f64),
|
|
)
|
|
}
|
|
|
|
fn get_date_time_from_time_value_float(value: f64) -> Option<NaiveDateTime> {
|
|
if value.is_infinite() || value.is_nan() || value < 0.0 || value >= 5373484.5 {
|
|
return None;
|
|
}
|
|
match julian_day_converter::julian_day_to_datetime(value) {
|
|
Ok(dt) => Some(dt),
|
|
Err(_) => None,
|
|
}
|
|
}
|
|
|
|
fn is_leap_second(dt: &NaiveDateTime) -> bool {
|
|
// The range from 1,000,000,000 to 1,999,999,999 represents the leap second.
|
|
dt.nanosecond() >= 1_000_000_000 && dt.nanosecond() <= 1_999_999_999
|
|
}
|
|
|
|
fn get_date_from_naive_datetime(value: NaiveDateTime) -> String {
|
|
// NaiveDateTime supports leap seconds, but SQLite does not.
|
|
// So we ignore them.
|
|
if is_leap_second(&value) || value > get_max_datetime_exclusive() {
|
|
return String::new();
|
|
}
|
|
value.format("%Y-%m-%d").to_string()
|
|
}
|
|
|
|
fn get_time_from_naive_datetime(value: NaiveDateTime) -> String {
|
|
// NaiveDateTime supports leap seconds, but SQLite does not.
|
|
// So we ignore them.
|
|
if is_leap_second(&value) || value > get_max_datetime_exclusive() {
|
|
return String::new();
|
|
}
|
|
value.format("%H:%M:%S").to_string()
|
|
}
|
|
|
|
fn get_max_datetime_exclusive() -> NaiveDateTime {
|
|
// The maximum date in SQLite is 9999-12-31
|
|
NaiveDateTime::new(
|
|
NaiveDate::from_ymd_opt(10000, 01, 01).unwrap(),
|
|
NaiveTime::from_hms_milli_opt(00, 00, 00, 000).unwrap(),
|
|
)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::rc::Rc;
|
|
|
|
#[test]
|
|
fn test_valid_get_date_from_time_value() {
|
|
let now = chrono::Local::now().to_utc().format("%Y-%m-%d").to_string();
|
|
|
|
let prev_date_str = "2024-07-20";
|
|
let test_date_str = "2024-07-21";
|
|
let next_date_str = "2024-07-22";
|
|
|
|
let test_cases = [
|
|
// Format 1: YYYY-MM-DD (no timezone applicable)
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21".to_string())),
|
|
test_date_str,
|
|
),
|
|
// Format 2: YYYY-MM-DD HH:MM
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30".to_string())),
|
|
test_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30+02:00".to_string())),
|
|
test_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30-05:00".to_string())),
|
|
next_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 01:30+05:00".to_string())),
|
|
prev_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30Z".to_string())),
|
|
test_date_str,
|
|
),
|
|
// Format 3: YYYY-MM-DD HH:MM:SS
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45".to_string())),
|
|
test_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45+02:00".to_string())),
|
|
test_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45-05:00".to_string())),
|
|
next_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 01:30:45+05:00".to_string())),
|
|
prev_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45Z".to_string())),
|
|
test_date_str,
|
|
),
|
|
// Format 4: YYYY-MM-DD HH:MM:SS.SSS
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45.123".to_string())),
|
|
test_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45.123+02:00".to_string())),
|
|
test_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45.123-05:00".to_string())),
|
|
next_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 01:30:45.123+05:00".to_string())),
|
|
prev_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45.123Z".to_string())),
|
|
test_date_str,
|
|
),
|
|
// Format 5: YYYY-MM-DDTHH:MM
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30".to_string())),
|
|
test_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30+02:00".to_string())),
|
|
test_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30-05:00".to_string())),
|
|
next_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T01:30+05:00".to_string())),
|
|
prev_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30Z".to_string())),
|
|
test_date_str,
|
|
),
|
|
// Format 6: YYYY-MM-DDTHH:MM:SS
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45".to_string())),
|
|
test_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45+02:00".to_string())),
|
|
test_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45-05:00".to_string())),
|
|
next_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T01:30:45+05:00".to_string())),
|
|
prev_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45Z".to_string())),
|
|
test_date_str,
|
|
),
|
|
// Format 7: YYYY-MM-DDTHH:MM:SS.SSS
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45.123".to_string())),
|
|
test_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45.123+02:00".to_string())),
|
|
test_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45.123-05:00".to_string())),
|
|
next_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T01:30:45.123+05:00".to_string())),
|
|
prev_date_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45.123Z".to_string())),
|
|
test_date_str,
|
|
),
|
|
// Format 8: HH:MM
|
|
(OwnedValue::Text(Rc::new("22:30".to_string())), "2000-01-01"),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30+02:00".to_string())),
|
|
"2000-01-01",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30-05:00".to_string())),
|
|
"2000-01-02",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("01:30+05:00".to_string())),
|
|
"1999-12-31",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30Z".to_string())),
|
|
"2000-01-01",
|
|
),
|
|
// Format 9: HH:MM:SS
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45".to_string())),
|
|
"2000-01-01",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45+02:00".to_string())),
|
|
"2000-01-01",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45-05:00".to_string())),
|
|
"2000-01-02",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("01:30:45+05:00".to_string())),
|
|
"1999-12-31",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45Z".to_string())),
|
|
"2000-01-01",
|
|
),
|
|
// Format 10: HH:MM:SS.SSS
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45.123".to_string())),
|
|
"2000-01-01",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45.123+02:00".to_string())),
|
|
"2000-01-01",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45.123-05:00".to_string())),
|
|
"2000-01-02",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("01:30:45.123+05:00".to_string())),
|
|
"1999-12-31",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45.123Z".to_string())),
|
|
"2000-01-01",
|
|
),
|
|
// Test Format 11: 'now'
|
|
(OwnedValue::Text(Rc::new("now".to_string())), &now),
|
|
// Format 12: DDDDDDDDDD (Julian date as float or integer)
|
|
(OwnedValue::Float(2460512.5), test_date_str),
|
|
(OwnedValue::Integer(2460513), test_date_str),
|
|
];
|
|
|
|
for (input, expected) in test_cases {
|
|
assert_eq!(
|
|
get_date_from_time_value(&input).unwrap(),
|
|
expected,
|
|
"Failed for input: {:?}",
|
|
input
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_invalid_get_date_from_time_value() {
|
|
let invalid_cases = [
|
|
OwnedValue::Text(Rc::new("2024-07-21 25:00".to_string())), // Invalid hour
|
|
OwnedValue::Text(Rc::new("2024-07-21 24:00:00".to_string())), // Invalid hour
|
|
OwnedValue::Text(Rc::new("2024-07-21 23:60:00".to_string())), // Invalid minute
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:58:60".to_string())), // Invalid second
|
|
OwnedValue::Text(Rc::new("2024-07-32".to_string())), // Invalid day
|
|
OwnedValue::Text(Rc::new("2024-13-01".to_string())), // Invalid month
|
|
OwnedValue::Text(Rc::new("invalid_date".to_string())), // Completely invalid string
|
|
OwnedValue::Text(Rc::new("".to_string())), // Empty string
|
|
OwnedValue::Integer(i64::MAX), // Large Julian day
|
|
OwnedValue::Integer(-1), // Negative Julian day
|
|
OwnedValue::Float(f64::MAX), // Large float
|
|
OwnedValue::Float(-1.0), // Negative Julian day as float
|
|
OwnedValue::Float(f64::NAN), // NaN
|
|
OwnedValue::Float(f64::INFINITY), // Infinity
|
|
OwnedValue::Null, // Null value
|
|
OwnedValue::Blob(vec![1, 2, 3].into()), // Blob (unsupported type)
|
|
// Invalid timezone tests
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00+24:00".to_string())), // Invalid timezone offset (too large)
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00-24:00".to_string())), // Invalid timezone offset (too small)
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00+00:60".to_string())), // Invalid timezone minutes
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00+00:00:00".to_string())), // Invalid timezone format (extra seconds)
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00+".to_string())), // Incomplete timezone
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00+Z".to_string())), // Invalid timezone format
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00+00:00Z".to_string())), // Mixing offset and Z
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00UTC".to_string())), // Named timezone (not supported)
|
|
];
|
|
|
|
for case in invalid_cases.iter() {
|
|
let result = get_date_from_time_value(case);
|
|
assert!(
|
|
result.is_ok(),
|
|
"Error encountered while parsing time value {}: {}",
|
|
case,
|
|
result.unwrap_err()
|
|
);
|
|
let result_str = result.unwrap();
|
|
assert!(
|
|
result_str.is_empty(),
|
|
"Expected empty string for input: {:?}, but got: {:?}",
|
|
case,
|
|
result_str
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_valid_get_time_from_datetime_value() {
|
|
let now = chrono::Local::now().to_utc().format("%H:%M:%S").to_string();
|
|
|
|
let test_time_str = "22:30:45";
|
|
let prev_time_str = "20:30:45";
|
|
let next_time_str = "03:30:45";
|
|
|
|
let test_cases = [
|
|
// Format 1: YYYY-MM-DD (no timezone applicable)
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21".to_string())),
|
|
"00:00:00",
|
|
),
|
|
// Format 2: YYYY-MM-DD HH:MM
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30".to_string())),
|
|
"22:30:00",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30+02:00".to_string())),
|
|
"20:30:00",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30-05:00".to_string())),
|
|
"03:30:00",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30Z".to_string())),
|
|
"22:30:00",
|
|
),
|
|
// Format 3: YYYY-MM-DD HH:MM:SS
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45".to_string())),
|
|
test_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45+02:00".to_string())),
|
|
prev_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45-05:00".to_string())),
|
|
next_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45Z".to_string())),
|
|
test_time_str,
|
|
),
|
|
// Format 4: YYYY-MM-DD HH:MM:SS.SSS
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45.123".to_string())),
|
|
test_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45.123+02:00".to_string())),
|
|
prev_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45.123-05:00".to_string())),
|
|
next_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:30:45.123Z".to_string())),
|
|
test_time_str,
|
|
),
|
|
// Format 5: YYYY-MM-DDTHH:MM
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30".to_string())),
|
|
"22:30:00",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30+02:00".to_string())),
|
|
"20:30:00",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30-05:00".to_string())),
|
|
"03:30:00",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30Z".to_string())),
|
|
"22:30:00",
|
|
),
|
|
// Format 6: YYYY-MM-DDTHH:MM:SS
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45".to_string())),
|
|
test_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45+02:00".to_string())),
|
|
prev_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45-05:00".to_string())),
|
|
next_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45Z".to_string())),
|
|
test_time_str,
|
|
),
|
|
// Format 7: YYYY-MM-DDTHH:MM:SS.SSS
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45.123".to_string())),
|
|
test_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45.123+02:00".to_string())),
|
|
prev_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45.123-05:00".to_string())),
|
|
next_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("2024-07-21T22:30:45.123Z".to_string())),
|
|
test_time_str,
|
|
),
|
|
// Format 8: HH:MM
|
|
(OwnedValue::Text(Rc::new("22:30".to_string())), "22:30:00"),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30+02:00".to_string())),
|
|
"20:30:00",
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30-05:00".to_string())),
|
|
"03:30:00",
|
|
),
|
|
(OwnedValue::Text(Rc::new("22:30Z".to_string())), "22:30:00"),
|
|
// Format 9: HH:MM:SS
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45".to_string())),
|
|
test_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45+02:00".to_string())),
|
|
prev_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45-05:00".to_string())),
|
|
next_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45Z".to_string())),
|
|
test_time_str,
|
|
),
|
|
// Format 10: HH:MM:SS.SSS
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45.123".to_string())),
|
|
test_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45.123+02:00".to_string())),
|
|
prev_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45.123-05:00".to_string())),
|
|
next_time_str,
|
|
),
|
|
(
|
|
OwnedValue::Text(Rc::new("22:30:45.123Z".to_string())),
|
|
test_time_str,
|
|
),
|
|
// Test Format 11: 'now'
|
|
(OwnedValue::Text(Rc::new("now".to_string())), &now),
|
|
// Format 12: DDDDDDDDDD (Julian date as float or integer)
|
|
(OwnedValue::Float(2460082.1), "14:24:00"),
|
|
(OwnedValue::Integer(2460082), "12:00:00"),
|
|
];
|
|
|
|
for (input, expected) in test_cases {
|
|
assert_eq!(
|
|
get_time_from_datetime_value(&input).unwrap(),
|
|
expected,
|
|
"Failed for input: {:?}",
|
|
input
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_invalid_get_time_from_datetime_value() {
|
|
let invalid_cases = [
|
|
OwnedValue::Text(Rc::new("2024-07-21 25:00".to_string())), // Invalid hour
|
|
OwnedValue::Text(Rc::new("2024-07-21 24:00:00".to_string())), // Invalid hour
|
|
OwnedValue::Text(Rc::new("2024-07-21 23:60:00".to_string())), // Invalid minute
|
|
OwnedValue::Text(Rc::new("2024-07-21 22:58:60".to_string())), // Invalid second
|
|
OwnedValue::Text(Rc::new("2024-07-32".to_string())), // Invalid day
|
|
OwnedValue::Text(Rc::new("2024-13-01".to_string())), // Invalid month
|
|
OwnedValue::Text(Rc::new("invalid_date".to_string())), // Completely invalid string
|
|
OwnedValue::Text(Rc::new("".to_string())), // Empty string
|
|
OwnedValue::Integer(i64::MAX), // Large Julian day
|
|
OwnedValue::Integer(-1), // Negative Julian day
|
|
OwnedValue::Float(f64::MAX), // Large float
|
|
OwnedValue::Float(-1.0), // Negative Julian day as float
|
|
OwnedValue::Float(f64::NAN), // NaN
|
|
OwnedValue::Float(f64::INFINITY), // Infinity
|
|
OwnedValue::Null, // Null value
|
|
OwnedValue::Blob(vec![1, 2, 3].into()), // Blob (unsupported type)
|
|
// Invalid timezone tests
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00+24:00".to_string())), // Invalid timezone offset (too large)
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00-24:00".to_string())), // Invalid timezone offset (too small)
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00+00:60".to_string())), // Invalid timezone minutes
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00+00:00:00".to_string())), // Invalid timezone format (extra seconds)
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00+".to_string())), // Incomplete timezone
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00+Z".to_string())), // Invalid timezone format
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00+00:00Z".to_string())), // Mixing offset and Z
|
|
OwnedValue::Text(Rc::new("2024-07-21T12:00:00UTC".to_string())), // Named timezone (not supported)
|
|
];
|
|
|
|
for case in invalid_cases.iter() {
|
|
let result = get_time_from_datetime_value(case);
|
|
assert!(
|
|
result.is_ok(),
|
|
"Error encountered while parsing time value {}: {}",
|
|
case,
|
|
result.unwrap_err()
|
|
);
|
|
let result_str = result.unwrap();
|
|
assert!(
|
|
result_str.is_empty(),
|
|
"Expected empty string for input: {:?}, but got: {:?}",
|
|
case,
|
|
result_str
|
|
);
|
|
}
|
|
}
|
|
}
|