mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-28 12:46:48 -06:00
Edit date cells as dates, not serial numbers (#362)
This commit is contained in:
@@ -31,6 +31,37 @@ pub fn format(v: &Value, code: &str, wb: &Workbook) -> Formatted {
|
||||
format_value(v, &parsed(code), wb.date_system)
|
||||
}
|
||||
|
||||
/// What the cell editor and the formula bar start from. Same as `Cell::input_text`, except that a
|
||||
/// constant number in a date or time format is shown as a date (as the grid shows it), not as its
|
||||
/// serial. When the formatted text would not read back as the same date (for example
|
||||
/// `dddd, mmmm d`), an ISO `yyyy-mm-dd` (with `hh:mm:ss` for a time of day; just `hh:mm:ss`
|
||||
/// for a time alone) is used instead, and if even that would not read back, the serial.
|
||||
pub fn edit_text(wb: &Workbook, sheet: &Sheet, c: CellRef) -> String {
|
||||
let raw = sheet.input_text(c);
|
||||
let Some(cell) = sheet.cell(c) else { return raw };
|
||||
let Value::Number(n) = &cell.value else { return raw };
|
||||
let code = wb.styles.get(cell.style).num_fmt.as_str();
|
||||
if cell.formula.is_some() || !parsed(code).is_date() {
|
||||
return raw;
|
||||
}
|
||||
let reads_back = |s: &str| matches!(gridcraft_core::parse::parse_input(s, wb.date_system).value, Value::Number(m) if (m - n).abs() < 1e-9);
|
||||
let shown = format(&cell.value, code, wb).text;
|
||||
if reads_back(&shown) {
|
||||
return shown;
|
||||
}
|
||||
let Some(dt) = gridcraft_core::date::datetime_from_serial(wb.date_system, *n) else { return raw };
|
||||
let time = format!("{:02}:{:02}:{:02}", dt.hour, dt.minute, dt.second);
|
||||
let date = format!("{:04}-{:02}-{:02}", dt.year, dt.month, dt.day);
|
||||
let iso = if *n < 1.0 {
|
||||
time
|
||||
} else if n.fract() == 0.0 {
|
||||
date
|
||||
} else {
|
||||
format!("{date} {time}")
|
||||
};
|
||||
if reads_back(&iso) { iso } else { raw }
|
||||
}
|
||||
|
||||
/// Whether `c` has a formula that must not be shown: its cell is formatted Hidden on a
|
||||
/// protected sheet. A spilled cell follows its anchor.
|
||||
pub fn formula_hidden(wb: &Workbook, sheet: &Sheet, c: CellRef) -> bool {
|
||||
|
||||
@@ -2095,3 +2095,31 @@ fn copying_areas_that_do_not_line_up_is_refused_with_a_message() {
|
||||
assert!(s.execute("edit.pasteSpecial", json!({"what": "all", "link": true, "at": "E1"})).is_err());
|
||||
assert!(v(&s, "E1").is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn editing_a_date_cell_shows_a_date_not_the_serial() {
|
||||
let mut s = s();
|
||||
let at = CellRef::parse("A1").unwrap();
|
||||
s.execute("cell.set", json!({"cell": "A1", "input": "2026-03-04"})).unwrap();
|
||||
{
|
||||
let d = s.doc().unwrap();
|
||||
let sh = d.wb.active().unwrap();
|
||||
assert_eq!(crate::display::edit_text(&d.wb, sh, at), "2026-03-04");
|
||||
}
|
||||
// A format whose text cannot be typed back falls back to ISO.
|
||||
s.execute("home.formatCells", json!({"range": "A1", "style": {"num_fmt": "dddd, mmmm d"}})).unwrap();
|
||||
let d = s.doc().unwrap();
|
||||
let sh = d.wb.active().unwrap();
|
||||
assert_eq!(crate::display::edit_text(&d.wb, sh, at), "2026-03-04");
|
||||
// A time alone falls back to a time, not to day 0 of 1900 ("9 AM" would lose the 36 minutes).
|
||||
let mut s3 = self::s();
|
||||
s3.execute("cell.set", json!({"cell": "A1", "input": "0.4"})).unwrap();
|
||||
s3.execute("home.formatCells", json!({"range": "A1", "style": {"num_fmt": "h AM/PM"}})).unwrap();
|
||||
let d = s3.doc().unwrap();
|
||||
assert_eq!(crate::display::edit_text(&d.wb, d.wb.active().unwrap(), at), "09:36:00");
|
||||
// Plain numbers are untouched.
|
||||
let mut s2 = self::s();
|
||||
s2.execute("cell.set", json!({"cell": "A1", "input": "42"})).unwrap();
|
||||
let d = s2.doc().unwrap();
|
||||
assert_eq!(crate::display::edit_text(&d.wb, d.wb.active().unwrap(), at), "42");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user