use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, Utc};
use sqlx::any::AnyArguments;
use sqlx::Arguments;
use crate::database::Drivers;
use crate::Error;
pub fn bind_datetime_utc(
query_args: &mut AnyArguments<'_>,
value: &DateTime<Utc>,
driver: &Drivers,
) -> Result<(), Error> {
match driver {
Drivers::Postgres => {
let _ = query_args.add(value.to_rfc3339());
}
Drivers::MySQL => {
let formatted = value.format("%Y-%m-%d %H:%M:%S%.6f").to_string();
let _ = query_args.add(formatted);
}
Drivers::SQLite => {
let _ = query_args.add(value.to_rfc3339());
}
}
Ok(())
}
pub fn bind_datetime_fixed(
query_args: &mut AnyArguments<'_>,
value: &DateTime<FixedOffset>,
driver: &Drivers,
) -> Result<(), Error> {
match driver {
Drivers::Postgres => {
let _ = query_args.add(value.to_rfc3339());
}
Drivers::MySQL => {
let value_utc: DateTime<Utc> = value.with_timezone(&Utc);
let formatted = value_utc.format("%Y-%m-%d %H:%M:%S%.6f").to_string();
let _ = query_args.add(formatted);
}
Drivers::SQLite => {
let _ = query_args.add(value.to_rfc3339());
}
}
Ok(())
}
pub fn parse_datetime_utc(value: &str) -> Result<DateTime<Utc>, Error> {
if let Ok(dt) = value.parse::<DateTime<Utc>>() {
return Ok(dt);
}
if let Ok(dt) = DateTime::parse_from_rfc3339(value) {
return Ok(dt.with_timezone(&Utc));
}
if let Ok(naive) = NaiveDateTime::parse_from_str(value, "%Y-%m-%d %H:%M:%S%.f") {
return Ok(DateTime::from_naive_utc_and_offset(naive, Utc));
}
if let Ok(naive) = NaiveDateTime::parse_from_str(value, "%Y-%m-%d %H:%M:%S") {
return Ok(DateTime::from_naive_utc_and_offset(naive, Utc));
}
Err(Error::Conversion(format!("Failed to parse DateTime<Utc> from '{}'", value)))
}
pub fn parse_datetime_fixed(value: &str) -> Result<DateTime<FixedOffset>, Error> {
if let Ok(dt) = DateTime::parse_from_rfc3339(value) {
return Ok(dt);
}
if let Ok(naive) = NaiveDateTime::parse_from_str(value, "%Y-%m-%d %H:%M:%S%.f") {
let offset = FixedOffset::east_opt(0).unwrap();
return Ok(DateTime::from_naive_utc_and_offset(naive, offset));
}
Err(Error::Conversion(format!("Failed to parse DateTime<FixedOffset> from '{}'", value)))
}
pub fn bind_naive_datetime(
query_args: &mut AnyArguments<'_>,
value: &NaiveDateTime,
driver: &Drivers,
) -> Result<(), Error> {
match driver {
Drivers::Postgres => {
let formatted = value.format("%Y-%m-%d %H:%M:%S%.6f").to_string();
let _ = query_args.add(formatted);
}
Drivers::MySQL => {
let formatted = value.format("%Y-%m-%d %H:%M:%S%.6f").to_string();
let _ = query_args.add(formatted);
}
Drivers::SQLite => {
let formatted = value.format("%Y-%m-%d %H:%M:%S%.f").to_string();
let _ = query_args.add(formatted);
}
}
Ok(())
}
pub fn parse_naive_datetime(value: &str) -> Result<NaiveDateTime, Error> {
if let Ok(dt) = value.parse::<NaiveDateTime>() {
return Ok(dt);
}
if let Ok(dt) = NaiveDateTime::parse_from_str(value, "%Y-%m-%d %H:%M:%S") {
return Ok(dt);
}
if let Ok(dt) = NaiveDateTime::parse_from_str(value, "%Y-%m-%d %H:%M:%S%.f") {
return Ok(dt);
}
if let Ok(dt) = NaiveDateTime::parse_from_str(value, "%Y-%m-%d %H:%M") {
return Ok(dt);
}
if let Ok(dt) = NaiveDateTime::parse_from_str(value, "%Y-%m-%dT%H:%M:%S%.f") {
return Ok(dt);
}
Err(Error::Conversion(format!("Failed to parse NaiveDateTime from '{}'", value)))
}
pub fn bind_naive_date(query_args: &mut AnyArguments<'_>, value: &NaiveDate, driver: &Drivers) -> Result<(), Error> {
match driver {
Drivers::Postgres | Drivers::MySQL | Drivers::SQLite => {
let formatted = value.format("%Y-%m-%d").to_string();
let _ = query_args.add(formatted);
}
}
Ok(())
}
pub fn parse_naive_date(value: &str) -> Result<NaiveDate, Error> {
value.parse::<NaiveDate>().map_err(|e| Error::Conversion(format!("Failed to parse NaiveDate: {}", e)))
}
pub fn bind_naive_time(query_args: &mut AnyArguments<'_>, value: &NaiveTime, driver: &Drivers) -> Result<(), Error> {
match driver {
Drivers::Postgres | Drivers::MySQL | Drivers::SQLite => {
let formatted = value.format("%H:%M:%S%.6f").to_string();
let _ = query_args.add(formatted);
}
}
Ok(())
}
pub fn parse_naive_time(value: &str) -> Result<NaiveTime, Error> {
value.parse::<NaiveTime>().map_err(|e| Error::Conversion(format!("Failed to parse NaiveTime: {}", e)))
}
pub fn bind_temporal_value(
query_args: &mut AnyArguments<'_>,
value_str: &str,
sql_type: &str,
driver: &Drivers,
) -> Result<(), Error> {
match sql_type {
"TIMESTAMPTZ" | "DateTime" => {
let value = parse_datetime_utc(value_str)?;
bind_datetime_utc(query_args, &value, driver)
}
"TIMESTAMP" | "NaiveDateTime" => {
let value = parse_naive_datetime(value_str)?;
bind_naive_datetime(query_args, &value, driver)
}
"DATE" | "NaiveDate" => {
let value = parse_naive_date(value_str)?;
bind_naive_date(query_args, &value, driver)
}
"TIME" | "NaiveTime" => {
let value = parse_naive_time(value_str)?;
bind_naive_time(query_args, &value, driver)
}
_ => Err(Error::Conversion(format!("Unknown temporal SQL type: {}", sql_type))),
}
}
pub fn get_postgres_type_cast(sql_type: &str) -> &'static str {
let normalized = sql_type.to_uppercase();
match normalized.as_str() {
"TIMESTAMPTZ" | "TIMESTAMP WITH TIME ZONE" | "DATETIME" => "::TIMESTAMPTZ",
"TIMESTAMP" | "TIMESTAMP WITHOUT TIME ZONE" | "NAIVEDATETIME" => "::TIMESTAMP",
"DATE" | "NAIVEDATE" => "::DATE",
"TIME" | "NAIVETIME" => "::TIME",
_ => "",
}
}
pub fn is_temporal_type(sql_type: &str) -> bool {
let normalized = sql_type.to_uppercase();
matches!(
normalized.as_str(),
"TIMESTAMPTZ"
| "TIMESTAMP WITH TIME ZONE"
| "TIMESTAMP"
| "TIMESTAMP WITHOUT TIME ZONE"
| "DATETIME"
| "DATE"
| "TIME"
| "NAIVEDATETIME"
| "NAIVEDATE"
| "NAIVETIME"
)
}
pub fn format_datetime_for_driver(value: &DateTime<Utc>, driver: &Drivers) -> String {
match driver {
Drivers::Postgres | Drivers::SQLite => value.to_rfc3339(),
Drivers::MySQL => value.format("%Y-%m-%d %H:%M:%S%.6f").to_string(),
}
}
pub fn format_datetime_fixed_for_driver(value: &DateTime<FixedOffset>, driver: &Drivers) -> String {
match driver {
Drivers::Postgres => value.to_rfc3339(),
Drivers::MySQL => {
let value_utc: DateTime<Utc> = value.with_timezone(&Utc);
value_utc.format("%Y-%m-%d %H:%M:%S%.6f").to_string()
}
Drivers::SQLite => value.to_rfc3339(),
}
}
pub fn format_naive_datetime_for_driver(value: &NaiveDateTime, driver: &Drivers) -> String {
match driver {
Drivers::Postgres | Drivers::MySQL => value.format("%Y-%m-%d %H:%M:%S%.6f").to_string(),
Drivers::SQLite => value.format("%Y-%m-%d %H:%M:%S%.f").to_string(),
}
}