Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions src/helper/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,14 @@ pub fn excel_to_date_time_parts(excel_timestamp: f64) -> (jiff::civil::Date, i64
/// The integer part of the timestamp represents the number of days since a base
/// date, while the fractional part represents the time of day.
///
/// The fallible version of this method is [`excel_to_date_time_jiff_checked`].
///
/// # Panics
///
/// This panics when the input causes the number of days since the `base_date`
/// to be outside of the supported range for `jiff` spans. See
/// [`jiff::Span::days`] for relevant bounds.
///
/// # Parameters
///
/// - `excel_timestamp: f64` The Excel timestamp to be converted and it is
Expand Down Expand Up @@ -191,10 +199,25 @@ pub fn excel_to_date_time_parts(excel_timestamp: f64) -> (jiff::civil::Date, i64
/// ```
#[must_use]
pub fn excel_to_date_time_jiff(excel_timestamp: f64) -> jiff::civil::DateTime {
excel_to_date_time_jiff_checked(excel_timestamp).expect("input was out of supported range")
}

/// This is the fallible version of [`excel_to_date_time_jiff`]. See
/// documentation on that function.
///
/// # Error
///
/// This returns an error when the input causes the number of days since the
/// `base_date` to be outside of the supported range for `jiff` spans. See
/// [`jiff::Span::days`] for relevant bounds.
pub fn excel_to_date_time_jiff_checked(
excel_timestamp: f64,
) -> Result<jiff::civil::DateTime, jiff::Error> {
let (base_date, days, time) = excel_to_date_time_parts(excel_timestamp);
let seconds: i64 = cast((time * (24.0 * 60.0 * 60.0)).round()).unwrap();
let seconds: i64 = cast((time * (24.0 * 60.0 * 60.0)).round())
.expect("this cast should never fail because `time` should be a value between 0 and 1");

base_date.at(0, 0, 0, 0) + days.day().seconds(seconds)
Ok(base_date.at(0, 0, 0, 0) + jiff::Span::new().try_days(days)?.seconds(seconds))
}

/// Converts an Excel timestamp to a [`jiff::civil::DateTime`] object with
Expand Down Expand Up @@ -691,4 +714,13 @@ mod tests {
"chrono conversion is incorrect"
);
}

#[test]
fn excel_to_date_time_checked() {
let actual = excel_to_date_time_jiff_checked(123_456_789.0);
assert!(
actual.is_err(),
"Value is out of range of jiff::Span days and should be rejected"
);
}
}
8 changes: 4 additions & 4 deletions src/helper/number_format/number_formater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ mod tests {
#[test]
fn format_as_number_rounds_half_away_from_zero() {
// Excel rounds display values; truncating turned 107310.6 into 107,310.
assert_eq!(format_as_number(107310.6, "#,##0"), "107,311");
assert_eq!(format_as_number(107_310.6, "#,##0"), "107,311");
assert_eq!(format_as_number(12.5, "0"), "13");
assert_eq!(format_as_number(-12.5, "0"), "-13");
assert_eq!(format_as_number(99999.5, "0"), "100000");
Expand All @@ -371,13 +371,13 @@ mod tests {
#[test]
fn format_as_number_keeps_currency_prefix() {
assert_eq!(format_as_number(39.1, "$0.00"), "$39.10");
assert_eq!(format_as_number(107310.6, "$#,##0"), "$107,311");
assert_eq!(format_as_number(107_310.6, "$#,##0"), "$107,311");
}

#[test]
fn format_as_number_thousands_grouping_survives_rounding() {
assert_eq!(format_as_number(999999.5, "#,##0"), "1,000,000");
assert_eq!(format_as_number(1234567.891, "#,##0.00"), "1,234,567.89");
assert_eq!(format_as_number(999_999.5, "#,##0"), "1,000,000");
assert_eq!(format_as_number(1_234_567.891, "#,##0.00"), "1,234,567.89");
assert_eq!(format_as_number(-1234.5, "#,##0"), "-1,235");
}
}
9 changes: 6 additions & 3 deletions src/helper/number_format/percentage_formater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,15 @@ mod tests {
fn format_as_percentage_keeps_decimal_precision() {
// Rounding to an integer before applying the decimal format code
// turned 17.309...% into "17.0%".
assert_eq!(format_as_percentage(0.1730909090909091, "0.0%"), "17.3%");
assert_eq!(
format_as_percentage(0.173_090_909_090_909_1, "0.0%"),
"17.3%"
);
}

#[test]
fn format_as_percentage_integer_format() {
assert_eq!(format_as_percentage(0.1730909090909091, "0%"), "17%");
assert_eq!(format_as_percentage(0.173_090_909_090_909_1, "0%"), "17%");
}

#[test]
Expand All @@ -71,6 +74,6 @@ mod tests {
// display value (106.5) away from zero.
assert_eq!(format_as_percentage(1.065, "0%"), "107%");
assert_eq!(format_as_percentage(0.125, "0.0%"), "12.5%");
assert_eq!(format_as_percentage(0.10649999999999999, "0%"), "11%");
assert_eq!(format_as_percentage(0.106_499_999_999_999_99, "0%"), "11%");
}
}
2 changes: 1 addition & 1 deletion src/structs/data_bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ mod tests {
let mut buf = Vec::new();
loop {
match reader.read_event_into(&mut buf) {
Ok(Event::Start(ref e)) | Ok(Event::Empty(ref e))
Ok(Event::Start(ref e) | Event::Empty(ref e))
if e.name().into_inner() == b"dataBar" =>
{
let mut obj = DataBar::default();
Expand Down
4 changes: 4 additions & 0 deletions tests/streaming_writer_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ use umya_spreadsheet::{
/// intentionally non-ASCII to exercise UTF-8 handling in workbook.xml.
const SHEETS: [&str; 3] = ["Sheet1", "Sheet2", "データ"];

#[expect(
clippy::approx_constant,
reason = "used in a test and not worth changing"
)]
/// Build a deterministic, multi-sheet workbook covering the common cell value
/// kinds (string, number, bool, formula) plus a non-ASCII string value.
fn build_book() -> Workbook {
Expand Down
Loading