use crate::errors::ProjectionError;
use chrono::{DateTime, FixedOffset, Local, NaiveDateTime, TimeZone};
pub fn parse_timestamp_flexible(s: &str) -> Result<DateTime<FixedOffset>, ProjectionError> {
parse_timestamp_flexible_str(s).map_err(ProjectionError::InvalidTimestamp)
}
pub fn parse_timestamp_flexible_str(s: &str) -> Result<DateTime<FixedOffset>, String> {
if let Ok(dt) = DateTime::parse_from_rfc3339(s) {
return Ok(dt);
}
let naive = NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f")
.or_else(|_| NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S"))
.or_else(|_| NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S%.f"))
.or_else(|_| NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S"))
.map_err(|e| {
format!(
"{} (expected RFC3339 with timezone, e.g. 2025-12-09T14:30:00+01:00, \
or ISO 8601 without timezone interpreted as local time)",
e
)
})?;
Local
.from_local_datetime(&naive)
.single()
.map(|dt| dt.fixed_offset())
.ok_or_else(|| format!("ambiguous or non-existent local time: '{}'", s))
}
pub fn parse_rfc3339_with_timezone(s: &str) -> Result<DateTime<FixedOffset>, ProjectionError> {
DateTime::parse_from_rfc3339(s)
.map_err(|e| ProjectionError::MissingTimezone(format!("Invalid timestamp: {}", e)))
}
pub fn validate_timezone_present(_dt: &DateTime<FixedOffset>) -> Result<(), ProjectionError> {
Ok(())
}
#[cfg(test)]
mod tests;