opening_hours_syntax/rules/
time.rs1use alloc::vec::Vec;
2use core::cmp::Ordering;
3use core::fmt::Display;
4use core::ops::Range;
5
6use chrono::Duration;
7
8use crate::display::write_selector;
9use crate::extended_time::ExtendedTime;
10
11#[derive(Clone, Debug, Hash, PartialEq, Eq)]
14pub struct TimeSelector {
15 pub spans: Vec<TimeSpan>,
16}
17
18impl TimeSelector {
19 pub(crate) fn is_00_24(&self) -> bool {
21 self.spans.len() == 1
22 && self.spans.first()
23 == Some(&TimeSpan::fixed_range(
24 ExtendedTime::MIDNIGHT_00,
25 ExtendedTime::MIDNIGHT_24,
26 ))
27 }
28}
29
30impl TimeSelector {
31 #[inline]
32 pub fn new(spans: Vec<TimeSpan>) -> Self {
33 if spans.is_empty() {
34 Self::default()
35 } else {
36 Self { spans }
37 }
38 }
39}
40
41impl Default for TimeSelector {
42 #[inline]
43 fn default() -> Self {
44 Self {
45 spans: vec![TimeSpan::fixed_range(
46 ExtendedTime::MIDNIGHT_00,
47 ExtendedTime::MIDNIGHT_24,
48 )],
49 }
50 }
51}
52
53impl Display for TimeSelector {
54 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
55 write_selector(f, &self.spans)
56 }
57}
58
59#[derive(Clone, Debug, Hash, PartialEq, Eq)]
62pub struct TimeSpan {
63 pub range: Range<Time>,
64 pub open_end: bool,
65 pub repeats: Option<Duration>,
66}
67
68impl TimeSpan {
69 #[inline]
70 pub const fn fixed_range(start: ExtendedTime, end: ExtendedTime) -> Self {
71 Self {
72 range: Time::Fixed(start)..Time::Fixed(end),
73 open_end: false,
74 repeats: None,
75 }
76 }
77}
78
79impl Ord for TimeSpan {
80 fn cmp(&self, other: &Self) -> Ordering {
81 (self.range.start.cmp(&other.range.start))
82 .then_with(|| self.range.end.cmp(&other.range.end))
83 .then_with(|| self.open_end.cmp(&other.open_end))
84 .then_with(|| self.repeats.cmp(&other.repeats))
85 }
86}
87
88impl PartialOrd for TimeSpan {
89 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
90 Some(self.cmp(other))
91 }
92}
93
94impl Display for TimeSpan {
95 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96 write!(f, "{}", self.range.start)?;
97
98 if !self.open_end || self.range.end != Time::Fixed(ExtendedTime::MIDNIGHT_24) {
99 write!(f, "-{}", self.range.end)?;
100 }
101
102 if self.open_end {
103 write!(f, "+")?;
104 }
105
106 if let Some(repeat) = self.repeats {
107 write!(f, "/")?;
108
109 if repeat.num_hours() > 0 {
110 write!(f, "{:02}:", repeat.num_hours())?;
111 }
112
113 write!(f, "{:02}", repeat.num_minutes() % 60)?;
114 }
115
116 Ok(())
117 }
118}
119
120#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
123pub enum Time {
124 Fixed(ExtendedTime),
125 Variable(VariableTime),
126}
127
128impl Display for Time {
129 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
130 match self {
131 Self::Fixed(time) => write!(f, "{time}"),
132 Self::Variable(time) => write!(f, "{time}"),
133 }
134 }
135}
136
137#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
140pub struct VariableTime {
141 pub event: TimeEvent,
142 pub offset: i16,
143}
144
145impl VariableTime {
146 pub fn stable_partial_ord(&self, other: &Self) -> Option<Ordering> {
149 match (self.event.cmp(&other.event), self.offset.cmp(&other.offset)) {
150 (Ordering::Equal, other_cmp) | (other_cmp, Ordering::Equal) => Some(other_cmp),
151 (event_cmp, offset_cmp) if event_cmp == offset_cmp => Some(event_cmp),
152 _ => None,
153 }
154 }
155
156 pub fn is_before(&self, other: &Self) -> bool {
158 self.stable_partial_ord(other) == Some(Ordering::Less)
159 }
160}
161
162impl Display for VariableTime {
163 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
164 let Self { event, offset } = self;
165 let offset_h = offset.abs() / 60;
166 let offset_m = offset.abs() % 60;
167
168 match offset.cmp(&0) {
169 Ordering::Less => write!(f, "({event}-{offset_h:02}:{offset_m:02})"),
170 Ordering::Greater => write!(f, "({event}+{offset_h:02}:{offset_m:02})"),
171 Ordering::Equal => write!(f, "{event}"),
172 }
173 }
174}
175
176#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
179pub enum TimeEvent {
180 Dawn,
181 Sunrise,
182 Sunset,
183 Dusk,
184}
185
186impl TimeEvent {
187 pub const fn as_str(&self) -> &'static str {
188 match self {
189 Self::Dawn => "dawn",
190 Self::Sunrise => "sunrise",
191 Self::Sunset => "sunset",
192 Self::Dusk => "dusk",
193 }
194 }
195}
196
197impl Display for TimeEvent {
198 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
199 write!(f, "{}", self.as_str())
200 }
201}