1use chrono::{Duration as ChronoDur, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
2use std::{
3 fmt::{self, Display},
4 hash::{Hash, Hasher},
5};
6
7use crate::ExcelError;
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12pub fn datetime_to_serial(dt: &NaiveDateTime) -> f64 {
21 let mut days = (dt.date() - EXCEL_EPOCH).num_days();
23 if days >= 60 {
24 days += 1;
25 }
26
27 let secs_in_day = dt.time().num_seconds_from_midnight() as f64;
28 days as f64 + secs_in_day / 86_400.0
29}
30
31pub fn serial_to_datetime(serial: f64) -> NaiveDateTime {
32 let days = serial.trunc() as i64;
34 let frac_secs = (serial.fract() * 86_400.0).round() as i64; let base_date = if days < 60 {
39 EXCEL_EPOCH
40 } else {
41 EXCEL_EPOCH + ChronoDur::days(1)
42 };
43
44 let date = base_date + ChronoDur::days(days);
45 let time =
46 NaiveTime::from_num_seconds_from_midnight_opt((frac_secs.rem_euclid(86_400)) as u32, 0)
47 .unwrap();
48 date.and_time(time)
49}
50
51const EXCEL_EPOCH: NaiveDate = NaiveDate::from_ymd_opt(1899, 12, 30).unwrap();
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
54pub enum DateSystem {
55 Excel1900,
56 Excel1904,
57}
58
59impl Display for DateSystem {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 match self {
62 DateSystem::Excel1900 => write!(f, "1900"),
63 DateSystem::Excel1904 => write!(f, "1904"),
64 }
65 }
66}
67
68#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
71#[derive(Debug, Clone, PartialEq)]
72pub enum LiteralValue {
73 Int(i64),
74 Number(f64),
75 Text(String),
76 Boolean(bool),
77 Array(Vec<Vec<LiteralValue>>), Date(chrono::NaiveDate), DateTime(chrono::NaiveDateTime), Time(chrono::NaiveTime), Duration(chrono::Duration), Empty, Pending, Error(ExcelError),
86}
87
88impl Hash for LiteralValue {
89 fn hash<H: Hasher>(&self, state: &mut H) {
90 match self {
91 LiteralValue::Int(i) => i.hash(state),
92 LiteralValue::Number(n) => n.to_bits().hash(state),
93 LiteralValue::Text(s) => s.hash(state),
94 LiteralValue::Boolean(b) => b.hash(state),
95 LiteralValue::Array(a) => a.hash(state),
96 LiteralValue::Date(d) => d.hash(state),
97 LiteralValue::DateTime(dt) => dt.hash(state),
98 LiteralValue::Time(t) => t.hash(state),
99 LiteralValue::Duration(d) => d.hash(state),
100 LiteralValue::Empty => state.write_u8(0),
101 LiteralValue::Pending => state.write_u8(1),
102 LiteralValue::Error(e) => e.hash(state),
103 }
104 }
105}
106
107impl Eq for LiteralValue {}
108
109impl Display for LiteralValue {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 match self {
112 LiteralValue::Int(i) => write!(f, "{i}"),
113 LiteralValue::Number(n) => write!(f, "{n}"),
114 LiteralValue::Text(s) => write!(f, "{s}"),
115 LiteralValue::Boolean(b) => write!(f, "{b}"),
116 LiteralValue::Error(e) => write!(f, "{e}"),
117 LiteralValue::Array(a) => write!(f, "{a:?}"),
118 LiteralValue::Date(d) => write!(f, "{d}"),
119 LiteralValue::DateTime(dt) => write!(f, "{dt}"),
120 LiteralValue::Time(t) => write!(f, "{t}"),
121 LiteralValue::Duration(d) => write!(f, "{d}"),
122 LiteralValue::Empty => write!(f, ""),
123 LiteralValue::Pending => write!(f, "Pending"),
124 }
125 }
126}
127
128#[derive(Debug, Clone, PartialEq)]
129pub enum ValueError {
130 ImplicitIntersection(String),
131}
132
133impl LiteralValue {
134 pub fn coerce_to_single_value(&self) -> Result<LiteralValue, ValueError> {
136 match self {
137 LiteralValue::Array(arr) => {
138 if arr.len() == 1 && arr[0].len() == 1 {
141 Ok(arr[0][0].clone())
142 } else if arr.is_empty() || arr[0].is_empty() {
143 Ok(LiteralValue::Empty) } else {
145 Err(ValueError::ImplicitIntersection(
146 "#LiteralValue! Implicit intersection failed".to_string(),
147 ))
148 }
149 }
150 _ => Ok(self.clone()),
151 }
152 }
153
154 pub fn as_serial_number(&self) -> Option<f64> {
155 match self {
156 LiteralValue::Date(d) => {
157 let dt = d.and_time(NaiveTime::from_hms_opt(0, 0, 0).unwrap());
158 Some(datetime_to_serial(&dt))
159 }
160 LiteralValue::DateTime(dt) => Some(datetime_to_serial(dt)),
161 LiteralValue::Time(t) => Some(t.num_seconds_from_midnight() as f64 / 86_400.0),
162 LiteralValue::Duration(d) => Some(d.num_seconds() as f64 / 86_400.0),
163 LiteralValue::Int(i) => Some(*i as f64),
164 LiteralValue::Number(n) => Some(*n),
165 LiteralValue::Boolean(b) => Some(if *b { 1.0 } else { 0.0 }),
166 _ => None,
167 }
168 }
169
170 pub fn from_serial_number(serial: f64) -> Self {
173 let dt = serial_to_datetime(serial);
174 if dt.time() == NaiveTime::from_hms_opt(0, 0, 0).unwrap() {
175 LiteralValue::Date(dt.date())
176 } else {
177 LiteralValue::DateTime(dt)
178 }
179 }
180
181 pub fn is_truthy(&self) -> bool {
182 match self {
183 LiteralValue::Boolean(b) => *b,
184 LiteralValue::Int(i) => *i != 0,
185 LiteralValue::Number(n) => *n != 0.0,
186 LiteralValue::Text(s) => !s.is_empty(),
187 LiteralValue::Array(arr) => !arr.is_empty(),
188 LiteralValue::Date(_) => true,
189 LiteralValue::DateTime(_) => true,
190 LiteralValue::Time(_) => true,
191 LiteralValue::Duration(_) => true,
192 LiteralValue::Error(_) => false,
193 LiteralValue::Empty => false,
194 LiteralValue::Pending => false,
195 }
196 }
197}