1use crate::{
2 common::{TimeSize, HEADER_RESERVED_LEN, TZIF_MAGIC},
3 validate::validate_file,
4 DataBlock, TzifError, TzifFile, Version,
5};
6
7impl TzifFile {
8 pub fn to_bytes(&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
34fn write_header(out: &mut Vec<u8>, version: Version, block: &DataBlock) -> Result<(), TzifError> {
35 out.extend_from_slice(TZIF_MAGIC);
36 out.push(version.byte());
37 out.extend_from_slice(&[0; HEADER_RESERVED_LEN]);
38 write_u32(out, block.ut_local_indicators.len())?;
39 write_u32(out, block.standard_wall_indicators.len())?;
40 write_u32(out, block.leap_seconds.len())?;
41 write_u32(out, block.transition_times.len())?;
42 write_u32(out, block.local_time_types.len())?;
43 write_u32(out, block.designations.len())?;
44 Ok(())
45}
46
47fn write_data_block(
48 out: &mut Vec<u8>,
49 block: &DataBlock,
50 time_size: TimeSize,
51) -> Result<(), TzifError> {
52 for (index, &time) in block.transition_times.iter().enumerate() {
53 write_time(out, time, time_size, index, "transition")?;
54 }
55 out.extend_from_slice(&block.transition_types);
56 for local_time_type in &block.local_time_types {
57 out.extend_from_slice(&local_time_type.utc_offset.to_be_bytes());
58 out.push(u8::from(local_time_type.is_dst));
59 out.push(local_time_type.designation_index);
60 }
61 out.extend_from_slice(&block.designations);
62 for (index, leap_second) in block.leap_seconds.iter().enumerate() {
63 write_time(out, leap_second.occurrence, time_size, index, "leap")?;
64 out.extend_from_slice(&leap_second.correction.to_be_bytes());
65 }
66 for &is_standard in &block.standard_wall_indicators {
67 out.push(u8::from(is_standard));
68 }
69 for &is_ut in &block.ut_local_indicators {
70 out.push(u8::from(is_ut));
71 }
72 Ok(())
73}
74
75fn write_time(
76 out: &mut Vec<u8>,
77 value: i64,
78 time_size: TimeSize,
79 index: usize,
80 kind: &'static str,
81) -> Result<(), TzifError> {
82 match time_size {
83 TimeSize::ThirtyTwo => {
84 let value = i32::try_from(value).map_err(|_| match kind {
85 "transition" => TzifError::Version1TransitionOutOfRange { index, value },
86 _ => TzifError::Version1LeapSecondOutOfRange { index, value },
87 })?;
88 out.extend_from_slice(&value.to_be_bytes());
89 }
90 TimeSize::SixtyFour => out.extend_from_slice(&value.to_be_bytes()),
91 }
92 Ok(())
93}
94
95fn write_u32(out: &mut Vec<u8>, value: usize) -> Result<(), TzifError> {
96 let value = u32::try_from(value).map_err(|_| TzifError::CountOverflow {
97 field: "count",
98 count: value,
99 })?;
100 out.extend_from_slice(&value.to_be_bytes());
101 Ok(())
102}