mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-03 08:51:30 -06:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cdd6db150 |
@@ -10,6 +10,11 @@ use gridcraft_numfmt::{Formatted, NumberFormat, format_value};
|
||||
/// Parsed number formats are cached by code.
|
||||
fn parsed(code: &str) -> NumberFormat {
|
||||
static CACHE: std::sync::LazyLock<Mutex<HashMap<String, NumberFormat>>> = std::sync::LazyLock::new(|| Mutex::new(HashMap::new()));
|
||||
// A code of more bytes than 4 × the character limit can't be within it: it shows as General
|
||||
// without being hashed or scanned again for every cell that shares it.
|
||||
if code.len() > 4 * gridcraft_numfmt::MAX_FORMAT_CODE_CHARS {
|
||||
return NumberFormat::parse("General");
|
||||
}
|
||||
let lock = || CACHE.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
if let Some(f) = lock().get(code) {
|
||||
return f.clone();
|
||||
@@ -226,3 +231,20 @@ pub fn stats(sheet: &Sheet, ranges: &[RangeRef]) -> Stats {
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// A format code far past Excel's 255 characters shows its values as General, for every cell
|
||||
/// that shares it, without being hashed into the cache each time.
|
||||
#[test]
|
||||
fn an_overlong_code_formats_as_general() {
|
||||
let wb = Workbook::new();
|
||||
let code = format!("0.00{}", "\"x\"".repeat(400_000));
|
||||
for _ in 0..1_000 {
|
||||
assert_eq!(format(&Value::Number(1.5), &code, &wb).text, "1.5");
|
||||
}
|
||||
assert_eq!(format(&Value::Number(1.5), "0.00", &wb).text, "1.50");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,9 +575,20 @@ pub fn series(cx: &mut Ctx, sheet: usize, options: SeriesOptions<'_>) -> Result<
|
||||
match (unit, d) {
|
||||
("month", Some(_)) => (step as i64).checked_mul(i as i64).and_then(|m| add_months_clamped(sys, start, m)).unwrap_or(v),
|
||||
("year", Some(_)) => (step as i64).checked_mul(i as i64 * 12).and_then(|m| add_months_clamped(sys, start, m)).unwrap_or(v),
|
||||
// Whole weeks at a time: a huge step costs no more than a small one.
|
||||
("weekday", Some(_)) => add_weekdays(sys, v, step as i64)
|
||||
.ok_or_else(|| crate::EngineError::Other("The weekday series runs past the supported date range.".into()))?,
|
||||
("weekday", Some(_)) => {
|
||||
let mut n = v;
|
||||
let mut left = step.abs() as i64;
|
||||
while left > 0 {
|
||||
n += step.signum();
|
||||
let Some(day) = datetime_from_serial(sys, n) else {
|
||||
return Err(crate::EngineError::Other("The weekday series runs past the supported date range.".into()));
|
||||
};
|
||||
if day.weekday != 0 && day.weekday != 6 {
|
||||
left -= 1;
|
||||
}
|
||||
}
|
||||
n
|
||||
}
|
||||
_ => v + step,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3975,24 +3975,6 @@ fn fill_series_weekday_out_of_range_step_errors() {
|
||||
assert_eq!(v(&s, "A2"), Value::Empty);
|
||||
}
|
||||
|
||||
/// A step of a million weekdays moves whole weeks at a time instead of visiting every day, and
|
||||
/// lands where counting day by day would.
|
||||
#[test]
|
||||
fn fill_series_weekday_large_step_is_arithmetic() {
|
||||
let mut s = s();
|
||||
// 45005 is Monday 20 March 2023; five weekdays on from a Monday is the next Monday.
|
||||
s.execute("cell.set", json!({"cell": "A1", "input": "45005"})).unwrap();
|
||||
s.execute("edit.fillSeries", json!({"range": "A1:A3", "direction": "columns", "type": "date", "dateUnit": "weekday", "step": 1_000_000}))
|
||||
.unwrap();
|
||||
assert_eq!(v(&s, "A2"), Value::Number(45005.0 + 1_400_000.0));
|
||||
assert_eq!(v(&s, "A3"), Value::Number(45005.0 + 2_800_000.0));
|
||||
s.execute("cell.set", json!({"cell": "B1", "input": "45009"})).unwrap();
|
||||
s.execute("edit.fillSeries", json!({"range": "B1:B3", "direction": "columns", "type": "date", "dateUnit": "weekday", "step": -1})).unwrap();
|
||||
// Friday 24 March 2023 back one weekday at a time: Thursday, Wednesday.
|
||||
assert_eq!(v(&s, "B2"), Value::Number(45008.0));
|
||||
assert_eq!(v(&s, "B3"), Value::Number(45007.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fill_series_month_leap_year() {
|
||||
let s = series_date("2028-01-31", "A1:A3", "month");
|
||||
|
||||
Reference in New Issue
Block a user