Skip to main content

tzif_codec/
write.rs

1use crate::{
2    common::{TimeSize, HEADER_RESERVED_LEN, TZIF_MAGIC},
3    validate::validate_file,
4    DataBlock, TzifError, TzifFile, Version,
5};
6
7impl TzifFile {
8    /// Serializes this file to `TZif` bytes after validating it.
9    ///
10    /// # Errors
11    ///
12    /// Returns an error if the file is invalid or contains counts or timestamps that
13    /// cannot be represented by the selected `TZif` version.
14    pub fn serialize(&self) -> Result<Vec<u8>, TzifError> {
15        validate_file(self)?;
16        let mut out = Vec::new();
17        write_header(&mut out, self.version, &self.v1)?;
18        write_data_block(&mut out, &self.v1, TimeSize::ThirtyTwo)?;
19        if self.version.is_v2_plus() {
20            let block = self
21                .v2_plus
22                .as_ref()
23                .ok_or(TzifError::MissingV2PlusData(self.version))?;
24            write_header(&mut out, self.version, block)?;
25            write_data_block(&mut out, block, TimeSize::SixtyFour)?;
26            out.push(b'\n');
27            out.extend_from_slice(self.footer.as_deref().unwrap_or("").as_bytes());
28            out.push(b'\n');
29        }
30        Ok(out)
31    }
32
33    /// Serializes this file to `TZif` bytes.
34    ///
35    /// # Errors
36    ///
37    /// Returns an error if serialization validation fails.
38    pub fn to_bytes(&self) -> Result<Vec<u8>, TzifError> {
39        self.serialize()
40    }
41}
42
43fn write_header(out: &mut Vec<u8>, version: Version, block: &DataBlock) -> Result<(), TzifError> {
44    out.extend_from_slice(TZIF_MAGIC);
45    out.push(version.byte());
46    out.extend_from_slice(&[0; HEADER_RESERVED_LEN]);
47    write_u32(out, block.ut_local_indicators.len())?;
48    write_u32(out, block.standard_wall_indicators.len())?;
49    write_u32(out, block.leap_seconds.len())?;
50    write_u32(out, block.transition_times.len())?;
51    write_u32(out, block.local_time_types.len())?;
52    write_u32(out, block.designations.len())?;
53    Ok(())
54}
55
56fn write_data_block(
57    out: &mut Vec<u8>,
58    block: &DataBlock,
59    time_size: TimeSize,
60) -> Result<(), TzifError> {
61    for (index, &time) in block.transition_times.iter().enumerate() {
62        write_time(out, time, time_size, index, "transition")?;
63    }
64    out.extend_from_slice(&block.transition_types);
65    for local_time_type in &block.local_time_types {
66        out.extend_from_slice(&local_time_type.utc_offset.to_be_bytes());
67        out.push(u8::from(local_time_type.is_dst));
68        out.push(local_time_type.designation_index);
69    }
70    out.extend_from_slice(&block.designations);
71    for (index, leap_second) in block.leap_seconds.iter().enumerate() {
72        write_time(out, leap_second.occurrence, time_size, index, "leap")?;
73        out.extend_from_slice(&leap_second.correction.to_be_bytes());
74    }
75    for &is_standard in &block.standard_wall_indicators {
76        out.push(u8::from(is_standard));
77    }
78    for &is_ut in &block.ut_local_indicators {
79        out.push(u8::from(is_ut));
80    }
81    Ok(())
82}
83
84fn write_time(
85    out: &mut Vec<u8>,
86    value: i64,
87    time_size: TimeSize,
88    index: usize,
89    kind: &'static str,
90) -> Result<(), TzifError> {
91    match time_size {
92        TimeSize::ThirtyTwo => {
93            let value = i32::try_from(value).map_err(|_| match kind {
94                "transition" => TzifError::Version1TransitionOutOfRange { index, value },
95                _ => TzifError::Version1LeapSecondOutOfRange { index, value },
96            })?;
97            out.extend_from_slice(&value.to_be_bytes());
98        }
99        TimeSize::SixtyFour => out.extend_from_slice(&value.to_be_bytes()),
100    }
101    Ok(())
102}
103
104fn write_u32(out: &mut Vec<u8>, value: usize) -> Result<(), TzifError> {
105    let value = u32::try_from(value).map_err(|_| TzifError::CountOverflow {
106        field: "count",
107        count: value,
108    })?;
109    out.extend_from_slice(&value.to_be_bytes());
110    Ok(())
111}