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        match self.0.parse::<citum_edtf::Edtf>() {
27            Ok(edtf) => RefDate::Edtf(edtf),
28            Err(_) => RefDate::Literal(self.0.clone()),
29        }
30    }
31
32    /// Extract the year from the date.
33    pub fn year(&self) -> String {
34        match self.parse() {
35            RefDate::Edtf(edtf) => edtf.year().to_string(),
36            RefDate::Literal(_) => String::new(),
37        }
38    }
39
40    /// Extract the day from the date.
41    pub fn day(&self) -> Option<u32> {
42        match self.parse() {
43            RefDate::Edtf(edtf) => edtf.day().filter(|&d| d > 0),
44            RefDate::Literal(_) => None,
45        }
46    }
47
48    /// Check if the date is uncertain (has "?" qualifier).
49    pub fn is_uncertain(&self) -> bool {
50        self.0.contains('?')
51    }
52
53    /// Check if the date is approximate (has "~" qualifier).
54    pub fn is_approximate(&self) -> bool {
55        self.0.contains('~')
56    }
57
58    /// Check if the date is a range (interval).
59    pub fn is_range(&self) -> bool {
60        matches!(self.parse(), RefDate::Edtf(edtf) if edtf.is_range())
61    }
62
63    /// Check if the range is open-ended (ends with "..").
64    pub fn is_open_range(&self) -> bool {
65        matches!(self.parse(), RefDate::Edtf(edtf) if edtf.is_open_range())
66    }
67
68    /// Extract the time component from the date, if present.
69    pub fn time(&self) -> Option<citum_edtf::Time> {
70        match self.parse() {
71            RefDate::Edtf(edtf) => edtf.time(),
72            _ => None,
73        }
74    }
75
76    /// Check if the date has a time component.
77    pub fn has_time(&self) -> bool {
78        self.time().is_some()
79    }
80}
81
82impl fmt::Display for EdtfString {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        write!(f, "{}", self.0)
85    }
86}