Checksum of a saved file

A common issue that occurs with rust_xlsxwriter, but also with Excel, is that running the same program twice doesn't generate the same file, byte for byte. This can cause issues with applications that do checksumming for testing purposes.

For example consider the following simple rust_xlsxwriter program:

// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright 2022-2024, John McNamara, jmcnamara@cpan.org

//! Create a simple workbook to demonstrate the changing checksum due to the
//! changing creation date.

use rust_xlsxwriter::{Workbook, XlsxError};

fn main() -> Result<(), XlsxError> {
    let mut workbook = Workbook::new();
    let worksheet = workbook.add_worksheet();

    worksheet.write_string(0, 0, "Hello")?;

    workbook.save("properties.xlsx")?;

    Ok(())
}

If we run this several times, with a small delay, we will get different checksums as shown below:

$ cargo run --example doc_properties_checksum1

$ sum properties.xlsx
62457 6 properties.xlsx

$ sleep 2

$ cargo run --example doc_properties_checksum1

$ sum properties.xlsx
56692 6 properties.xlsx # Different to previous.

This is due to a file creation datetime that is included in the file and which changes each time a new file is created.

The relevant section of the docProps/core.xml sub-file in the xlsx format looks like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cp:coreProperties>
  <dc:creator/>
  <cp:lastModifiedBy/>
  <dcterms:created xsi:type="dcterms:W3CDTF">2023-01-08T00:23:58Z</dcterms:created>
  <dcterms:modified xsi:type="dcterms:W3CDTF">2023-01-08T00:23:58Z</dcterms:modified>
</cp:coreProperties>

If required this can be avoided by setting a constant creation date in the document properties metadata:

// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright 2022-2024, John McNamara, jmcnamara@cpan.org

//! Create a simple workbook to demonstrate a constant checksum due to the a
//! constant creation date.

use rust_xlsxwriter::{DocProperties, ExcelDateTime, Workbook, XlsxError};

fn main() -> Result<(), XlsxError> {
    let mut workbook = Workbook::new();

    // Create a file creation date for the file.
    let date = ExcelDateTime::from_ymd(2023, 1, 1)?;

    // Add it to the document metadata.
    let properties = DocProperties::new().set_creation_datetime(&date);
    workbook.set_properties(&properties);

    let worksheet = workbook.add_worksheet();
    worksheet.write_string(0, 0, "Hello")?;

    workbook.save("properties.xlsx")?;

    Ok(())
}

Then we will get the same checksum for the same output every time:

$ cargo run --example doc_properties_checksum2

$ sum properties.xlsx
8914 6 properties.xlsx

$ sleep 2

$ cargo run --example doc_properties_checksum2

$ sum properties.xlsx
8914 6 properties.xlsx # Same as previous

For more details see DocProperties and workbook::set_properties().