Skip to main content

citum_schema_data/reference/
date.rs

1/*
2SPDX-License-Identifier: MIT OR Apache-2.0
3SPDX-FileCopyrightText: © 2023-2026 Bruce D'Arcus and Citum contributors
4*/
5
6use crate::reference::types::RefDate;
7#[cfg(feature = "schema")]
8use schemars::JsonSchema;
9use serde::{Deserialize, Serialize};
10use std::fmt;
11
12/// An EDTF string.
13#[derive(Debug, Deserialize, Serialize, Clone, Default, PartialEq)]
14#[cfg_attr(feature = "schema", derive(JsonSchema))]
15#[cfg_attr(feature = "bindings", derive(specta::Type))]
16pub struct EdtfString(pub String);
17
18impl EdtfString {
19    /// Check if the EdtfString is empty.
20    pub fn is_empty(&self) -> bool {
21        self.0.is_empty()
22    }
23
24    /// Parse the string as an EDTF date etc, or return the string as a literal.
25    pub fn parse(&self) -> RefDate {
26        let mut input = self.0.as_str();
27        match citum_edtf::parse(&mut input) {
28            Ok(edtf) => RefDate::Edtf(edtf),
29            Err(_) => RefDate::Literal(self.0.clone()),
30        }
31    }
32
33    /// Extract the year from the date.
34    pub fn year(&self) -> String {
35        match self.parse() {
36            RefDate::Edtf(edtf) => edtf.year().to_string(),
37            RefDate::Literal(_) => String::new(),
38        }
39    }
40
41    /// Extract the day from the date.
42    pub fn day(&self) -> Option<u32> {
43        match self.parse() {
44            RefDate::Edtf(edtf) => edtf.day().filter(|&d| d > 0),
45            RefDate::Literal(_) => None,
46        }
47    }
48
49    /// Check if the date is uncertain (has "?" qualifier).
50    pub fn is_uncertain(&self) -> bool {
51        self.0.contains('?')
52    }
53
54    /// Check if the date is approximate (has "~" qualifier).
55    pub fn is_approximate(&self) -> bool {
56        self.0.contains('~')
57    }
58
59    /// Check if the date is a range (interval).
60    pub fn is_range(&self) -> bool {
61        matches!(self.parse(), RefDate::Edtf(edtf) if edtf.is_range())
62    }
63
64    /// Check if the range is open-ended (ends with "..").
65    pub fn is_open_range(&self) -> bool {
66        matches!(self.parse(), RefDate::Edtf(edtf) if edtf.is_open_range())
67    }
68
69    /// Extract the time component from the date, if present.
70    pub fn time(&self) -> Option<citum_edtf::Time> {
71        match self.parse() {
72            RefDate::Edtf(edtf) => edtf.time(),
73            _ => None,
74        }
75    }
76
77    /// Check if the date has a time component.
78    pub fn has_time(&self) -> bool {
79        self.time().is_some()
80    }
81}
82
83impl fmt::Display for EdtfString {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        write!(f, "{}", self.0)
86    }
87}