diff --git a/libs/types-rs/Cargo.toml b/libs/types-rs/Cargo.toml index 6bcc6e293..dc24f8972 100644 --- a/libs/types-rs/Cargo.toml +++ b/libs/types-rs/Cargo.toml @@ -18,7 +18,7 @@ regex = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } sqlx = { workspace = true, optional = true } -time = { workspace = true } +time = { workspace = true, features = ["parsing", "macros"] } uuid = { workspace = true } [dev-dependencies] diff --git a/libs/types-rs/src/election.rs b/libs/types-rs/src/election.rs index baeae8ce0..eae80aeb6 100644 --- a/libs/types-rs/src/election.rs +++ b/libs/types-rs/src/election.rs @@ -3,6 +3,7 @@ use hmac_sha256::Hash; #[cfg(feature = "sqlx")] use sqlx::Type; use std::{fmt::Display, str::FromStr}; +use time::macros::format_description; use serde::{Deserialize, Serialize}; @@ -264,12 +265,39 @@ impl Serialize for PartialElectionHash { } } +mod election_date { + use super::*; + use serde::{de, Deserialize, Deserializer, Serializer}; + use time::format_description; + + const DATE_FORMATTER: &[format_description::FormatItem] = + format_description!("[year]-[month]-[day]"); + + pub fn serialize(date: &time::Date, serializer: S) -> Result + where + S: Serializer, + { + date.format(DATE_FORMATTER) + .unwrap_or_default() + .serialize(serializer) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let date_str = String::deserialize(deserializer)?; + time::Date::parse(date_str.as_str(), DATE_FORMATTER) + .map_err(|e| de::Error::custom(format!("invalid date: {e}"))) + } +} + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Election { pub title: String, - #[serde(with = "time::serde::iso8601")] - pub date: time::OffsetDateTime, + #[serde(with = "election_date")] + pub date: time::Date, pub ballot_styles: Vec, pub precincts: Vec, pub districts: Vec,