Skip to main content

calamine_styles/
datatype.rs

1// SPDX-License-Identifier: MIT
2//
3// Copyright 2016-2025, Johann Tuffe.
4
5use std::fmt;
6#[cfg(feature = "chrono")]
7use std::sync::OnceLock;
8
9use serde::de::Visitor;
10use serde::Deserialize;
11
12use super::CellErrorType;
13use super::RichText;
14use super::Style;
15
16// Constants used in Excel date calculations.
17const DAY_SECONDS: f64 = 24.0 * 60.0 * 60.;
18const HOUR_SECONDS: u64 = 60 * 60;
19const MINUTE_SECONDS: u64 = 60;
20const YEAR_DAYS: u64 = 365;
21const YEAR_DAYS_4: u64 = YEAR_DAYS * 4 + 1;
22const YEAR_DAYS_100: u64 = YEAR_DAYS * 100 + 25;
23const YEAR_DAYS_400: u64 = YEAR_DAYS * 400 + 97;
24
25#[cfg(feature = "chrono")]
26static EXCEL_EPOCH: OnceLock<chrono::NaiveDateTime> = OnceLock::new();
27
28#[cfg(feature = "chrono")]
29// https://learn.microsoft.com/en-us/office/troubleshoot/excel/1900-and-1904-date-system
30const EXCEL_1900_1904_DIFF: f64 = 1462.;
31
32#[cfg(feature = "chrono")]
33const MS_MULTIPLIER: f64 = 24f64 * 60f64 * 60f64 * 1e+3f64;
34
35/// A struct that combines cell value and style information
36#[derive(Debug, Clone, PartialEq, Default)]
37pub struct CellData {
38    /// The cell value
39    pub value: Data,
40    /// The cell style
41    pub style: Option<Style>,
42}
43
44impl CellData {
45    /// Create a new CellData with just a value
46    pub fn new(value: Data) -> Self {
47        Self { value, style: None }
48    }
49
50    /// Create a new CellData with value and style
51    pub fn with_style(value: Data, style: Style) -> Self {
52        Self {
53            value,
54            style: Some(style),
55        }
56    }
57
58    /// Get the cell value
59    pub fn get_value(&self) -> &Data {
60        &self.value
61    }
62
63    /// Get the cell style
64    pub fn get_style(&self) -> Option<&Style> {
65        self.style.as_ref()
66    }
67
68    /// Check if the cell has style information
69    pub fn has_style(&self) -> bool {
70        self.style.is_some()
71    }
72}
73
74impl From<Data> for CellData {
75    fn from(value: Data) -> Self {
76        Self::new(value)
77    }
78}
79
80impl From<CellData> for Data {
81    fn from(cell_data: CellData) -> Self {
82        cell_data.value
83    }
84}
85
86/// An enum to represent all different data types that can appear as
87/// a value in a worksheet cell
88#[derive(Debug, Clone, PartialEq, Default)]
89pub enum Data {
90    /// Signed integer
91    Int(i64),
92    /// Float
93    Float(f64),
94    /// String
95    String(String),
96    /// Rich text with multiple formatted runs
97    RichText(RichText),
98    /// Boolean
99    Bool(bool),
100    /// Date or Time
101    DateTime(ExcelDateTime),
102    /// Date, Time or Date/Time in ISO 8601
103    DateTimeIso(String),
104    /// Duration in ISO 8601
105    DurationIso(String),
106    /// Error
107    Error(CellErrorType),
108    /// Empty cell
109    #[default]
110    Empty,
111}
112
113/// An enum to represent all different data types that can appear as
114/// a value in a worksheet cell
115impl DataType for Data {
116    fn is_empty(&self) -> bool {
117        *self == Data::Empty
118    }
119    fn is_int(&self) -> bool {
120        matches!(*self, Data::Int(_))
121    }
122    fn is_float(&self) -> bool {
123        matches!(*self, Data::Float(_))
124    }
125    fn is_bool(&self) -> bool {
126        matches!(*self, Data::Bool(_))
127    }
128    fn is_string(&self) -> bool {
129        matches!(*self, Data::String(_) | Data::RichText(_))
130    }
131
132    fn is_rich_text(&self) -> bool {
133        matches!(*self, Data::RichText(_))
134    }
135
136    fn is_duration_iso(&self) -> bool {
137        matches!(*self, Data::DurationIso(_))
138    }
139
140    fn is_datetime(&self) -> bool {
141        matches!(*self, Data::DateTime(_))
142    }
143
144    fn is_datetime_iso(&self) -> bool {
145        matches!(*self, Data::DateTimeIso(_))
146    }
147
148    fn is_error(&self) -> bool {
149        matches!(*self, Data::Error(_))
150    }
151
152    fn get_int(&self) -> Option<i64> {
153        if let Data::Int(v) = self {
154            Some(*v)
155        } else {
156            None
157        }
158    }
159    fn get_float(&self) -> Option<f64> {
160        if let Data::Float(v) = self {
161            Some(*v)
162        } else {
163            None
164        }
165    }
166    fn get_bool(&self) -> Option<bool> {
167        if let Data::Bool(v) = self {
168            Some(*v)
169        } else {
170            None
171        }
172    }
173    fn get_string(&self) -> Option<&str> {
174        if let Data::String(v) = self {
175            Some(&**v)
176        } else {
177            None
178        }
179    }
180
181    fn get_rich_text(&self) -> Option<&RichText> {
182        if let Data::RichText(v) = self {
183            Some(v)
184        } else {
185            None
186        }
187    }
188
189    fn get_datetime(&self) -> Option<ExcelDateTime> {
190        match self {
191            Data::DateTime(v) => Some(*v),
192            _ => None,
193        }
194    }
195
196    fn get_datetime_iso(&self) -> Option<&str> {
197        match self {
198            Data::DateTimeIso(v) => Some(&**v),
199            _ => None,
200        }
201    }
202
203    fn get_duration_iso(&self) -> Option<&str> {
204        match self {
205            Data::DurationIso(v) => Some(&**v),
206            _ => None,
207        }
208    }
209
210    fn get_error(&self) -> Option<&CellErrorType> {
211        match self {
212            Data::Error(e) => Some(e),
213            _ => None,
214        }
215    }
216
217    fn as_string(&self) -> Option<String> {
218        match self {
219            Data::Float(v) => Some(v.to_string()),
220            Data::Int(v) => Some(v.to_string()),
221            Data::String(v) => Some(v.clone()),
222            Data::RichText(v) => Some(v.plain_text()),
223            _ => None,
224        }
225    }
226
227    fn as_i64(&self) -> Option<i64> {
228        match self {
229            Data::Int(v) => Some(*v),
230            Data::Float(v) => Some(*v as i64),
231            Data::Bool(v) => Some(*v as i64),
232            Data::String(v) => atoi_simd::parse::<i64>(v.as_bytes()).ok(),
233            _ => None,
234        }
235    }
236
237    fn as_f64(&self) -> Option<f64> {
238        match self {
239            Data::Int(v) => Some(*v as f64),
240            Data::Float(v) => Some(*v),
241            Data::Bool(v) => Some((*v as i32).into()),
242            Data::String(v) => fast_float2::parse(v).ok(),
243            _ => None,
244        }
245    }
246}
247
248impl PartialEq<&str> for Data {
249    fn eq(&self, other: &&str) -> bool {
250        matches!(self, Data::String(s) if s == other)
251    }
252}
253
254impl PartialEq<str> for Data {
255    fn eq(&self, other: &str) -> bool {
256        matches!(self, Data::String(s) if s == other)
257    }
258}
259
260impl PartialEq<f64> for Data {
261    fn eq(&self, other: &f64) -> bool {
262        matches!(self, Data::Float(s) if *s == *other)
263    }
264}
265
266impl PartialEq<bool> for Data {
267    fn eq(&self, other: &bool) -> bool {
268        matches!(self, Data::Bool(s) if *s == *other)
269    }
270}
271
272impl PartialEq<i64> for Data {
273    fn eq(&self, other: &i64) -> bool {
274        matches!(self, Data::Int(s) if *s == *other)
275    }
276}
277
278impl fmt::Display for Data {
279    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
280        match self {
281            Data::Int(e) => write!(f, "{e}"),
282            Data::Float(e) => write!(f, "{e}"),
283            Data::String(e) => write!(f, "{e}"),
284            Data::RichText(e) => write!(f, "{e}"),
285            Data::Bool(e) => write!(f, "{e}"),
286            Data::DateTime(e) => write!(f, "{e}"),
287            Data::DateTimeIso(e) => write!(f, "{e}"),
288            Data::DurationIso(e) => write!(f, "{e}"),
289            Data::Error(e) => write!(f, "{e}"),
290            Data::Empty => Ok(()),
291        }
292    }
293}
294
295impl<'de> Deserialize<'de> for Data {
296    #[inline]
297    fn deserialize<D>(deserializer: D) -> Result<Data, D::Error>
298    where
299        D: serde::Deserializer<'de>,
300    {
301        struct DataVisitor;
302
303        impl<'de> Visitor<'de> for DataVisitor {
304            type Value = Data;
305
306            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
307                formatter.write_str("any valid JSON value")
308            }
309
310            #[inline]
311            fn visit_bool<E>(self, value: bool) -> Result<Data, E> {
312                Ok(Data::Bool(value))
313            }
314
315            #[inline]
316            fn visit_i64<E>(self, value: i64) -> Result<Data, E> {
317                Ok(Data::Int(value))
318            }
319
320            #[inline]
321            fn visit_u64<E>(self, value: u64) -> Result<Data, E> {
322                Ok(Data::Int(value as i64))
323            }
324
325            #[inline]
326            fn visit_f64<E>(self, value: f64) -> Result<Data, E> {
327                Ok(Data::Float(value))
328            }
329
330            #[inline]
331            fn visit_str<E>(self, value: &str) -> Result<Data, E>
332            where
333                E: serde::de::Error,
334            {
335                self.visit_string(String::from(value))
336            }
337
338            #[inline]
339            fn visit_string<E>(self, value: String) -> Result<Data, E> {
340                Ok(Data::String(value))
341            }
342
343            #[inline]
344            fn visit_none<E>(self) -> Result<Data, E> {
345                Ok(Data::Empty)
346            }
347
348            #[inline]
349            fn visit_some<D>(self, deserializer: D) -> Result<Data, D::Error>
350            where
351                D: serde::Deserializer<'de>,
352            {
353                Deserialize::deserialize(deserializer)
354            }
355
356            #[inline]
357            fn visit_unit<E>(self) -> Result<Data, E> {
358                Ok(Data::Empty)
359            }
360        }
361
362        deserializer.deserialize_any(DataVisitor)
363    }
364}
365
366macro_rules! define_from {
367    ($variant:path, $ty:ty) => {
368        impl From<$ty> for Data {
369            fn from(v: $ty) -> Self {
370                $variant(v)
371            }
372        }
373    };
374}
375
376define_from!(Data::Int, i64);
377define_from!(Data::Float, f64);
378define_from!(Data::String, String);
379define_from!(Data::RichText, RichText);
380define_from!(Data::Bool, bool);
381define_from!(Data::Error, CellErrorType);
382
383impl<'a> From<&'a str> for Data {
384    fn from(v: &'a str) -> Self {
385        Data::String(String::from(v))
386    }
387}
388
389impl From<()> for Data {
390    fn from(_: ()) -> Self {
391        Data::Empty
392    }
393}
394
395impl<T> From<Option<T>> for Data
396where
397    Data: From<T>,
398{
399    fn from(v: Option<T>) -> Self {
400        match v {
401            Some(v) => From::from(v),
402            None => Data::Empty,
403        }
404    }
405}
406
407/// An enum to represent all different data types that can appear as
408/// a value in a worksheet cell
409#[derive(Debug, Clone, PartialEq, Default)]
410pub enum DataRef<'a> {
411    /// Signed integer
412    Int(i64),
413    /// Float
414    Float(f64),
415    /// String
416    String(String),
417    /// Shared String
418    SharedString(&'a str),
419    /// Shared Rich Text (reference to rich text in shared strings table)
420    SharedRichText(&'a RichText),
421    /// Boolean
422    Bool(bool),
423    /// Date or Time
424    DateTime(ExcelDateTime),
425    /// Date, Time or Date/Time in ISO 8601
426    DateTimeIso(String),
427    /// Duration in ISO 8601
428    DurationIso(String),
429    /// Error
430    Error(CellErrorType),
431    /// Empty cell
432    #[default]
433    Empty,
434}
435
436impl DataType for DataRef<'_> {
437    fn is_empty(&self) -> bool {
438        *self == DataRef::Empty
439    }
440
441    fn is_int(&self) -> bool {
442        matches!(*self, DataRef::Int(_))
443    }
444
445    fn is_float(&self) -> bool {
446        matches!(*self, DataRef::Float(_))
447    }
448
449    fn is_bool(&self) -> bool {
450        matches!(*self, DataRef::Bool(_))
451    }
452
453    fn is_string(&self) -> bool {
454        matches!(
455            *self,
456            DataRef::String(_) | DataRef::SharedString(_) | DataRef::SharedRichText(_)
457        )
458    }
459
460    fn is_rich_text(&self) -> bool {
461        matches!(*self, DataRef::SharedRichText(_))
462    }
463
464    fn is_duration_iso(&self) -> bool {
465        matches!(*self, DataRef::DurationIso(_))
466    }
467
468    fn is_datetime(&self) -> bool {
469        matches!(*self, DataRef::DateTime(_))
470    }
471
472    fn is_datetime_iso(&self) -> bool {
473        matches!(*self, DataRef::DateTimeIso(_))
474    }
475
476    fn is_error(&self) -> bool {
477        matches!(*self, DataRef::Error(_))
478    }
479
480    fn get_int(&self) -> Option<i64> {
481        if let DataRef::Int(v) = self {
482            Some(*v)
483        } else {
484            None
485        }
486    }
487
488    fn get_float(&self) -> Option<f64> {
489        if let DataRef::Float(v) = self {
490            Some(*v)
491        } else {
492            None
493        }
494    }
495
496    fn get_bool(&self) -> Option<bool> {
497        if let DataRef::Bool(v) = self {
498            Some(*v)
499        } else {
500            None
501        }
502    }
503
504    fn get_string(&self) -> Option<&str> {
505        match self {
506            DataRef::String(v) => Some(&**v),
507            DataRef::SharedString(v) => Some(v),
508            _ => None,
509        }
510    }
511
512    fn get_rich_text(&self) -> Option<&RichText> {
513        match self {
514            DataRef::SharedRichText(rt) => Some(rt),
515            _ => None,
516        }
517    }
518
519    fn get_datetime(&self) -> Option<ExcelDateTime> {
520        match self {
521            DataRef::DateTime(v) => Some(*v),
522            _ => None,
523        }
524    }
525
526    fn get_datetime_iso(&self) -> Option<&str> {
527        match self {
528            DataRef::DateTimeIso(v) => Some(&**v),
529            _ => None,
530        }
531    }
532
533    fn get_duration_iso(&self) -> Option<&str> {
534        match self {
535            DataRef::DurationIso(v) => Some(&**v),
536            _ => None,
537        }
538    }
539
540    fn get_error(&self) -> Option<&CellErrorType> {
541        match self {
542            DataRef::Error(e) => Some(e),
543            _ => None,
544        }
545    }
546
547    fn as_string(&self) -> Option<String> {
548        match self {
549            DataRef::Float(v) => Some(v.to_string()),
550            DataRef::Int(v) => Some(v.to_string()),
551            DataRef::String(v) => Some(v.clone()),
552            DataRef::SharedString(v) => Some(v.to_string()),
553            DataRef::SharedRichText(v) => Some(v.plain_text()),
554            _ => None,
555        }
556    }
557
558    fn as_i64(&self) -> Option<i64> {
559        match self {
560            DataRef::Int(v) => Some(*v),
561            DataRef::Float(v) => Some(*v as i64),
562            DataRef::Bool(v) => Some(*v as i64),
563            DataRef::String(v) => atoi_simd::parse::<i64>(v.as_bytes()).ok(),
564            DataRef::SharedString(v) => atoi_simd::parse::<i64>(v.as_bytes()).ok(),
565            _ => None,
566        }
567    }
568
569    fn as_f64(&self) -> Option<f64> {
570        match self {
571            DataRef::Int(v) => Some(*v as f64),
572            DataRef::Float(v) => Some(*v),
573            DataRef::Bool(v) => Some((*v as i32).into()),
574            DataRef::String(v) => fast_float2::parse(v).ok(),
575            DataRef::SharedString(v) => fast_float2::parse(v).ok(),
576            _ => None,
577        }
578    }
579}
580
581impl PartialEq<&str> for DataRef<'_> {
582    fn eq(&self, other: &&str) -> bool {
583        matches!(self, DataRef::String(s) if s == other)
584    }
585}
586
587impl PartialEq<str> for DataRef<'_> {
588    fn eq(&self, other: &str) -> bool {
589        matches!(self, DataRef::String(s) if s == other)
590    }
591}
592
593impl PartialEq<f64> for DataRef<'_> {
594    fn eq(&self, other: &f64) -> bool {
595        matches!(self, DataRef::Float(s) if *s == *other)
596    }
597}
598
599impl PartialEq<bool> for DataRef<'_> {
600    fn eq(&self, other: &bool) -> bool {
601        matches!(self, DataRef::Bool(s) if *s == *other)
602    }
603}
604
605impl PartialEq<i64> for DataRef<'_> {
606    fn eq(&self, other: &i64) -> bool {
607        matches!(self, DataRef::Int(s) if *s == *other)
608    }
609}
610
611/// A trait to represent all different data types that can appear as
612/// a value in a worksheet cell
613pub trait DataType {
614    /// Assess if datatype is empty
615    fn is_empty(&self) -> bool;
616
617    /// Assess if datatype is a int
618    fn is_int(&self) -> bool;
619
620    /// Assess if datatype is a float
621    fn is_float(&self) -> bool;
622
623    /// Assess if datatype is a bool
624    fn is_bool(&self) -> bool;
625
626    /// Assess if datatype is a string (includes rich text)
627    fn is_string(&self) -> bool;
628
629    /// Assess if datatype is rich text (string with formatting)
630    fn is_rich_text(&self) -> bool;
631
632    /// Assess if datatype is a `CellErrorType`
633    fn is_error(&self) -> bool;
634
635    /// Assess if datatype is an ISO8601 duration
636    fn is_duration_iso(&self) -> bool;
637
638    /// Assess if datatype is a datetime
639    fn is_datetime(&self) -> bool;
640
641    /// Assess if datatype is an ISO8601 datetime
642    fn is_datetime_iso(&self) -> bool;
643
644    /// Try getting int value
645    fn get_int(&self) -> Option<i64>;
646
647    /// Try getting float value
648    fn get_float(&self) -> Option<f64>;
649
650    /// Try getting bool value
651    fn get_bool(&self) -> Option<bool>;
652
653    /// Try getting string value (plain string only, not rich text)
654    fn get_string(&self) -> Option<&str>;
655
656    /// Try getting rich text value
657    fn get_rich_text(&self) -> Option<&RichText>;
658
659    /// Try getting datetime value
660    fn get_datetime(&self) -> Option<ExcelDateTime>;
661
662    /// Try getting datetime ISO8601 value
663    fn get_datetime_iso(&self) -> Option<&str>;
664
665    /// Try getting duration ISO8601 value
666    fn get_duration_iso(&self) -> Option<&str>;
667
668    /// Try getting Error value
669    fn get_error(&self) -> Option<&CellErrorType>;
670
671    /// Try converting data type into a string (includes rich text as plain text)
672    fn as_string(&self) -> Option<String>;
673
674    /// Try converting data type into an int
675    fn as_i64(&self) -> Option<i64>;
676
677    /// Try converting data type into a float
678    fn as_f64(&self) -> Option<f64>;
679
680    /// Try converting data type into a date
681    #[cfg(feature = "chrono")]
682    #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
683    fn as_date(&self) -> Option<chrono::NaiveDate> {
684        use std::str::FromStr;
685        if self.is_datetime_iso() {
686            self.as_datetime().map(|dt| dt.date()).or_else(|| {
687                self.get_datetime_iso()
688                    .and_then(|s| chrono::NaiveDate::from_str(s).ok())
689            })
690        } else {
691            self.as_datetime().map(|dt| dt.date())
692        }
693    }
694
695    /// Try converting data type into a time
696    #[cfg(feature = "chrono")]
697    #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
698    fn as_time(&self) -> Option<chrono::NaiveTime> {
699        use std::str::FromStr;
700        if self.is_datetime_iso() {
701            self.as_datetime().map(|dt| dt.time()).or_else(|| {
702                self.get_datetime_iso()
703                    .and_then(|s| chrono::NaiveTime::from_str(s).ok())
704            })
705        } else if self.is_duration_iso() {
706            self.get_duration_iso()
707                .and_then(|s| chrono::NaiveTime::parse_from_str(s, "PT%HH%MM%S%.fS").ok())
708        } else {
709            self.as_datetime().map(|dt| dt.time())
710        }
711    }
712
713    /// Try converting data type into a duration
714    #[cfg(feature = "chrono")]
715    #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
716    fn as_duration(&self) -> Option<chrono::Duration> {
717        use chrono::Timelike;
718
719        if self.is_datetime() {
720            self.get_datetime().and_then(|dt| dt.as_duration())
721        } else if self.is_duration_iso() {
722            // need replace in the future to something like chrono::Duration::from_str()
723            // https://github.com/chronotope/chrono/issues/579
724            self.as_time().map(|t| {
725                chrono::Duration::nanoseconds(
726                    t.num_seconds_from_midnight() as i64 * 1_000_000_000 + t.nanosecond() as i64,
727                )
728            })
729        } else {
730            None
731        }
732    }
733
734    // Try converting data type into a datetime.
735    #[cfg(feature = "chrono")]
736    fn as_datetime(&self) -> Option<chrono::NaiveDateTime> {
737        use std::str::FromStr;
738
739        if self.is_int() || self.is_float() {
740            self.as_f64()
741                .map(|f| ExcelDateTime::from_value_only(f).as_datetime())
742        } else if self.is_datetime() {
743            self.get_datetime().map(|d| d.as_datetime())
744        } else if self.is_datetime_iso() {
745            self.get_datetime_iso()
746                .map(|s| chrono::NaiveDateTime::from_str(s).ok())
747        } else {
748            None
749        }
750        .flatten()
751    }
752}
753
754impl<'a> From<DataRef<'a>> for Data {
755    fn from(value: DataRef<'a>) -> Self {
756        match value {
757            DataRef::Int(v) => Data::Int(v),
758            DataRef::Float(v) => Data::Float(v),
759            DataRef::String(v) => Data::String(v),
760            DataRef::SharedString(v) => Data::String(v.into()),
761            DataRef::SharedRichText(v) => Data::RichText(v.clone()),
762            DataRef::Bool(v) => Data::Bool(v),
763            DataRef::DateTime(v) => Data::DateTime(v),
764            DataRef::DateTimeIso(v) => Data::DateTimeIso(v),
765            DataRef::DurationIso(v) => Data::DurationIso(v),
766            DataRef::Error(v) => Data::Error(v),
767            DataRef::Empty => Data::Empty,
768        }
769    }
770}
771
772/// Excel datetime type. Possible: date, time, datetime, duration.
773/// At this time we can only determine datetime (date and time are datetime too) and duration.
774#[derive(Debug, Clone, Copy, PartialEq)]
775pub enum ExcelDateTimeType {
776    /// `DateTime`
777    DateTime,
778    /// `TimeDelta` (Duration)
779    TimeDelta,
780}
781
782/// Structure for Excel date and time representation.
783#[derive(Debug, Clone, Copy, PartialEq)]
784pub struct ExcelDateTime {
785    value: f64,
786    datetime_type: ExcelDateTimeType,
787    is_1904: bool,
788}
789
790impl ExcelDateTime {
791    /// Creates a new `ExcelDateTime`
792    pub fn new(value: f64, datetime_type: ExcelDateTimeType, is_1904: bool) -> Self {
793        ExcelDateTime {
794            value,
795            datetime_type,
796            is_1904,
797        }
798    }
799
800    // Is used only for converting excel value to chrono.
801    #[cfg(feature = "chrono")]
802    fn from_value_only(value: f64) -> Self {
803        ExcelDateTime {
804            value,
805            ..Default::default()
806        }
807    }
808
809    /// True if excel datetime has duration format (`[hh]:mm:ss`, for example)
810    pub fn is_duration(&self) -> bool {
811        matches!(self.datetime_type, ExcelDateTimeType::TimeDelta)
812    }
813
814    /// True if excel datetime has datetime format (not duration)
815    pub fn is_datetime(&self) -> bool {
816        matches!(self.datetime_type, ExcelDateTimeType::DateTime)
817    }
818
819    /// Converting data type into a float
820    pub fn as_f64(&self) -> f64 {
821        self.value
822    }
823
824    /// Convert an Excel serial datetime to standard date components.
825    ///
826    /// Datetimes in Excel are serial dates with days counted from an epoch
827    /// (usually 1900-01-01) and where the time is a percentage/decimal of the
828    /// milliseconds in the day. Both the date and time are stored in the same
829    /// f64 value. For example, 2025/10/13 12:00:00 is stored as 45943.5.
830    ///
831    /// This function returns a tuple of (year, month, day, hour, minutes,
832    /// seconds, milliseconds). It works for serial dates in both the 1900 and
833    /// 1904 epochs.
834    ///
835    /// This function always returns a date, even if the serial value is outside
836    /// of Excel's range of `0.0 <= datetime < 10000.0`. It also returns, as
837    /// Excel does, the invalid date 1900/02/29 due to the [Excel 1900 leap year
838    /// bug](https://en.wikipedia.org/wiki/Leap_year_problem#Occurrences).
839    ///
840    /// Excel only supports millisecond precision and it also doesn't use or
841    /// encode timezone information in any way.
842    ///
843    /// # Examples
844    ///
845    /// An example of converting an Excel date/time to standard components.
846    ///
847    /// ```
848    /// use calamine::{ExcelDateTime, ExcelDateTimeType};
849    ///
850    /// // Create an Excel datetime from the serial value 45943.541 which is
851    /// // equivalent to the date "2025/10/13 12:59:02.400".
852    /// let excel_datetime = ExcelDateTime::new(
853    ///     45943.541,
854    ///     ExcelDateTimeType::DateTime,
855    ///     false, // Using 1900 epoch (not 1904).
856    /// );
857    ///
858    /// // Convert to standard date/time components.
859    /// let (year, month, day, hour, min, sec, milli) = excel_datetime.to_ymd_hms_milli();
860    ///
861    /// assert_eq!(year, 2025);
862    /// assert_eq!(month, 10);
863    /// assert_eq!(day, 13);
864    /// assert_eq!(hour, 12);
865    /// assert_eq!(min, 59);
866    /// assert_eq!(sec, 2);
867    /// assert_eq!(milli, 400);
868    /// ```
869    ///
870    pub fn to_ymd_hms_milli(&self) -> (u16, u8, u8, u8, u8, u8, u16) {
871        Self::excel_to_standard_datetime(self.value, self.is_1904)
872    }
873
874    /// Try converting data type into a duration.
875    #[cfg(feature = "chrono")]
876    #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
877    pub fn as_duration(&self) -> Option<chrono::Duration> {
878        let ms = self.value * MS_MULTIPLIER;
879        Some(chrono::Duration::milliseconds(ms.round() as i64))
880    }
881
882    /// Try converting data type into a datetime.
883    #[cfg(feature = "chrono")]
884    #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
885    pub fn as_datetime(&self) -> Option<chrono::NaiveDateTime> {
886        let excel_epoch = EXCEL_EPOCH.get_or_init(|| {
887            chrono::NaiveDate::from_ymd_opt(1899, 12, 30)
888                .unwrap()
889                .and_time(chrono::NaiveTime::MIN)
890        });
891        let f = if self.is_1904 {
892            self.value + EXCEL_1900_1904_DIFF
893        } else {
894            self.value
895        };
896        let f = if f >= 60.0 { f } else { f + 1.0 };
897        let ms = f * MS_MULTIPLIER;
898        let excel_duration = chrono::Duration::milliseconds(ms.round() as i64);
899        excel_epoch.checked_add_signed(excel_duration)
900    }
901
902    // Convert an Excel serial datetime to its date components.
903    //
904    // Datetimes in Excel are serial dates with days counted from an epoch and
905    // where the time is a percentage/decimal of the milliseconds in the day.
906    // Both the date and time are stored in the same f64 value.
907    //
908    // The calculation back to standard date and time components is deceptively
909    // tricky since simple division doesn't work due to the 4/100/400 year leap
910    // day changes. The basic approach is to divide the range into 400 year
911    // blocks, 100 year blocks, 4 year blocks and 1 year blocks to calculate the
912    // year (relative to the epoch). The remaining days and seconds are used to
913    // calculate the year day and time. To make the leap year calculations
914    // easier we move the effective epoch back to 1600-01-01 which is the
915    // closest 400 year epoch before 1900/1904.
916    //
917    // In addition we need to handle both a 1900 and 1904 epoch and we need to
918    // account for the Excel date bug where it treats 1900 as a leap year.
919    //
920    // Works in the range 1899-12-31/1904-01-01 to 9999-12-31.
921    //
922    // Leap seconds and the timezone aren't taken into account since Excel
923    // doesn't handle them.
924    //
925    fn excel_to_standard_datetime(
926        excel_datetime: f64,
927        is_1904: bool,
928    ) -> (u16, u8, u8, u8, u8, u8, u16) {
929        let mut months = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
930
931        // Convert the seconds to a whole number of days.
932        let mut days = excel_datetime.floor() as u64;
933
934        // Move the epoch to 1600-01-01 to make the leap calculations easier.
935        if is_1904 {
936            // 1904 epoch dates.
937            days += 111_033;
938        } else if days > YEAR_DAYS {
939            // 1900 epoch years other than 1900.
940            days += 109_571;
941        } else {
942            // Adjust for the Excel 1900 leap year bug.
943            days += 109_572;
944        }
945
946        // Get the number of 400 year blocks.
947        let year_days_400 = days / YEAR_DAYS_400;
948        let mut days = days % YEAR_DAYS_400;
949
950        // Get the number of 100 year blocks. There are 2 kinds: those starting
951        // from a %400 year with an extra leap day (36,525 days) and those
952        // starting from other 100 year intervals with 1 day less (36,524 days).
953        let year_days_100;
954        if days < YEAR_DAYS_100 {
955            year_days_100 = days / YEAR_DAYS_100;
956            days %= YEAR_DAYS_100;
957        } else {
958            year_days_100 = 1 + (days - YEAR_DAYS_100) / (YEAR_DAYS_100 - 1);
959            days = (days - YEAR_DAYS_100) % (YEAR_DAYS_100 - 1);
960        }
961
962        // Get the number of 4 year blocks. There are 2 kinds: a 4 year block
963        // with a leap day (1461 days) and a 4 year block starting from non-leap
964        // %100 years without a leap day (1460 days). We also need to account
965        // for whether a 1461 day block was preceded by a 1460 day block at the
966        // start of the 100 year block.
967        let year_days_4;
968        let mut non_leap_year_block = false;
969        if year_days_100 == 0 {
970            // Any 4 year block in a 36,525 day 100 year block. Has extra leap.
971            year_days_4 = days / YEAR_DAYS_4;
972            days %= YEAR_DAYS_4;
973        } else if days < YEAR_DAYS_4 {
974            // A 4 year block at the start of a 36,524 day 100 year block.
975            year_days_4 = days / (YEAR_DAYS_4 - 1);
976            days %= YEAR_DAYS_4 - 1;
977            non_leap_year_block = true;
978        } else {
979            // A non-initial 4 year block in a 36,524 day 100 year block.
980            year_days_4 = 1 + (days - (YEAR_DAYS_4 - 1)) / YEAR_DAYS_4;
981            days = (days - (YEAR_DAYS_4 - 1)) % YEAR_DAYS_4;
982        }
983
984        // Get the number of 1 year blocks. We need to account for leap years
985        // and non-leap years and whether the non-leap occurs after a leap year.
986        let year_days_1;
987        if non_leap_year_block {
988            // A non-leap block not preceded by a leap block.
989            year_days_1 = days / YEAR_DAYS;
990            days %= YEAR_DAYS;
991        } else if days < YEAR_DAYS + 1 {
992            // A leap year block.
993            year_days_1 = days / (YEAR_DAYS + 1);
994            days %= YEAR_DAYS + 1;
995        } else {
996            // A non-leap block preceded by a leap block.
997            year_days_1 = 1 + (days - (YEAR_DAYS + 1)) / YEAR_DAYS;
998            days = (days - (YEAR_DAYS + 1)) % YEAR_DAYS;
999        }
1000
1001        // Calculate the year as the number of blocks*days since the epoch.
1002        let year = 1600 + year_days_400 * 400 + year_days_100 * 100 + year_days_4 * 4 + year_days_1;
1003
1004        // Convert from 0 indexed to 1 indexed days.
1005        days += 1;
1006
1007        // Adjust February day count for leap years.
1008        if Self::is_leap_year(year) {
1009            months[1] = 29;
1010        }
1011
1012        // Handle edge cases due to Excel erroneously treating 1900 as a leap year.
1013        if !is_1904 && year == 1900 {
1014            months[1] = 29;
1015
1016            // Adjust last day of 1900.
1017            if excel_datetime.trunc() == 366.0 {
1018                days += 1;
1019            }
1020        }
1021
1022        // Calculate the relevant month based on the sequential number of days.
1023        let mut month = 1;
1024        for month_days in months {
1025            if days > month_days {
1026                days -= month_days;
1027                month += 1;
1028            } else {
1029                break;
1030            }
1031        }
1032
1033        // The final remainder is the day of the month.
1034        let day = days;
1035
1036        // Get the time part of the Excel datetime.
1037        let time = excel_datetime.fract();
1038        let milli = ((time * DAY_SECONDS).fract() * 1000.0).round() as u64;
1039        let day_as_seconds = (time * DAY_SECONDS) as u64;
1040
1041        // Calculate the hours, minutes and seconds in the day.
1042        let hour = day_as_seconds / HOUR_SECONDS;
1043        let min = (day_as_seconds - hour * HOUR_SECONDS) / MINUTE_SECONDS;
1044        let sec = (day_as_seconds - hour * HOUR_SECONDS - min * MINUTE_SECONDS) % MINUTE_SECONDS;
1045
1046        // Return the date and time components.
1047        (
1048            year as u16,
1049            month as u8,
1050            day as u8,
1051            hour as u8,
1052            min as u8,
1053            sec as u8,
1054            milli as u16,
1055        )
1056    }
1057
1058    // Check if a year is a leap year.
1059    fn is_leap_year(year: u64) -> bool {
1060        year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
1061    }
1062}
1063
1064impl Default for ExcelDateTime {
1065    fn default() -> Self {
1066        ExcelDateTime {
1067            value: 0.,
1068            datetime_type: ExcelDateTimeType::DateTime,
1069            is_1904: false,
1070        }
1071    }
1072}
1073
1074impl fmt::Display for ExcelDateTime {
1075    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
1076        write!(f, "{}", self.value)
1077    }
1078}
1079
1080#[cfg(all(test, feature = "chrono"))]
1081mod date_tests {
1082    use super::*;
1083
1084    #[test]
1085    fn test_dates() {
1086        use chrono::{Duration, NaiveDate, NaiveDateTime, NaiveTime};
1087
1088        #[allow(clippy::excessive_precision)]
1089        let unix_epoch = Data::Float(25569.);
1090        assert_eq!(
1091            unix_epoch.as_datetime(),
1092            Some(NaiveDateTime::new(
1093                NaiveDate::from_ymd_opt(1970, 1, 1).unwrap(),
1094                NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
1095            ))
1096        );
1097
1098        // test for https://github.com/tafia/calamine/issues/251
1099        let unix_epoch_precision = Data::Float(44484.7916666667);
1100        assert_eq!(
1101            unix_epoch_precision.as_datetime(),
1102            Some(NaiveDateTime::new(
1103                NaiveDate::from_ymd_opt(2021, 10, 15).unwrap(),
1104                NaiveTime::from_hms_opt(19, 0, 0).unwrap(),
1105            ))
1106        );
1107
1108        // test rounding
1109        #[allow(clippy::excessive_precision)]
1110        let date = Data::Float(0.18737500000000001);
1111        assert_eq!(
1112            date.as_time(),
1113            Some(NaiveTime::from_hms_milli_opt(4, 29, 49, 200).unwrap())
1114        );
1115
1116        #[allow(clippy::excessive_precision)]
1117        let date = Data::Float(0.25951736111111101);
1118        assert_eq!(
1119            date.as_time(),
1120            Some(NaiveTime::from_hms_milli_opt(6, 13, 42, 300).unwrap())
1121        );
1122
1123        // test overflow
1124        assert_eq!(Data::Float(1e20).as_time(), None);
1125
1126        #[allow(clippy::excessive_precision)]
1127        let unix_epoch_15h30m = Data::Float(25569.645833333333333);
1128        let chrono_dt = NaiveDateTime::new(
1129            NaiveDate::from_ymd_opt(1970, 1, 1).unwrap(),
1130            NaiveTime::from_hms_opt(15, 30, 0).unwrap(),
1131        );
1132        let micro = Duration::microseconds(1);
1133        assert!(unix_epoch_15h30m.as_datetime().unwrap() - chrono_dt < micro);
1134    }
1135
1136    #[test]
1137    fn test_int_dates() {
1138        use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
1139
1140        let unix_epoch = Data::Int(25569);
1141        assert_eq!(
1142            unix_epoch.as_datetime(),
1143            Some(NaiveDateTime::new(
1144                NaiveDate::from_ymd_opt(1970, 1, 1).unwrap(),
1145                NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
1146            ))
1147        );
1148
1149        let time = Data::Int(44060);
1150        assert_eq!(
1151            time.as_datetime(),
1152            Some(NaiveDateTime::new(
1153                NaiveDate::from_ymd_opt(2020, 8, 17).unwrap(),
1154                NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
1155            ))
1156        );
1157    }
1158}
1159
1160/// An enum to represent all different data types that can appear as
1161/// a value in a worksheet cell
1162impl DataType for CellData {
1163    fn is_empty(&self) -> bool {
1164        self.value.is_empty()
1165    }
1166    fn is_int(&self) -> bool {
1167        self.value.is_int()
1168    }
1169    fn is_float(&self) -> bool {
1170        self.value.is_float()
1171    }
1172    fn is_bool(&self) -> bool {
1173        self.value.is_bool()
1174    }
1175    fn is_string(&self) -> bool {
1176        self.value.is_string()
1177    }
1178
1179    fn is_rich_text(&self) -> bool {
1180        self.value.is_rich_text()
1181    }
1182
1183    fn is_duration_iso(&self) -> bool {
1184        self.value.is_duration_iso()
1185    }
1186
1187    fn is_datetime(&self) -> bool {
1188        self.value.is_datetime()
1189    }
1190
1191    fn is_datetime_iso(&self) -> bool {
1192        self.value.is_datetime_iso()
1193    }
1194
1195    fn is_error(&self) -> bool {
1196        self.value.is_error()
1197    }
1198
1199    fn get_int(&self) -> Option<i64> {
1200        self.value.get_int()
1201    }
1202    fn get_float(&self) -> Option<f64> {
1203        self.value.get_float()
1204    }
1205    fn get_bool(&self) -> Option<bool> {
1206        self.value.get_bool()
1207    }
1208    fn get_string(&self) -> Option<&str> {
1209        self.value.get_string()
1210    }
1211
1212    fn get_rich_text(&self) -> Option<&RichText> {
1213        self.value.get_rich_text()
1214    }
1215
1216    fn get_datetime(&self) -> Option<ExcelDateTime> {
1217        self.value.get_datetime()
1218    }
1219
1220    fn get_datetime_iso(&self) -> Option<&str> {
1221        self.value.get_datetime_iso()
1222    }
1223
1224    fn get_duration_iso(&self) -> Option<&str> {
1225        self.value.get_duration_iso()
1226    }
1227
1228    fn get_error(&self) -> Option<&CellErrorType> {
1229        self.value.get_error()
1230    }
1231
1232    fn as_string(&self) -> Option<String> {
1233        self.value.as_string()
1234    }
1235
1236    fn as_i64(&self) -> Option<i64> {
1237        self.value.as_i64()
1238    }
1239
1240    fn as_f64(&self) -> Option<f64> {
1241        self.value.as_f64()
1242    }
1243}
1244
1245#[cfg(test)]
1246mod tests {
1247    use super::*;
1248
1249    #[test]
1250    fn test_partial_eq() {
1251        assert_eq!(Data::String("value".to_string()), "value");
1252        assert_eq!(Data::String("value".to_string()), "value"[..]);
1253        assert_eq!(Data::Float(100.0), 100.0f64);
1254        assert_eq!(Data::Bool(true), true);
1255        assert_eq!(Data::Int(100), 100i64);
1256    }
1257
1258    #[test]
1259    fn test_as_i64_with_bools() {
1260        assert_eq!(Data::Bool(true).as_i64(), Some(1));
1261        assert_eq!(Data::Bool(false).as_i64(), Some(0));
1262        assert_eq!(DataRef::Bool(true).as_i64(), Some(1));
1263        assert_eq!(DataRef::Bool(false).as_i64(), Some(0));
1264    }
1265
1266    #[test]
1267    fn test_as_f64_with_bools() {
1268        assert_eq!(Data::Bool(true).as_f64(), Some(1.0));
1269        assert_eq!(Data::Bool(false).as_f64(), Some(0.0));
1270        assert_eq!(DataRef::Bool(true).as_f64(), Some(1.0));
1271        assert_eq!(DataRef::Bool(false).as_f64(), Some(0.0));
1272    }
1273
1274    #[test]
1275    fn test_datetimes_1900_epoch() {
1276        #[allow(clippy::excessive_precision)]
1277        let test_data = vec![
1278            (0.0, (1899, 12, 31, 0, 0, 0, 0)),
1279            (30188.010650613425, (1982, 8, 25, 0, 15, 20, 213)),
1280            (60376.011670023145, (2065, 4, 19, 0, 16, 48, 290)),
1281            (90565.038488958337, (2147, 12, 15, 0, 55, 25, 446)),
1282            (120753.04359827546, (2230, 8, 10, 1, 2, 46, 891)),
1283            (150942.04462496529, (2313, 4, 6, 1, 4, 15, 597)),
1284            (181130.04838991899, (2395, 11, 30, 1, 9, 40, 889)),
1285            (211318.04968240741, (2478, 7, 25, 1, 11, 32, 560)),
1286            (241507.06272186342, (2561, 3, 21, 1, 30, 19, 169)),
1287            (271695.07529606484, (2643, 11, 15, 1, 48, 25, 580)),
1288            (301884.08578609955, (2726, 7, 12, 2, 3, 31, 919)),
1289            (332072.09111094906, (2809, 3, 6, 2, 11, 11, 986)),
1290            (362261.10042934027, (2891, 10, 31, 2, 24, 37, 95)),
1291            (392449.10772245371, (2974, 6, 26, 2, 35, 7, 220)),
1292            (422637.11472348380, (3057, 2, 19, 2, 45, 12, 109)),
1293            (452826.12962951389, (3139, 10, 17, 3, 6, 39, 990)),
1294            (483014.13065105322, (3222, 6, 11, 3, 8, 8, 251)),
1295            (513203.13834000000, (3305, 2, 5, 3, 19, 12, 576)),
1296            (543391.14563164348, (3387, 10, 1, 3, 29, 42, 574)),
1297            (573579.15105107636, (3470, 5, 27, 3, 37, 30, 813)),
1298            (603768.17683137732, (3553, 1, 21, 4, 14, 38, 231)),
1299            (633956.17810832174, (3635, 9, 16, 4, 16, 28, 559)),
1300            (664145.17914608796, (3718, 5, 13, 4, 17, 58, 222)),
1301            (694333.18173372687, (3801, 1, 6, 4, 21, 41, 794)),
1302            (724522.20596981479, (3883, 9, 2, 4, 56, 35, 792)),
1303            (754710.22586672450, (3966, 4, 28, 5, 25, 14, 885)),
1304            (784898.22645513888, (4048, 12, 21, 5, 26, 5, 724)),
1305            (815087.24078782403, (4131, 8, 18, 5, 46, 44, 68)),
1306            (845275.24167987274, (4214, 4, 13, 5, 48, 1, 141)),
1307            (875464.24574438657, (4296, 12, 7, 5, 53, 52, 315)),
1308            (905652.26028449077, (4379, 8, 3, 6, 14, 48, 580)),
1309            (935840.28212659725, (4462, 3, 28, 6, 46, 15, 738)),
1310            (966029.31343063654, (4544, 11, 22, 7, 31, 20, 407)),
1311            (996217.33233511576, (4627, 7, 19, 7, 58, 33, 754)),
1312            (1026406.3386936343, (4710, 3, 15, 8, 7, 43, 130)),
1313            (1056594.3536005903, (4792, 11, 7, 8, 29, 11, 91)),
1314            (1086783.3807329629, (4875, 7, 4, 9, 8, 15, 328)),
1315            (1116971.3963169097, (4958, 2, 27, 9, 30, 41, 781)),
1316            (1147159.3986627546, (5040, 10, 23, 9, 34, 4, 462)),
1317            (1177348.4009715857, (5123, 6, 20, 9, 37, 23, 945)),
1318            (1207536.4013501736, (5206, 2, 12, 9, 37, 56, 655)),
1319            (1237725.4063915510, (5288, 10, 8, 9, 45, 12, 230)),
1320            (1267913.4126710880, (5371, 6, 4, 9, 54, 14, 782)),
1321            (1298101.4127558796, (5454, 1, 28, 9, 54, 22, 108)),
1322            (1328290.4177795255, (5536, 9, 24, 10, 1, 36, 151)),
1323            (1358478.5068125231, (5619, 5, 20, 12, 9, 48, 602)),
1324            (1388667.5237100578, (5702, 1, 14, 12, 34, 8, 549)),
1325            (1418855.5389640625, (5784, 9, 8, 12, 56, 6, 495)),
1326            (1449044.5409515856, (5867, 5, 6, 12, 58, 58, 217)),
1327            (1479232.5416002662, (5949, 12, 30, 12, 59, 54, 263)),
1328            (1509420.5657561459, (6032, 8, 24, 13, 34, 41, 331)),
1329            (1539609.5822754744, (6115, 4, 21, 13, 58, 28, 601)),
1330            (1569797.5849178126, (6197, 12, 14, 14, 2, 16, 899)),
1331            (1599986.6085352316, (6280, 8, 10, 14, 36, 17, 444)),
1332            (1630174.6096927200, (6363, 4, 6, 14, 37, 57, 451)),
1333            (1660363.6234115392, (6445, 11, 30, 14, 57, 42, 757)),
1334            (1690551.6325035533, (6528, 7, 26, 15, 10, 48, 307)),
1335            (1720739.6351839120, (6611, 3, 22, 15, 14, 39, 890)),
1336            (1750928.6387498612, (6693, 11, 15, 15, 19, 47, 988)),
1337            (1781116.6697262037, (6776, 7, 11, 16, 4, 24, 344)),
1338            (1811305.6822216667, (6859, 3, 7, 16, 22, 23, 952)),
1339            (1841493.6874536921, (6941, 10, 31, 16, 29, 55, 999)),
1340            (1871681.7071789235, (7024, 6, 26, 16, 58, 20, 259)),
1341            (1901870.7111390624, (7107, 2, 21, 17, 4, 2, 415)),
1342            (1932058.7211762732, (7189, 10, 16, 17, 18, 29, 630)),
1343            (1962247.7412190163, (7272, 6, 11, 17, 47, 21, 323)),
1344            (1992435.7454845603, (7355, 2, 5, 17, 53, 29, 866)),
1345            (2022624.7456143056, (7437, 10, 2, 17, 53, 41, 76)),
1346            (2052812.7465977315, (7520, 5, 28, 17, 55, 6, 44)),
1347            (2083000.7602910995, (7603, 1, 21, 18, 14, 49, 151)),
1348            (2113189.7623349307, (7685, 9, 16, 18, 17, 45, 738)),
1349            (2143377.7708298611, (7768, 5, 12, 18, 29, 59, 700)),
1350            (2173566.7731624190, (7851, 1, 7, 18, 33, 21, 233)),
1351            (2203754.8016744559, (7933, 9, 2, 19, 14, 24, 673)),
1352            (2233942.8036205554, (8016, 4, 27, 19, 17, 12, 816)),
1353            (2264131.8080603937, (8098, 12, 22, 19, 23, 36, 418)),
1354            (2294319.8239109721, (8181, 8, 17, 19, 46, 25, 908)),
1355            (2324508.8387420601, (8264, 4, 13, 20, 7, 47, 314)),
1356            (2354696.8552963310, (8346, 12, 8, 20, 31, 37, 603)),
1357            (2384885.8610853008, (8429, 8, 3, 20, 39, 57, 770)),
1358            (2415073.8682530904, (8512, 3, 29, 20, 50, 17, 67)),
1359            (2445261.8770581828, (8594, 11, 22, 21, 2, 57, 827)),
1360            (2475450.8910360998, (8677, 7, 19, 21, 23, 5, 519)),
1361            (2505638.8991848612, (8760, 3, 14, 21, 34, 49, 572)),
1362            (2535827.9021521294, (8842, 11, 8, 21, 39, 5, 944)),
1363            (2566015.9022965971, (8925, 7, 4, 21, 39, 18, 426)),
1364            (2596203.9070343636, (9008, 2, 28, 21, 46, 7, 769)),
1365            (2626392.9152275696, (9090, 10, 24, 21, 57, 55, 662)),
1366            (2656580.9299968979, (9173, 6, 19, 22, 19, 11, 732)),
1367            (2686769.9332335186, (9256, 2, 13, 22, 23, 51, 376)),
1368            (2716957.9360968866, (9338, 10, 9, 22, 27, 58, 771)),
1369            (2747146.9468795368, (9421, 6, 5, 22, 43, 30, 392)),
1370            (2777334.9502990046, (9504, 1, 30, 22, 48, 25, 834)),
1371            (2807522.9540709145, (9586, 9, 24, 22, 53, 51, 727)),
1372            (2837711.9673210187, (9669, 5, 20, 23, 12, 56, 536)),
1373            (2867899.9693762613, (9752, 1, 14, 23, 15, 54, 109)),
1374            (2898088.9702850925, (9834, 9, 10, 23, 17, 12, 632)),
1375            (2958465.9999884260, (9999, 12, 31, 23, 59, 59, 0)),
1376        ];
1377
1378        for test in test_data {
1379            let (excel_serial_datetime, expected) = test;
1380            let datetime =
1381                ExcelDateTime::new(excel_serial_datetime, ExcelDateTimeType::DateTime, false);
1382            let got = datetime.to_ymd_hms_milli();
1383
1384            assert_eq!(expected, got);
1385        }
1386    }
1387
1388    #[test]
1389    fn test_dates_only_1900_epoch() {
1390        let test_data = vec![
1391            (0.0, (1899, 12, 31)),
1392            (1.0, (1900, 1, 1)),
1393            (58.0, (1900, 2, 27)),
1394            (59.0, (1900, 2, 28)),
1395            (60.0, (1900, 2, 29)),
1396            (61.0, (1900, 3, 1)),
1397            (62.0, (1900, 3, 2)),
1398            (71.0, (1900, 3, 11)),
1399            (99.0, (1900, 4, 8)),
1400            (256.0, (1900, 9, 12)),
1401            (364.0, (1900, 12, 29)),
1402            (365.0, (1900, 12, 30)),
1403            (366.0, (1900, 12, 31)),
1404            (367.0, (1901, 1, 1)),
1405            (489.0, (1901, 5, 3)),
1406            (652.0, (1901, 10, 13)),
1407            (777.0, (1902, 2, 15)),
1408            (888.0, (1902, 6, 6)),
1409            (999.0, (1902, 9, 25)),
1410            (1001.0, (1902, 9, 27)),
1411            (1212.0, (1903, 4, 26)),
1412            (1313.0, (1903, 8, 5)),
1413            (1461.0, (1903, 12, 31)),
1414            (1462.0, (1904, 1, 1)),
1415            (1520.0, (1904, 2, 28)),
1416            (1521.0, (1904, 2, 29)),
1417            (1522.0, (1904, 3, 1)),
1418            (2615.0, (1907, 2, 27)),
1419            (2616.0, (1907, 2, 28)),
1420            (2617.0, (1907, 3, 1)),
1421            (2618.0, (1907, 3, 2)),
1422            (2619.0, (1907, 3, 3)),
1423            (2620.0, (1907, 3, 4)),
1424            (2621.0, (1907, 3, 5)),
1425            (2622.0, (1907, 3, 6)),
1426            (36161.0, (1999, 1, 1)),
1427            (36191.0, (1999, 1, 31)),
1428            (36192.0, (1999, 2, 1)),
1429            (36219.0, (1999, 2, 28)),
1430            (36220.0, (1999, 3, 1)),
1431            (36250.0, (1999, 3, 31)),
1432            (36251.0, (1999, 4, 1)),
1433            (36280.0, (1999, 4, 30)),
1434            (36281.0, (1999, 5, 1)),
1435            (36311.0, (1999, 5, 31)),
1436            (36312.0, (1999, 6, 1)),
1437            (36341.0, (1999, 6, 30)),
1438            (36342.0, (1999, 7, 1)),
1439            (36372.0, (1999, 7, 31)),
1440            (36373.0, (1999, 8, 1)),
1441            (36403.0, (1999, 8, 31)),
1442            (36404.0, (1999, 9, 1)),
1443            (36433.0, (1999, 9, 30)),
1444            (36434.0, (1999, 10, 1)),
1445            (36464.0, (1999, 10, 31)),
1446            (36465.0, (1999, 11, 1)),
1447            (36494.0, (1999, 11, 30)),
1448            (36495.0, (1999, 12, 1)),
1449            (36525.0, (1999, 12, 31)),
1450            (36526.0, (2000, 1, 1)),
1451            (36556.0, (2000, 1, 31)),
1452            (36557.0, (2000, 2, 1)),
1453            (36585.0, (2000, 2, 29)),
1454            (36586.0, (2000, 3, 1)),
1455            (36616.0, (2000, 3, 31)),
1456            (36617.0, (2000, 4, 1)),
1457            (36646.0, (2000, 4, 30)),
1458            (36647.0, (2000, 5, 1)),
1459            (36677.0, (2000, 5, 31)),
1460            (36678.0, (2000, 6, 1)),
1461            (36707.0, (2000, 6, 30)),
1462            (36708.0, (2000, 7, 1)),
1463            (36738.0, (2000, 7, 31)),
1464            (36739.0, (2000, 8, 1)),
1465            (36769.0, (2000, 8, 31)),
1466            (36770.0, (2000, 9, 1)),
1467            (36799.0, (2000, 9, 30)),
1468            (36800.0, (2000, 10, 1)),
1469            (36830.0, (2000, 10, 31)),
1470            (36831.0, (2000, 11, 1)),
1471            (36860.0, (2000, 11, 30)),
1472            (36861.0, (2000, 12, 1)),
1473            (36891.0, (2000, 12, 31)),
1474            (36892.0, (2001, 1, 1)),
1475            (36922.0, (2001, 1, 31)),
1476            (36923.0, (2001, 2, 1)),
1477            (36950.0, (2001, 2, 28)),
1478            (36951.0, (2001, 3, 1)),
1479            (36981.0, (2001, 3, 31)),
1480            (36982.0, (2001, 4, 1)),
1481            (37011.0, (2001, 4, 30)),
1482            (37012.0, (2001, 5, 1)),
1483            (37042.0, (2001, 5, 31)),
1484            (37043.0, (2001, 6, 1)),
1485            (37072.0, (2001, 6, 30)),
1486            (37073.0, (2001, 7, 1)),
1487            (37103.0, (2001, 7, 31)),
1488            (37104.0, (2001, 8, 1)),
1489            (37134.0, (2001, 8, 31)),
1490            (37135.0, (2001, 9, 1)),
1491            (37164.0, (2001, 9, 30)),
1492            (37165.0, (2001, 10, 1)),
1493            (37195.0, (2001, 10, 31)),
1494            (37196.0, (2001, 11, 1)),
1495            (37225.0, (2001, 11, 30)),
1496            (37226.0, (2001, 12, 1)),
1497            (37256.0, (2001, 12, 31)),
1498            (182623.0, (2400, 1, 1)),
1499            (182653.0, (2400, 1, 31)),
1500            (182654.0, (2400, 2, 1)),
1501            (182682.0, (2400, 2, 29)),
1502            (182683.0, (2400, 3, 1)),
1503            (182713.0, (2400, 3, 31)),
1504            (182714.0, (2400, 4, 1)),
1505            (182743.0, (2400, 4, 30)),
1506            (182744.0, (2400, 5, 1)),
1507            (182774.0, (2400, 5, 31)),
1508            (182775.0, (2400, 6, 1)),
1509            (182804.0, (2400, 6, 30)),
1510            (182805.0, (2400, 7, 1)),
1511            (182835.0, (2400, 7, 31)),
1512            (182836.0, (2400, 8, 1)),
1513            (182866.0, (2400, 8, 31)),
1514            (182867.0, (2400, 9, 1)),
1515            (182896.0, (2400, 9, 30)),
1516            (182897.0, (2400, 10, 1)),
1517            (182927.0, (2400, 10, 31)),
1518            (182928.0, (2400, 11, 1)),
1519            (182957.0, (2400, 11, 30)),
1520            (182958.0, (2400, 12, 1)),
1521            (182988.0, (2400, 12, 31)),
1522            (767011.0, (4000, 1, 1)),
1523            (767041.0, (4000, 1, 31)),
1524            (767042.0, (4000, 2, 1)),
1525            (767070.0, (4000, 2, 29)),
1526            (767071.0, (4000, 3, 1)),
1527            (767101.0, (4000, 3, 31)),
1528            (767102.0, (4000, 4, 1)),
1529            (767131.0, (4000, 4, 30)),
1530            (767132.0, (4000, 5, 1)),
1531            (767162.0, (4000, 5, 31)),
1532            (767163.0, (4000, 6, 1)),
1533            (767192.0, (4000, 6, 30)),
1534            (767193.0, (4000, 7, 1)),
1535            (767223.0, (4000, 7, 31)),
1536            (767224.0, (4000, 8, 1)),
1537            (767254.0, (4000, 8, 31)),
1538            (767255.0, (4000, 9, 1)),
1539            (767284.0, (4000, 9, 30)),
1540            (767285.0, (4000, 10, 1)),
1541            (767315.0, (4000, 10, 31)),
1542            (767316.0, (4000, 11, 1)),
1543            (767345.0, (4000, 11, 30)),
1544            (767346.0, (4000, 12, 1)),
1545            (767376.0, (4000, 12, 31)),
1546            (884254.0, (4321, 1, 1)),
1547            (884284.0, (4321, 1, 31)),
1548            (884285.0, (4321, 2, 1)),
1549            (884312.0, (4321, 2, 28)),
1550            (884313.0, (4321, 3, 1)),
1551            (884343.0, (4321, 3, 31)),
1552            (884344.0, (4321, 4, 1)),
1553            (884373.0, (4321, 4, 30)),
1554            (884374.0, (4321, 5, 1)),
1555            (884404.0, (4321, 5, 31)),
1556            (884405.0, (4321, 6, 1)),
1557            (884434.0, (4321, 6, 30)),
1558            (884435.0, (4321, 7, 1)),
1559            (884465.0, (4321, 7, 31)),
1560            (884466.0, (4321, 8, 1)),
1561            (884496.0, (4321, 8, 31)),
1562            (884497.0, (4321, 9, 1)),
1563            (884526.0, (4321, 9, 30)),
1564            (884527.0, (4321, 10, 1)),
1565            (884557.0, (4321, 10, 31)),
1566            (884558.0, (4321, 11, 1)),
1567            (884587.0, (4321, 11, 30)),
1568            (884588.0, (4321, 12, 1)),
1569            (884618.0, (4321, 12, 31)),
1570            (2958101.0, (9999, 1, 1)),
1571            (2958131.0, (9999, 1, 31)),
1572            (2958132.0, (9999, 2, 1)),
1573            (2958159.0, (9999, 2, 28)),
1574            (2958160.0, (9999, 3, 1)),
1575            (2958190.0, (9999, 3, 31)),
1576            (2958191.0, (9999, 4, 1)),
1577            (2958220.0, (9999, 4, 30)),
1578            (2958221.0, (9999, 5, 1)),
1579            (2958251.0, (9999, 5, 31)),
1580            (2958252.0, (9999, 6, 1)),
1581            (2958281.0, (9999, 6, 30)),
1582            (2958282.0, (9999, 7, 1)),
1583            (2958312.0, (9999, 7, 31)),
1584            (2958313.0, (9999, 8, 1)),
1585            (2958343.0, (9999, 8, 31)),
1586            (2958344.0, (9999, 9, 1)),
1587            (2958373.0, (9999, 9, 30)),
1588            (2958374.0, (9999, 10, 1)),
1589            (2958404.0, (9999, 10, 31)),
1590            (2958405.0, (9999, 11, 1)),
1591            (2958434.0, (9999, 11, 30)),
1592            (2958435.0, (9999, 12, 1)),
1593            (2958465.0, (9999, 12, 31)),
1594        ];
1595
1596        for test in test_data {
1597            let (excel_serial_datetime, expected) = test;
1598            let datetime =
1599                ExcelDateTime::new(excel_serial_datetime, ExcelDateTimeType::DateTime, false);
1600            let got = datetime.to_ymd_hms_milli();
1601            let got = (got.0, got.1, got.2); // Date parts only.
1602            assert_eq!(expected, got);
1603        }
1604    }
1605
1606    #[test]
1607    fn test_dates_only_1904_epoch() {
1608        let test_data = vec![(0.0, (1904, 1, 1))];
1609
1610        for test in test_data {
1611            let (excel_serial_datetime, expected) = test;
1612            let datetime =
1613                ExcelDateTime::new(excel_serial_datetime, ExcelDateTimeType::DateTime, true);
1614            let got = datetime.to_ymd_hms_milli();
1615            let got = (got.0, got.1, got.2); // Date parts only.
1616            assert_eq!(expected, got);
1617        }
1618    }
1619
1620    #[test]
1621    fn test_times_only_both_epochs() {
1622        #[allow(clippy::excessive_precision)]
1623        let test_data = vec![
1624            (0.0, (0, 0, 0, 0)),
1625            (1.0650613425925924e-2, (0, 15, 20, 213)),
1626            (1.1670023148148148e-2, (0, 16, 48, 290)),
1627            (3.8488958333333337e-2, (0, 55, 25, 446)),
1628            (4.3598275462962965e-2, (1, 2, 46, 891)),
1629            (4.4624965277777782e-2, (1, 4, 15, 597)),
1630            (4.8389918981481483e-2, (1, 9, 40, 889)),
1631            (4.9682407407407404e-2, (1, 11, 32, 560)),
1632            (6.2721863425925936e-2, (1, 30, 19, 169)),
1633            (7.5296064814814809e-2, (1, 48, 25, 580)),
1634            (8.5786099537037031e-2, (2, 3, 31, 919)),
1635            (9.1110949074074077e-2, (2, 11, 11, 986)),
1636            (0.10042934027777778, (2, 24, 37, 95)),
1637            (0.10772245370370370, (2, 35, 7, 220)),
1638            (0.11472348379629631, (2, 45, 12, 109)),
1639            (0.12962951388888888, (3, 6, 39, 990)),
1640            (0.13065105324074075, (3, 8, 8, 251)),
1641            (0.13833999999999999, (3, 19, 12, 576)),
1642            (0.14563164351851851, (3, 29, 42, 574)),
1643            (0.15105107638888890, (3, 37, 30, 813)),
1644            (0.17683137731481480, (4, 14, 38, 231)),
1645            (0.17810832175925925, (4, 16, 28, 559)),
1646            (0.17914608796296297, (4, 17, 58, 222)),
1647            (0.18173372685185185, (4, 21, 41, 794)),
1648            (0.20596981481481480, (4, 56, 35, 792)),
1649            (0.22586672453703704, (5, 25, 14, 885)),
1650            (0.22645513888888891, (5, 26, 5, 724)),
1651            (0.24078782407407406, (5, 46, 44, 68)),
1652            (0.24167987268518520, (5, 48, 1, 141)),
1653            (0.24574438657407408, (5, 53, 52, 315)),
1654            (0.26028449074074073, (6, 14, 48, 580)),
1655            (0.28212659722222222, (6, 46, 15, 738)),
1656            (0.31343063657407405, (7, 31, 20, 407)),
1657            (0.33233511574074076, (7, 58, 33, 754)),
1658            (0.33869363425925925, (8, 7, 43, 130)),
1659            (0.35360059027777774, (8, 29, 11, 91)),
1660            (0.38073296296296300, (9, 8, 15, 328)),
1661            (0.39631690972222228, (9, 30, 41, 781)),
1662            (0.39866275462962958, (9, 34, 4, 462)),
1663            (0.40097158564814817, (9, 37, 23, 945)),
1664            (0.40135017361111114, (9, 37, 56, 655)),
1665            (0.40639155092592594, (9, 45, 12, 230)),
1666            (0.41267108796296298, (9, 54, 14, 782)),
1667            (0.41275587962962962, (9, 54, 22, 108)),
1668            (0.41777952546296299, (10, 1, 36, 151)),
1669            (0.50681252314814818, (12, 9, 48, 602)),
1670            (0.52371005787037039, (12, 34, 8, 549)),
1671            (0.53896406249999995, (12, 56, 6, 495)),
1672            (0.54095158564814816, (12, 58, 58, 217)),
1673            (0.54160026620370372, (12, 59, 54, 263)),
1674            (0.56575614583333333, (13, 34, 41, 331)),
1675            (0.58227547453703699, (13, 58, 28, 601)),
1676            (0.58491781249999997, (14, 2, 16, 899)),
1677            (0.60853523148148148, (14, 36, 17, 444)),
1678            (0.60969271990740748, (14, 37, 57, 451)),
1679            (0.62341153935185190, (14, 57, 42, 757)),
1680            (0.63250355324074070, (15, 10, 48, 307)),
1681            (0.63518391203703706, (15, 14, 39, 890)),
1682            (0.63874986111111109, (15, 19, 47, 988)),
1683            (0.66972620370370362, (16, 4, 24, 344)),
1684            (0.68222166666666662, (16, 22, 23, 952)),
1685            (0.68745369212962970, (16, 29, 55, 999)),
1686            (0.70717892361111112, (16, 58, 20, 259)),
1687            (0.71113906250000003, (17, 4, 2, 415)),
1688            (0.72117627314814825, (17, 18, 29, 630)),
1689            (0.74121901620370367, (17, 47, 21, 323)),
1690            (0.74548456018518516, (17, 53, 29, 866)),
1691            (0.74561430555555563, (17, 53, 41, 76)),
1692            (0.74659773148148145, (17, 55, 6, 44)),
1693            (0.76029109953703700, (18, 14, 49, 151)),
1694            (0.76233493055555546, (18, 17, 45, 738)),
1695            (0.77082986111111118, (18, 29, 59, 700)),
1696            (0.77316241898148153, (18, 33, 21, 233)),
1697            (0.80167445601851861, (19, 14, 24, 673)),
1698            (0.80362055555555545, (19, 17, 12, 816)),
1699            (0.80806039351851855, (19, 23, 36, 418)),
1700            (0.82391097222222232, (19, 46, 25, 908)),
1701            (0.83874206018518516, (20, 7, 47, 314)),
1702            (0.85529633101851854, (20, 31, 37, 603)),
1703            (0.86108530092592594, (20, 39, 57, 770)),
1704            (0.86825309027777775, (20, 50, 17, 67)),
1705            (0.87705818287037041, (21, 2, 57, 827)),
1706            (0.89103609953703700, (21, 23, 5, 519)),
1707            (0.89918486111111118, (21, 34, 49, 572)),
1708            (0.90215212962962965, (21, 39, 5, 944)),
1709            (0.90229659722222222, (21, 39, 18, 426)),
1710            (0.90703436342592603, (21, 46, 7, 769)),
1711            (0.91522756944444439, (21, 57, 55, 662)),
1712            (0.92999689814814823, (22, 19, 11, 732)),
1713            (0.93323351851851843, (22, 23, 51, 376)),
1714            (0.93609688657407408, (22, 27, 58, 771)),
1715            (0.94687953703703709, (22, 43, 30, 392)),
1716            (0.95029900462962968, (22, 48, 25, 834)),
1717            (0.95407091435185187, (22, 53, 51, 727)),
1718            (0.96732101851851848, (23, 12, 56, 536)),
1719            (0.96937626157407408, (23, 15, 54, 109)),
1720            (0.97028509259259266, (23, 17, 12, 632)),
1721            (0.99999998842592586, (23, 59, 59, 999)),
1722        ];
1723
1724        for test in test_data {
1725            let (excel_serial_datetime, expected) = test;
1726
1727            // 1900 epoch.
1728            let datetime =
1729                ExcelDateTime::new(excel_serial_datetime, ExcelDateTimeType::DateTime, false);
1730            let got = datetime.to_ymd_hms_milli();
1731            let got = (got.3, got.4, got.5, got.6); // Time parts only.
1732            assert_eq!(expected, got);
1733
1734            // 1904 epoch.
1735            let datetime =
1736                ExcelDateTime::new(excel_serial_datetime, ExcelDateTimeType::DateTime, true);
1737            let got = datetime.to_ymd_hms_milli();
1738            let got = (got.3, got.4, got.5, got.6); // Time parts only.
1739            assert_eq!(expected, got);
1740        }
1741    }
1742}