Skip to main content

karbon_framework/validation/constraints/date/
date_time.rs

1use crate::validation::constraints::{Constraint, ConstraintResult, ConstraintViolation};
2
3/// Validates that a value is a valid date-time string.
4///
5/// Equivalent to Symfony's `DateTime` constraint.
6/// Default format: YYYY-MM-DD HH:MM:SS
7pub struct DateTime {
8    pub message: String,
9    pub format: String,
10}
11
12impl Default for DateTime {
13    fn default() -> Self {
14        Self {
15            message: "This value is not a valid datetime.".to_string(),
16            format: "%Y-%m-%d %H:%M:%S".to_string(),
17        }
18    }
19}
20
21impl DateTime {
22    pub fn new() -> Self {
23        Self::default()
24    }
25
26    pub fn with_message(mut self, message: impl Into<String>) -> Self {
27        self.message = message.into();
28        self
29    }
30
31    pub fn with_format(mut self, format: impl Into<String>) -> Self {
32        self.format = format.into();
33        self
34    }
35
36    /// Accept ISO 8601 format (e.g., 2024-01-01T12:00:00)
37    pub fn iso8601() -> Self {
38        Self {
39            format: "%Y-%m-%dT%H:%M:%S".to_string(),
40            ..Self::default()
41        }
42    }
43}
44
45impl Constraint for DateTime {
46    fn validate(&self, value: &str) -> ConstraintResult {
47        if chrono::NaiveDateTime::parse_from_str(value, &self.format).is_err() {
48            return Err(ConstraintViolation::new(
49                self.name(),
50                &self.message,
51                value,
52            ));
53        }
54        Ok(())
55    }
56
57    fn name(&self) -> &'static str {
58        "DateTime"
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn test_valid_datetimes() {
68        let constraint = DateTime::new();
69        assert!(constraint.validate("2024-01-01 00:00:00").is_ok());
70        assert!(constraint.validate("2024-12-31 23:59:59").is_ok());
71        assert!(constraint.validate("2024-06-15 14:30:00").is_ok());
72    }
73
74    #[test]
75    fn test_invalid_datetimes() {
76        let constraint = DateTime::new();
77        assert!(constraint.validate("").is_err());
78        assert!(constraint.validate("2024-01-01").is_err()); // missing time
79        assert!(constraint.validate("not-a-datetime").is_err());
80        assert!(constraint.validate("2024-01-01 25:00:00").is_err()); // invalid hour
81    }
82
83    #[test]
84    fn test_iso8601_format() {
85        let constraint = DateTime::iso8601();
86        assert!(constraint.validate("2024-01-01T12:00:00").is_ok());
87        assert!(constraint.validate("2024-01-01 12:00:00").is_err());
88    }
89
90    #[test]
91    fn test_custom_format() {
92        let constraint = DateTime::new().with_format("%d/%m/%Y %H:%M");
93        assert!(constraint.validate("01/01/2024 12:00").is_ok());
94        assert!(constraint.validate("2024-01-01 12:00:00").is_err());
95    }
96}