tz/
lib.rs

1#![crate_name = "tz"]
2#![crate_type = "rlib"]
3#![crate_type = "dylib"]
4
5//! This is a library for parsing zoneinfo files.
6//!
7//! ## Example
8//!
9//! ```no_run
10//! use std::fs::File;
11//! use std::io::Read;
12//! use std::path::Path;
13//! use tz::parse;
14//!
15//! let path = Path::new("/etc/localtime");
16//! let mut contents = Vec::new();
17//! File::open(path).unwrap().read_to_end(&mut contents).unwrap();
18//! let tz = parse(contents).unwrap();
19//!
20//! for t in tz.transitions {
21//!     println!("{:?}", t);
22//! }
23//! ```
24
25extern crate byteorder;
26use std::sync::Arc;
27
28pub mod internals;
29pub use internals::Result;
30
31
32/// Parsed, interpreted contents of a zoneinfo file.
33#[derive(PartialEq, Eq, Debug, Clone)]
34pub struct TZData {
35
36    /// Vector of transitions that are described in this data.
37    pub transitions: Vec<Transition>,
38
39    /// Vector of leap seconds that are described in this data.
40    pub leap_seconds: Vec<LeapSecond>,
41}
42
43/// The 'type' of time that the change was announced in.
44#[derive(Debug, PartialEq, Eq, Clone)]
45pub enum TransitionType {
46
47    /// Standard Time ("non-summer" time)
48    Standard,
49
50    /// Wall clock time
51    Wall,
52
53    /// Co-ordinated Universal Time
54    UTC,
55}
56
57/// A time change specification.
58#[derive(Debug, PartialEq, Eq, Clone)]
59pub struct Transition {
60
61    /// Unix timestamp when the clocks change.
62    pub timestamp: i32,
63
64    /// The new description of the local time.
65    pub local_time_type: Arc<LocalTimeType>,
66}
67
68/// A leap second specification.
69#[derive(Debug, PartialEq, Eq, Clone)]
70pub struct LeapSecond {
71
72    /// Unix timestamp at which a leap second occurs.
73    pub timestamp: i32,
74
75    /// Number of leap seconds to be added.
76    pub leap_second_count: u32,
77}
78
79/// A description of the local time in a particular timezone, during the
80/// period in which the clocks do not change.
81#[derive(Debug, PartialEq, Eq, Clone)]
82pub struct LocalTimeType {
83
84    /// The time zone abbreviation - such as "GMT" or "UTC".
85    pub name: String,
86
87    /// Number of seconds to be added to Universal Time.
88    pub offset: i32,
89
90    /// Whether to set DST.
91    pub is_dst: bool,
92
93    /// The current 'type' of time.
94    pub transition_type: TransitionType,
95}
96
97/// Parses a series of bytes into a timezone data structure.
98pub fn parse(input: Vec<u8>) -> Result<TZData> {
99    let tz = try!(internals::parse(input, internals::Limits::sensible()));
100    cook(tz)
101}
102
103/// Interpret a set of internal time zone data.
104pub fn cook(tz: internals::TZData) -> Result<TZData> {
105    let mut transitions = Vec::with_capacity(tz.header.num_transitions as usize);
106    let mut local_time_types = Vec::with_capacity(tz.header.num_local_time_types as usize);
107
108    // First, build up a list of local time types...
109    for i in 0 .. tz.header.num_local_time_types as usize {
110        let ref data = tz.time_info[i];
111
112        // Isolate the relevant bytes by the index of the start of the
113        // string and the next available null char
114        let name_bytes = tz.strings.iter()
115                                   .cloned()
116                                   .skip(data.name_offset as usize)
117                                   .take_while(|&c| c != 0)
118                                   .collect();
119
120        let info = LocalTimeType {
121            name:             try!(String::from_utf8(name_bytes)),
122            offset:           data.offset,
123            is_dst:           data.is_dst != 0,
124            transition_type:  flags_to_transition_type(tz.standard_flags[i] != 0,
125                                                       tz.gmt_flags[i]      != 0),
126        };
127
128        local_time_types.push(Arc::new(info));
129    }
130
131    // ...then, link each transition with the time type it refers to.
132    for i in 0 .. tz.header.num_transitions as usize {
133        let ref t = tz.transitions[i];
134
135        let transition = Transition {
136            timestamp:        t.timestamp,
137            local_time_type:  local_time_types[t.local_time_type_index as usize].clone(),
138        };
139
140        transitions.push(transition);
141    }
142
143    let mut leap_seconds = Vec::new();
144    for ls in tz.leap_seconds.iter() {
145        let leap_second = LeapSecond {
146            timestamp: ls.timestamp,
147            leap_second_count: ls.leap_second_count,
148        };
149
150        leap_seconds.push(leap_second);
151    }
152
153    Ok(TZData {
154        transitions: transitions,
155        leap_seconds: leap_seconds,
156    })
157}
158
159/// Combine the two flags to get the type of this transition.
160///
161/// The transition type is stored as two separate flags in the data file. The
162/// first set comes completely before the second, so these can only be
163/// combined after the entire file has been read.
164fn flags_to_transition_type(standard: bool, gmt: bool) -> TransitionType {
165    match (standard, gmt) {
166        (_,     true)   => TransitionType::UTC,
167        (true,  _)      => TransitionType::Standard,
168        (false, false)  => TransitionType::Wall,
169    }
170}