1use core::ops::{Add, Sub};
7use core::fmt;
8use oldtime::Duration as OldDuration;
9
10use Timelike;
11use div::div_mod_floor;
12use naive::{NaiveTime, NaiveDate, NaiveDateTime};
13use DateTime;
14use super::{TimeZone, Offset, LocalResult};
15
16#[derive(PartialEq, Eq, Hash, Copy, Clone)]
23pub struct FixedOffset {
24 local_minus_utc: i32,
25}
26
27impl FixedOffset {
28 pub fn east(secs: i32) -> FixedOffset {
43 FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds")
44 }
45
46 pub fn east_opt(secs: i32) -> Option<FixedOffset> {
51 if -86_400 < secs && secs < 86_400 {
52 Some(FixedOffset { local_minus_utc: secs })
53 } else {
54 None
55 }
56 }
57
58 pub fn west(secs: i32) -> FixedOffset {
73 FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds")
74 }
75
76 pub fn west_opt(secs: i32) -> Option<FixedOffset> {
81 if -86_400 < secs && secs < 86_400 {
82 Some(FixedOffset { local_minus_utc: -secs })
83 } else {
84 None
85 }
86 }
87
88 #[inline]
90 pub fn local_minus_utc(&self) -> i32 {
91 self.local_minus_utc
92 }
93
94 #[inline]
96 pub fn utc_minus_local(&self) -> i32 {
97 -self.local_minus_utc
98 }
99}
100
101impl TimeZone for FixedOffset {
102 type Offset = FixedOffset;
103
104 fn from_offset(offset: &FixedOffset) -> FixedOffset { *offset }
105
106 fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<FixedOffset> {
107 LocalResult::Single(*self)
108 }
109 fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<FixedOffset> {
110 LocalResult::Single(*self)
111 }
112
113 fn offset_from_utc_date(&self, _utc: &NaiveDate) -> FixedOffset { *self }
114 fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> FixedOffset { *self }
115}
116
117impl Offset for FixedOffset {
118 fn fix(&self) -> FixedOffset { *self }
119}
120
121impl fmt::Debug for FixedOffset {
122 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
123 let offset = self.local_minus_utc;
124 let (sign, offset) = if offset < 0 {('-', -offset)} else {('+', offset)};
125 let (mins, sec) = div_mod_floor(offset, 60);
126 let (hour, min) = div_mod_floor(mins, 60);
127 if sec == 0 {
128 write!(f, "{}{:02}:{:02}", sign, hour, min)
129 } else {
130 write!(f, "{}{:02}:{:02}:{:02}", sign, hour, min, sec)
131 }
132 }
133}
134
135impl fmt::Display for FixedOffset {
136 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) }
137}
138
139fn add_with_leapsecond<T>(lhs: &T, rhs: i32) -> T
145 where T: Timelike + Add<OldDuration, Output=T>
146{
147 let nanos = lhs.nanosecond();
149 let lhs = lhs.with_nanosecond(0).unwrap();
150 (lhs + OldDuration::seconds(i64::from(rhs))).with_nanosecond(nanos).unwrap()
151}
152
153impl Add<FixedOffset> for NaiveTime {
154 type Output = NaiveTime;
155
156 #[inline]
157 fn add(self, rhs: FixedOffset) -> NaiveTime {
158 add_with_leapsecond(&self, rhs.local_minus_utc)
159 }
160}
161
162impl Sub<FixedOffset> for NaiveTime {
163 type Output = NaiveTime;
164
165 #[inline]
166 fn sub(self, rhs: FixedOffset) -> NaiveTime {
167 add_with_leapsecond(&self, -rhs.local_minus_utc)
168 }
169}
170
171impl Add<FixedOffset> for NaiveDateTime {
172 type Output = NaiveDateTime;
173
174 #[inline]
175 fn add(self, rhs: FixedOffset) -> NaiveDateTime {
176 add_with_leapsecond(&self, rhs.local_minus_utc)
177 }
178}
179
180impl Sub<FixedOffset> for NaiveDateTime {
181 type Output = NaiveDateTime;
182
183 #[inline]
184 fn sub(self, rhs: FixedOffset) -> NaiveDateTime {
185 add_with_leapsecond(&self, -rhs.local_minus_utc)
186 }
187}
188
189impl<Tz: TimeZone> Add<FixedOffset> for DateTime<Tz> {
190 type Output = DateTime<Tz>;
191
192 #[inline]
193 fn add(self, rhs: FixedOffset) -> DateTime<Tz> {
194 add_with_leapsecond(&self, rhs.local_minus_utc)
195 }
196}
197
198impl<Tz: TimeZone> Sub<FixedOffset> for DateTime<Tz> {
199 type Output = DateTime<Tz>;
200
201 #[inline]
202 fn sub(self, rhs: FixedOffset) -> DateTime<Tz> {
203 add_with_leapsecond(&self, -rhs.local_minus_utc)
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use offset::TimeZone;
210 use super::FixedOffset;
211
212 #[test]
213 fn test_date_extreme_offset() {
214 assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29)),
217 "2012-02-29+23:59:59".to_string());
218 assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29).and_hms(5, 6, 7)),
219 "2012-02-29T05:06:07+23:59:59".to_string());
220 assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4)),
221 "2012-03-04-23:59:59".to_string());
222 assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4).and_hms(5, 6, 7)),
223 "2012-03-04T05:06:07-23:59:59".to_string());
224 }
225}
226