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
5 changes: 5 additions & 0 deletions src/reader/xlsx/workbook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ pub(crate) fn read<R: io::Read + io::Seek>(
obj.set_attributes(&mut reader, e);
wb.set_workbook_view(obj);
}
b"workbookPr" => {
if let Some(v) = get_attribute(e, b"date1904") {
wb.set_date1904(v == "1" || v == "true");
}
}
b"workbookProtection" => {
let mut obj = WorkbookProtection::default();
obj.set_attributes(&mut reader, e);
Expand Down
23 changes: 23 additions & 0 deletions src/structs/workbook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub struct Workbook {
pivot_caches: Vec<(Box<str>, Box<str>, Box<str>)>,
workbook_protection: Option<Box<WorkbookProtection>>,
defined_names: Vec<DefinedName>,
date1904: bool,
}

impl Workbook {
Expand Down Expand Up @@ -1008,6 +1009,28 @@ impl Workbook {
self
}

/// Whether the workbook uses the 1904 date system (`workbookPr/@date1904`).
///
/// In the 1904 system, serial 0 is 1904-01-01 and serial 1 is 1904-01-02;
/// in the default 1900 system, serial 1 is 1900-01-01. From 1900-03-01 on
/// (past Excel's fictitious 1900-02-29), the same serial is a date 1,462
/// days later in the 1904 system than in the 1900 system.
#[inline]
#[must_use]
pub fn date1904(&self) -> bool {
self.date1904
}

/// Set whether the workbook uses the 1904 date system.
///
/// This only changes how serial numbers are interpreted as dates; the
/// stored cell values are not converted.
#[inline]
pub fn set_date1904(&mut self, value: bool) -> &mut Self {
self.date1904 = value;
self
}

/// Get Defined Name (Vec).
#[inline]
#[must_use]
Expand Down
3 changes: 3 additions & 0 deletions src/writer/xlsx/workbook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ pub(crate) fn write<W: io::Seek + io::Write>(

// workbookPr
let mut attributes: crate::structs::AttrCollection = Vec::new();
if wb.date1904() {
attributes.push(("date1904", "1").into());
}
attributes.push(("filterPrivacy", "1").into());
// attributes.push(("defaultThemeVersion", "124226").into());
if wb.has_macros() {
Expand Down
79 changes: 79 additions & 0 deletions tests/date1904_roundtrip.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use std::io::{
Cursor,
Read,
};

use umya_spreadsheet::{
reader,
writer,
};
use zip::ZipArchive;

fn workbook_xml(bytes: &[u8]) -> String {
let mut archive = ZipArchive::new(Cursor::new(bytes)).unwrap();
let mut xml = String::new();
archive
.by_name("xl/workbook.xml")
.unwrap()
.read_to_string(&mut xml)
.unwrap();
xml
}

fn write(book: &umya_spreadsheet::Workbook) -> Vec<u8> {
let mut out = Vec::new();
writer::xlsx::write_writer(book, &mut out).unwrap();
out
}

#[test]
fn date1904_defaults_to_false_and_is_not_written() {
let book = umya_spreadsheet::new_file();
assert!(!book.date1904());
assert!(!workbook_xml(&write(&book)).contains("date1904"));
}

#[test]
fn date1904_survives_write_and_read() {
let mut book = umya_spreadsheet::new_file();
book.set_date1904(true);
let bytes = write(&book);
assert!(workbook_xml(&bytes).contains(r#"date1904="1""#));

let reread = reader::xlsx::read_reader(Cursor::new(bytes), true).unwrap();
assert!(reread.date1904());

// and a second save keeps it: a 1904 workbook must not silently become a
// 1900 one, which would move every date back by 1,462 days
assert!(workbook_xml(&write(&reread)).contains(r#"date1904="1""#));
}

#[test]
fn date1904_true_spelling_is_read() {
let mut book = umya_spreadsheet::new_file();
book.set_date1904(true);
let bytes = write(&book);

// rewrite the attribute as xsd:boolean "true", which is also valid
let mut archive = ZipArchive::new(Cursor::new(bytes)).unwrap();
let mut out = zip::ZipWriter::new(Cursor::new(Vec::new()));
for i in 0..archive.len() {
let mut entry = archive.by_index(i).unwrap();
let name = entry.name().to_string();
let mut data = Vec::new();
entry.read_to_end(&mut data).unwrap();
if name == "xl/workbook.xml" {
data = String::from_utf8(data)
.unwrap()
.replace(r#"date1904="1""#, r#"date1904="true""#)
.into_bytes();
}
out.start_file(name, zip::write::SimpleFileOptions::default())
.unwrap();
std::io::Write::write_all(&mut out, &data).unwrap();
}
let bytes = out.finish().unwrap().into_inner();

let reread = reader::xlsx::read_reader(Cursor::new(bytes), true).unwrap();
assert!(reread.date1904());
}
Loading