use bottle_orm::{
database::Drivers,
temporal::{
format_datetime_fixed_for_driver, format_datetime_for_driver,
format_naive_datetime_for_driver, get_postgres_type_cast, is_temporal_type,
parse_datetime_fixed, parse_datetime_utc, parse_naive_date, parse_naive_datetime,
parse_naive_time,
},
};
use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
#[test]
fn test_parse_datetime_utc_rfc3339() {
let result = parse_datetime_utc("2024-06-15T12:30:00Z");
assert!(result.is_ok());
let dt = result.unwrap();
assert_eq!(dt.year(), 2024);
assert_eq!(dt.month(), 6);
assert_eq!(dt.day(), 15);
assert_eq!(dt.hour(), 12);
assert_eq!(dt.minute(), 30);
}
#[test]
fn test_parse_datetime_utc_with_offset() {
let result = parse_datetime_utc("2024-01-01T10:00:00+02:00");
assert!(result.is_ok());
let dt = result.unwrap();
assert_eq!(dt.hour(), 8);
}
#[test]
fn test_parse_datetime_utc_mysql_format() {
let result = parse_datetime_utc("2024-03-20 14:45:30.123456");
assert!(result.is_ok());
let dt = result.unwrap();
assert_eq!(dt.year(), 2024);
assert_eq!(dt.month(), 3);
assert_eq!(dt.day(), 20);
assert_eq!(dt.hour(), 14);
}
#[test]
fn test_parse_datetime_utc_no_subseconds() {
let result = parse_datetime_utc("2024-03-20 14:45:30");
assert!(result.is_ok());
let dt = result.unwrap();
assert_eq!(dt.second(), 30);
}
#[test]
fn test_parse_datetime_utc_invalid() {
let result = parse_datetime_utc("not-a-date");
assert!(result.is_err());
let err_msg = format!("{}", result.unwrap_err());
assert!(err_msg.contains("not-a-date"));
}
#[test]
fn test_parse_datetime_fixed_rfc3339() {
let result = parse_datetime_fixed("2024-06-15T12:30:00+03:00");
assert!(result.is_ok());
let dt = result.unwrap();
assert_eq!(dt.hour(), 12);
assert_eq!(dt.offset().local_minus_utc(), 3 * 3600);
}
#[test]
fn test_parse_datetime_fixed_naive_fallback() {
let result = parse_datetime_fixed("2024-01-10 09:15:00.000");
assert!(result.is_ok());
let dt = result.unwrap();
assert_eq!(dt.offset().local_minus_utc(), 0);
}
#[test]
fn test_parse_datetime_fixed_invalid() {
let result = parse_datetime_fixed("garbage");
assert!(result.is_err());
}
#[test]
fn test_parse_naive_datetime_space_format() {
let result = parse_naive_datetime("2024-07-04 08:00:00");
assert!(result.is_ok());
let dt = result.unwrap();
assert_eq!(dt.year(), 2024);
assert_eq!(dt.month(), 7);
assert_eq!(dt.day(), 4);
}
#[test]
fn test_parse_naive_datetime_with_subseconds() {
let result = parse_naive_datetime("2024-07-04 08:00:00.999");
assert!(result.is_ok());
let dt = result.unwrap();
assert_eq!(dt.second(), 0);
}
#[test]
fn test_parse_naive_datetime_t_separator() {
let result = parse_naive_datetime("2024-12-31T23:59:59.000");
assert!(result.is_ok());
let dt = result.unwrap();
assert_eq!(dt.hour(), 23);
assert_eq!(dt.minute(), 59);
}
#[test]
fn test_parse_naive_datetime_no_seconds() {
let result = parse_naive_datetime("2024-01-01 12:30");
assert!(result.is_ok());
let dt = result.unwrap();
assert_eq!(dt.hour(), 12);
assert_eq!(dt.minute(), 30);
}
#[test]
fn test_parse_naive_datetime_invalid() {
let result = parse_naive_datetime("2024-13-40 99:99:99");
assert!(result.is_err());
}
#[test]
fn test_parse_naive_date_valid() {
let result = parse_naive_date("2024-02-29");
assert!(result.is_ok()); let d = result.unwrap();
assert_eq!(d.month(), 2);
assert_eq!(d.day(), 29);
}
#[test]
fn test_parse_naive_date_invalid_leap() {
let result = parse_naive_date("2023-02-29");
assert!(result.is_err());
}
#[test]
fn test_parse_naive_date_invalid_format() {
let result = parse_naive_date("not-a-date");
assert!(result.is_err());
}
#[test]
fn test_parse_naive_time_valid() {
let result = parse_naive_time("14:30:00");
assert!(result.is_ok());
let t = result.unwrap();
assert_eq!(t.hour(), 14);
assert_eq!(t.minute(), 30);
assert_eq!(t.second(), 0);
}
#[test]
fn test_parse_naive_time_with_subseconds() {
let result = parse_naive_time("09:05:30.123456");
assert!(result.is_ok());
let t = result.unwrap();
assert_eq!(t.hour(), 9);
assert_eq!(t.minute(), 5);
assert_eq!(t.second(), 30);
}
#[test]
fn test_parse_naive_time_invalid() {
let result = parse_naive_time("25:00:00");
assert!(result.is_err());
}
#[test]
fn test_format_datetime_postgres_is_rfc3339() {
let dt: DateTime<Utc> = Utc.with_ymd_and_hms(2024, 1, 15, 10, 20, 30).unwrap();
let s = format_datetime_for_driver(&dt, &Drivers::Postgres);
assert!(s.contains('T'));
assert!(s.contains('+') || s.ends_with('Z'));
}
#[test]
fn test_format_datetime_sqlite_is_rfc3339() {
let dt: DateTime<Utc> = Utc.with_ymd_and_hms(2024, 1, 15, 10, 20, 30).unwrap();
let s = format_datetime_for_driver(&dt, &Drivers::SQLite);
assert!(s.contains('T'));
}
#[test]
fn test_format_datetime_mysql_format() {
let dt: DateTime<Utc> = Utc.with_ymd_and_hms(2024, 6, 1, 8, 5, 3).unwrap();
let s = format_datetime_for_driver(&dt, &Drivers::MySQL);
assert!(s.starts_with("2024-06-01 08:05:03"));
assert!(!s.contains('T'));
}
#[test]
fn test_format_datetime_fixed_postgres_keeps_offset() {
let offset = FixedOffset::east_opt(2 * 3600).unwrap();
let dt: DateTime<FixedOffset> = offset.with_ymd_and_hms(2024, 3, 10, 12, 0, 0).unwrap();
let s = format_datetime_fixed_for_driver(&dt, &Drivers::Postgres);
assert!(s.contains("+02:00"));
}
#[test]
fn test_format_datetime_fixed_mysql_converts_to_utc() {
let offset = FixedOffset::east_opt(3 * 3600).unwrap();
let dt: DateTime<FixedOffset> = offset.with_ymd_and_hms(2024, 3, 10, 15, 0, 0).unwrap();
let s = format_datetime_fixed_for_driver(&dt, &Drivers::MySQL);
assert!(s.starts_with("2024-03-10 12:00:00"));
}
#[test]
fn test_format_datetime_fixed_sqlite_is_rfc3339() {
let offset = FixedOffset::east_opt(0).unwrap();
let dt: DateTime<FixedOffset> = offset.with_ymd_and_hms(2024, 1, 1, 0, 0, 0).unwrap();
let s = format_datetime_fixed_for_driver(&dt, &Drivers::SQLite);
assert!(s.contains('T'));
}
#[test]
fn test_format_naive_datetime_postgres_mysql() {
let dt = NaiveDateTime::new(
NaiveDate::from_ymd_opt(2024, 11, 5).unwrap(),
NaiveTime::from_hms_opt(7, 8, 9).unwrap(),
);
let pg = format_naive_datetime_for_driver(&dt, &Drivers::Postgres);
let my = format_naive_datetime_for_driver(&dt, &Drivers::MySQL);
assert!(pg.starts_with("2024-11-05 07:08:09"));
assert_eq!(pg, my);
}
#[test]
fn test_format_naive_datetime_sqlite() {
let dt = NaiveDateTime::new(
NaiveDate::from_ymd_opt(2024, 11, 5).unwrap(),
NaiveTime::from_hms_opt(7, 8, 9).unwrap(),
);
let s = format_naive_datetime_for_driver(&dt, &Drivers::SQLite);
assert!(s.starts_with("2024-11-05 07:08:09"));
}
#[test]
fn test_postgres_type_cast_timestamptz() {
assert_eq!(get_postgres_type_cast("TIMESTAMPTZ"), "::TIMESTAMPTZ");
assert_eq!(get_postgres_type_cast("TIMESTAMP WITH TIME ZONE"), "::TIMESTAMPTZ");
assert_eq!(get_postgres_type_cast("DateTime"), "::TIMESTAMPTZ");
}
#[test]
fn test_postgres_type_cast_timestamp() {
assert_eq!(get_postgres_type_cast("TIMESTAMP"), "::TIMESTAMP");
assert_eq!(get_postgres_type_cast("NaiveDateTime"), "::TIMESTAMP");
}
#[test]
fn test_postgres_type_cast_date() {
assert_eq!(get_postgres_type_cast("DATE"), "::DATE");
assert_eq!(get_postgres_type_cast("NaiveDate"), "::DATE");
}
#[test]
fn test_postgres_type_cast_time() {
assert_eq!(get_postgres_type_cast("TIME"), "::TIME");
assert_eq!(get_postgres_type_cast("NaiveTime"), "::TIME");
}
#[test]
fn test_postgres_type_cast_case_insensitive() {
assert_eq!(get_postgres_type_cast("timestamptz"), "::TIMESTAMPTZ");
assert_eq!(get_postgres_type_cast("date"), "::DATE");
}
#[test]
fn test_postgres_type_cast_unknown() {
assert_eq!(get_postgres_type_cast("TEXT"), "");
assert_eq!(get_postgres_type_cast("INTEGER"), "");
}
#[test]
fn test_is_temporal_type_positive() {
assert!(is_temporal_type("TIMESTAMPTZ"));
assert!(is_temporal_type("TIMESTAMP WITH TIME ZONE"));
assert!(is_temporal_type("TIMESTAMP"));
assert!(is_temporal_type("TIMESTAMP WITHOUT TIME ZONE"));
assert!(is_temporal_type("DateTime"));
assert!(is_temporal_type("DATE"));
assert!(is_temporal_type("TIME"));
assert!(is_temporal_type("NaiveDateTime"));
assert!(is_temporal_type("NaiveDate"));
assert!(is_temporal_type("NaiveTime"));
}
#[test]
fn test_is_temporal_type_case_insensitive() {
assert!(is_temporal_type("timestamptz"));
assert!(is_temporal_type("date"));
assert!(is_temporal_type("naivedatetime"));
}
#[test]
fn test_is_temporal_type_negative() {
assert!(!is_temporal_type("TEXT"));
assert!(!is_temporal_type("INTEGER"));
assert!(!is_temporal_type("UUID"));
assert!(!is_temporal_type("BOOLEAN"));
assert!(!is_temporal_type(""));
}
use chrono::{Datelike, Timelike};