FIX: Dates are only valid up to the last day of 9999

This commit is contained in:
Nicolás Hatcher
2024-12-30 12:50:33 +01:00
committed by Nicolás Hatcher Andrés
parent d04691b790
commit c6adf8449b
6 changed files with 54 additions and 8 deletions

View File

@@ -21,7 +21,8 @@ pub(crate) const EXCEL_DATE_BASE: i32 = 693_594;
// However, it uses a different numbering scheme for dates
// that are before 1900-01-01.
// So for now we will simply not support dates before 1900-01-01.
pub(crate) const EXCEL_DATE_MIN: i32 = 2;
pub(crate) const MINIMUM_DATE_SERIAL_NUMBER: i32 = 2;
// Excel can handle dates until the year 9999-12-31
pub(crate) const EXCEL_DATE_MAX: i32 = 2_958_465;
// 2958465 is the number of days from 1900-01-01 to 9999-12-31
pub(crate) const MAXIMUM_DATE_SERIAL_NUMBER: i32 = 2_958_465;

View File

@@ -5,8 +5,8 @@ use chrono::Months;
use chrono::NaiveDate;
use crate::constants::EXCEL_DATE_BASE;
use crate::constants::EXCEL_DATE_MAX;
use crate::constants::EXCEL_DATE_MIN;
use crate::constants::MAXIMUM_DATE_SERIAL_NUMBER;
use crate::constants::MINIMUM_DATE_SERIAL_NUMBER;
#[inline]
fn convert_to_serial_number(date: NaiveDate) -> i32 {
@@ -14,8 +14,8 @@ fn convert_to_serial_number(date: NaiveDate) -> i32 {
}
fn is_date_within_range(date: NaiveDate) -> bool {
convert_to_serial_number(date) >= EXCEL_DATE_MIN
&& convert_to_serial_number(date) <= EXCEL_DATE_MAX
convert_to_serial_number(date) >= MINIMUM_DATE_SERIAL_NUMBER
&& convert_to_serial_number(date) <= MAXIMUM_DATE_SERIAL_NUMBER
}
pub fn from_excel_date(days: i64) -> NaiveDate {
@@ -132,11 +132,11 @@ mod tests {
fn test_max_and_min_dates() {
assert_eq!(
permissive_date_to_serial_number(31, 12, 9999),
Ok(EXCEL_DATE_MAX),
Ok(MAXIMUM_DATE_SERIAL_NUMBER),
);
assert_eq!(
permissive_date_to_serial_number(1, 1, 1900),
Ok(EXCEL_DATE_MIN),
Ok(MINIMUM_DATE_SERIAL_NUMBER),
);
}
}

View File

@@ -3,6 +3,7 @@ use chrono::Datelike;
use chrono::Months;
use chrono::Timelike;
use crate::constants::MAXIMUM_DATE_SERIAL_NUMBER;
use crate::expressions::types::CellReferenceIndex;
use crate::formatter::dates::date_to_serial_number;
use crate::formatter::dates::permissive_date_to_serial_number;
@@ -32,6 +33,13 @@ impl Model {
}
Err(s) => return s,
};
if serial_number > MAXIMUM_DATE_SERIAL_NUMBER as i64 {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "Function DAY parameter 1 value is too large.".to_string(),
};
}
let date = from_excel_date(serial_number);
let day = date.day() as f64;
CalcResult::Number(day)
@@ -56,6 +64,13 @@ impl Model {
}
Err(s) => return s,
};
if serial_number > MAXIMUM_DATE_SERIAL_NUMBER as i64 {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "Function DAY parameter 1 value is too large.".to_string(),
};
}
let date = from_excel_date(serial_number);
let month = date.month() as f64;
CalcResult::Number(month)
@@ -80,6 +95,13 @@ impl Model {
}
Err(s) => return s,
};
if serial_number > MAXIMUM_DATE_SERIAL_NUMBER as i64 {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "Function DAY parameter 1 value is too large.".to_string(),
};
}
let months = match self.get_number_no_bools(&args[1], cell) {
Ok(c) => {
@@ -178,6 +200,13 @@ impl Model {
}
Err(s) => return s,
};
if serial_number > MAXIMUM_DATE_SERIAL_NUMBER as i64 {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "Function DAY parameter 1 value is too large.".to_string(),
};
}
let date = from_excel_date(serial_number);
let year = date.year() as f64;
CalcResult::Number(year)

View File

@@ -13,6 +13,7 @@ mod test_fn_averageifs;
mod test_fn_choose;
mod test_fn_concatenate;
mod test_fn_count;
mod test_fn_day;
mod test_fn_exact;
mod test_fn_financial;
mod test_fn_formulatext;

View File

@@ -0,0 +1,15 @@
#![allow(clippy::unwrap_used)]
use crate::test::util::new_empty_model;
#[test]
fn test_fn_date_arguments() {
let mut model = new_empty_model();
model._set("A1", "=DAY(95051806)");
model._set("A2", "=DAY(2958465)");
model.evaluate();
assert_eq!(model._get_text("A1"), *"#NUM!");
assert_eq!(model._get_text("A2"), *"31");
}