1#![no_std]
2
3#[cfg(any(test, feature = "system_time"))] #[macro_use] extern crate std;
4
5mod num;
6#[cfg(feature = "system_time")] mod system_time;
7#[cfg(test)] mod tests;
8mod time_zones;
9
10use core::fmt;
11use num::positive_rem;
12use time_zones::days_since_unix;
13pub use time_zones::{TimeZone, LocalTimeConversionError, UnambiguousTimeZone, DaylightSaving,
14 Utc, FixedOffsetFromUtc, CentralEurope};
15
16#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
18pub struct UnixTimestamp(pub i64);
19
20#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
21pub struct DateTime<Tz: TimeZone> {
22 pub naive: NaiveDateTime,
23 pub time_zone: Tz,
24}
25
26#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
28pub struct NaiveDateTime {
29 pub year: i32,
33
34 pub month: Month,
35
36 pub day: u8,
38
39 pub hour: u8,
40 pub minute: u8,
41 pub second: u8,
42}
43
44impl<Tz: fmt::Debug + TimeZone> fmt::Debug for DateTime<Tz> {
45 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
46 write!(formatter, "DateTime({:?}, {:?})", self.time_zone, self.naive)
47 }
48}
49
50impl fmt::Debug for NaiveDateTime {
51 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
52 write!(formatter, "{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
53 self.year, self.month.to_number(), self.day,
54 self.hour, self.minute, self.second)
55 }
56}
57
58impl<Tz: TimeZone> DateTime<Tz> {
59 pub fn new(time_zone: Tz, year: i32, month: Month, day: u8,
60 hour: u8, minute: u8, second: u8)
61 -> Self {
62 DateTime {
63 naive: NaiveDateTime::new(year, month, day, hour, minute, second),
64 time_zone: time_zone,
65 }
66 }
67
68 pub fn year(&self) -> i32 { self.naive.year }
69 pub fn month(&self) -> Month { self.naive.month }
70 pub fn day(&self) -> u8 { self.naive.day }
71 pub fn hour(&self) -> u8 { self.naive.hour }
72 pub fn minute(&self) -> u8 { self.naive.minute }
73 pub fn second(&self) -> u8 { self.naive.second }
74
75 pub fn day_of_the_week(&self) -> DayOfTheWeek { self.naive.day_of_the_week() }
76
77 pub fn from_timestamp(t: UnixTimestamp, time_zone: Tz) -> Self {
78 DateTime {
79 naive: time_zone.from_timestamp(t),
80 time_zone: time_zone,
81 }
82 }
83
84 pub fn to_timestamp(&self) -> Result<UnixTimestamp, LocalTimeConversionError> {
85 self.time_zone.to_timestamp(&self.naive)
86 }
87
88 pub fn convert_time_zone<NewTz: TimeZone>(&self, new_time_zone: NewTz)
89 -> Result<DateTime<NewTz>, LocalTimeConversionError> {
90 Ok(DateTime::from_timestamp(try!(self.to_timestamp()), new_time_zone))
91 }
92}
93
94impl<Tz: UnambiguousTimeZone> DateTime<Tz> {
95 pub fn to_unambiguous_timestamp(&self) -> UnixTimestamp {
96 self.time_zone.to_unambiguous_timestamp(&self.naive)
97 }
98
99 pub fn convert_unambiguous_time_zone<NewTz: TimeZone>(&self, new_time_zone: NewTz) -> DateTime<NewTz> {
100 DateTime::from_timestamp(self.to_unambiguous_timestamp(), new_time_zone)
101 }
102}
103
104
105impl NaiveDateTime {
106 pub fn new(year: i32, month: Month, day: u8, hour: u8, minute: u8, second: u8) -> Self {
107 NaiveDateTime {
108 year: year,
109 month: month,
110 day: day,
111 hour: hour,
112 minute: minute,
113 second: second,
114 }
115 }
116
117 pub fn day_of_the_week(&self) -> DayOfTheWeek {
118 const JANUARY_1ST_1970: DayOfTheWeek = DayOfTheWeek::Thursday;
119 JANUARY_1ST_1970.add_days(days_since_unix(self))
120 }
121}
122
123impl<Tz: Default + TimeZone> From<UnixTimestamp> for DateTime<Tz> {
124 fn from(u: UnixTimestamp) -> Self {
125 let tz = Tz::default();
126 DateTime {
127 naive: tz.from_timestamp(u),
128 time_zone: tz,
129 }
130 }
131}
132
133impl<Tz: UnambiguousTimeZone> From<DateTime<Tz>> for UnixTimestamp {
134 fn from(datetime: DateTime<Tz>) -> Self {
135 datetime.time_zone.to_unambiguous_timestamp(&datetime.naive)
136 }
137}
138
139
140#[derive(Debug, Eq, PartialEq, Copy, Clone)]
141pub enum YearKind {
142 Common,
143 Leap,
144}
145
146impl From<i32> for YearKind {
147 fn from(year: i32) -> Self {
148 fn is_multiple(n: i32, divisor: i32) -> bool {
149 n % divisor == 0
150 }
151
152 if is_multiple(year, 4) && (!is_multiple(year, 100) || is_multiple(year, 400)) {
153 YearKind::Leap
154 } else {
155 YearKind::Common
156 }
157 }
158}
159
160macro_rules! declare_month {
161 ([ $((
162 $name: ident,
163 $number: expr,
164 $length_in_common_years: expr,
165 $length_in_leap_years: expr,
166 $first_day_in_common_years: expr,
167 $last_day_in_common_years: expr,
168 $first_day_in_leap_years: expr,
169 $last_day_in_leap_years: expr
170 )),+ ]) => {
171 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
172 pub enum Month {
173 $(
174 $name = $number,
175 )+
176 }
177
178 impl Month {
179 pub fn from_number(n: u8) -> Option<Self> {
181 match n {
182 $(
183 $number => Some(Month::$name),
184 )+
185 _ => None
186 }
187 }
188
189 pub fn to_number(self) -> u8 {
191 match self {
192 $(
193 Month::$name => $number,
194 )+
195 }
196 }
197
198 pub fn length(self, year_kind: YearKind) -> u8 {
199 match year_kind {
200 YearKind::Common => match self {
201 $(
202 Month::$name => $length_in_common_years,
203 )+
204 },
205 YearKind::Leap => match self {
206 $(
207 Month::$name => $length_in_leap_years,
208 )+
209 },
210 }
211 }
212
213 fn days_since_january_1st(self, year_kind: YearKind) -> i32 {
215 match year_kind {
216 YearKind::Common => match self {
217 $(
218 Month::$name => $first_day_in_common_years,
219 )+
220 },
221 YearKind::Leap => match self {
222 $(
223 Month::$name => $first_day_in_leap_years,
224 )+
225 },
226 }
227 }
228
229 fn from_day_of_the_year(day: i32, year_kind: YearKind) -> (Month, u8) {
232 match year_kind {
233 YearKind::Common => match day {
234 $(
235 $first_day_in_common_years ... $last_day_in_common_years => {
236 (Month::$name, (day - $first_day_in_common_years + 1) as u8)
237 }
238 )+
239 _ => panic!("Day #{} of the year is out of range", day)
240 },
241 YearKind::Leap => match day {
242 $(
243 $first_day_in_leap_years ... $last_day_in_leap_years => {
244 (Month::$name, (day - $first_day_in_leap_years + 1) as u8)
245 }
246 )+
247 _ => panic!("Day #{} of the year is out of range", day)
248 },
249 }
250 }
251 }
252 }
253}
254
255macro_rules! declare_day_of_the_week {
256 ([ $((
257 $name: ident,
258 $number: expr
259 )),+ ]) => {
260 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
261 pub enum DayOfTheWeek {
262 $(
263 $name = $number,
264 )+
265 }
266
267 impl DayOfTheWeek {
268 pub fn from_iso_number(n: u8) -> Option<Self> {
271 match n {
272 $(
273 $number => Some(DayOfTheWeek::$name),
274 )+
275 _ => None
276 }
277 }
278
279 pub fn to_iso_number(self) -> u8 {
282 match self {
283 $(
284 DayOfTheWeek::$name => $number,
285 )+
286 }
287 }
288
289 fn add_days(self, days: i32) -> Self {
291 let number = i32::from(self.to_iso_number()) + days;
292 let number = positive_rem((number - 1), 7) + 1; DayOfTheWeek::from_iso_number(number as u8).unwrap()
294 }
295 }
296 }
297}
298
299include!(concat!(env!("OUT_DIR"), "/generated_data.rs"));
300
301with_month_data!(declare_month);
302with_day_of_the_week_data!(declare_day_of_the_week);