Skip to content
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
1 change: 0 additions & 1 deletion mordant-baseline.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@
"bare_bool_args:src/shell_parser/parse.rs" = 1

[bun_sql_jsc]
"bare_bool_args:src/sql_jsc/shared/datetime_text.rs" = 1

[bun_sys]
"error_collapsed_to_bool:src/sys/lib.rs" = 4
31 changes: 26 additions & 5 deletions src/sql_jsc/shared/datetime_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,36 @@ pub struct DateTimeText {
pub(crate) microsecond: u32,
}

/// Whether the 10-byte date-only form (`YYYY-MM-DD`) parses, or only the full
/// date-and-time shape does.
#[derive(Clone, Copy)]
enum TimePart {
Optional,
Required,
}

/// Which bytes may separate the date from the time.
#[derive(Clone, Copy)]
enum Separator {
Space,
SpaceOrT,
}

/// MySQL DATE/DATETIME/TIMESTAMP text. Accepts the 10-byte date-only form
/// (`YYYY-MM-DD`) and either `' '` or `'T'` as the date/time separator.
pub(crate) fn parse_mysql(text: &[u8]) -> Option<DateTimeText> {
parse(text, true, true)
parse(text, TimePart::Optional, Separator::SpaceOrT)
}

/// Postgres `timestamp` (WITHOUT TIME ZONE) text. Requires the full
/// `YYYY-MM-DD HH:MM:SS[.ffffff]` shape — anything else (date-only, `'T'`
/// separator, `infinity`, BC dates, 5+ digit years) returns `None` so the
/// caller can fall back to `Date.parse`.
pub(crate) fn parse_postgres_timestamp(text: &[u8]) -> Option<DateTimeText> {
parse(text, false, false)
parse(text, TimePart::Required, Separator::Space)
}

fn parse(text: &[u8], allow_date_only: bool, allow_t_separator: bool) -> Option<DateTimeText> {
fn parse(text: &[u8], time_part: TimePart, separator: Separator) -> Option<DateTimeText> {
fn parse_u(bytes: &[u8]) -> Option<u32> {
if bytes.is_empty() {
return None;
Expand All @@ -63,10 +78,16 @@ fn parse(text: &[u8], allow_date_only: bool, allow_t_separator: bool) -> Option<
..Default::default()
};
if text.len() == 10 {
return if allow_date_only { Some(result) } else { None };
return match time_part {
TimePart::Optional => Some(result),
TimePart::Required => None,
};
}

let separator_ok = text[10] == b' ' || (allow_t_separator && text[10] == b'T');
let separator_ok = match separator {
Separator::Space => text[10] == b' ',
Separator::SpaceOrT => text[10] == b' ' || text[10] == b'T',
};
if text.len() < 19 || !separator_ok || text[13] != b':' || text[16] != b':' {
return None;
}
Expand Down
Loading