1use std::error::Error;
2use chrono::{DateTime, NaiveDateTime, Timelike, Utc};
3use super::{DateFormat, DateValue, FormattedDate, ParseError};
4
5pub type DefaultDateFormat = BasicDateTime;
7
8#[derive(ElasticDateFormat, PartialEq, Debug, Default, Clone, Copy)]
10#[elastic(date_format = "yyyy-MM-dd'T'HH:mm:ssZ")]
11pub struct ChronoFormat;
12
13#[derive(ElasticDateFormat, PartialEq, Debug, Default, Clone, Copy)]
20#[elastic(date_format = "yyyyMMdd'T'HHmmssZ", date_format_name = "basic_date_time_no_millis")]
21pub struct BasicDateTimeNoMillis;
22
23#[derive(ElasticDateFormat, PartialEq, Debug, Default, Clone, Copy)]
30#[elastic(date_format = "yyyyMMdd'T'HHmmss.SSSZ", date_format_name = "basic_date_time")]
31pub struct BasicDateTime;
32
33#[derive(PartialEq, Debug, Default, Clone, Copy)]
43pub struct EpochMillis;
44
45impl DateFormat for EpochMillis {
46 fn name() -> &'static str {
47 "epoch_millis"
48 }
49
50 fn parse(date: &str) -> Result<DateValue, ParseError> {
51 let millis = date.parse::<i64>()
52 .map_err(|e| e.description().to_string())?;
53
54 let (s, m) = {
55 if millis >= 0 {
58 ((millis / 1000), (millis % 1000))
59 }
60 else {
63 ((millis / 1000) - 1, (1000 + (millis % 1000)))
64 }
65 };
66
67 let date = DateTime::from_utc(NaiveDateTime::from_timestamp(s, m as u32 * 1000000), Utc);
68
69 Ok(date.into())
70 }
71
72 fn format<'a>(date: &'a DateValue) -> FormattedDate<'a> {
73 let msec = (date.timestamp() * 1000) + (date.nanosecond() as i64 / 1000000);
74 msec.into()
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use chrono::{DateTime, Utc};
81 use date::{format, parse};
82 use prelude::*;
83
84 #[test]
85 fn chrono() {
86 let date = parse::<DefaultDateMapping<ChronoFormat>>("2015-07-03T14:55:02Z").unwrap();
87
88 assert_eq!(
89 (2015i32, 7u32, 3u32, 14u32, 55u32, 2u32),
90 (
91 date.year(),
92 date.month(),
93 date.day(),
94 date.hour(),
95 date.minute(),
96 date.second()
97 )
98 );
99
100 let fmtd = format(&date).to_string();
101 assert_eq!("2015-07-03T14:55:02Z", &fmtd);
102 }
103
104 #[test]
105 fn chrono_name() {
106 assert_eq!("yyyy-MM-dd'T'HH:mm:ssZ", ChronoFormat::name());
107 }
108
109 #[test]
110 fn basic_datetime_no_millis() {
111 let date = parse::<DefaultDateMapping<BasicDateTimeNoMillis>>("20150703T145502Z").unwrap();
112
113 assert_eq!(
114 (2015i32, 7u32, 3u32, 14u32, 55u32, 2u32),
115 (
116 date.year(),
117 date.month(),
118 date.day(),
119 date.hour(),
120 date.minute(),
121 date.second()
122 )
123 );
124
125 let fmtd = format(&date).to_string();
126 assert_eq!("20150703T145502Z", &fmtd);
127 }
128
129 #[test]
130 fn basic_datetime_no_millis_name() {
131 assert_eq!("basic_date_time_no_millis", BasicDateTimeNoMillis::name());
132 }
133
134 #[test]
135 fn basic_date_time() {
136 let date = parse::<DefaultDateMapping<BasicDateTime>>("20150703T145502.478Z").unwrap();
137
138 assert_eq!(
139 (2015i32, 7u32, 3u32, 14u32, 55u32, 2u32, 478u32),
140 (
141 date.year(),
142 date.month(),
143 date.day(),
144 date.hour(),
145 date.minute(),
146 date.second(),
147 date.nanosecond() / 1000000
148 )
149 );
150
151 let fmtd = format(&date).to_string();
152 assert_eq!("20150703T145502.478Z", &fmtd);
153 }
154
155 #[test]
156 fn basic_date_time_name() {
157 assert_eq!("basic_date_time", BasicDateTime::name());
158 }
159
160 #[test]
161 fn epoch_millis() {
162 let date = parse::<DefaultDateMapping<EpochMillis>>("1435935302478").unwrap();
163
164 assert_eq!(
165 (2015i32, 7u32, 3u32, 14u32, 55u32, 2u32, 478u32),
166 (
167 date.year(),
168 date.month(),
169 date.day(),
170 date.hour(),
171 date.minute(),
172 date.second(),
173 date.nanosecond() / 1000000
174 )
175 );
176
177 let fmtd = format(&date).to_string();
178 assert_eq!("1435935302478", &fmtd);
179 }
180
181 #[test]
182 fn epoch_millis_name() {
183 assert_eq!("epoch_millis", EpochMillis::name());
184 }
185
186 #[test]
187 fn epoch_millis_no_millis() {
188 let date = parse::<DefaultDateMapping<EpochMillis>>("1435935302000").unwrap();
189
190 assert_eq!(
191 (2015i32, 7u32, 3u32, 14u32, 55u32, 2u32, 0u32),
192 (
193 date.year(),
194 date.month(),
195 date.day(),
196 date.hour(),
197 date.minute(),
198 date.second(),
199 date.nanosecond() / 1000000
200 )
201 );
202
203 let fmtd = format(&date).to_string();
204 assert_eq!("1435935302000", &fmtd);
205 }
206
207 #[test]
208 fn epoch_millis_minus() {
209 let date = parse::<DefaultDateMapping<EpochMillis>>("-8031171898478").unwrap();
210
211 assert_eq!(
212 (1715i32, 7u32, 3u32, 14u32, 55u32, 1u32, 522u32),
213 (
214 date.year(),
215 date.month(),
216 date.day(),
217 date.hour(),
218 date.minute(),
219 date.second(),
220 date.nanosecond() / 1000000
221 )
222 );
223
224 let fmtd = format(&date).to_string();
225 assert_eq!("-8031171898478", &fmtd);
226 }
227
228 #[test]
229 fn epoch_millis_minus_no_millis() {
230 let date = parse::<DefaultDateMapping<EpochMillis>>("-8031171898000").unwrap();
231
232 assert_eq!(
233 (1715i32, 7u32, 3u32, 14u32, 55u32, 1u32, 1000u32),
234 (
235 date.year(),
236 date.month(),
237 date.day(),
238 date.hour(),
239 date.minute(),
240 date.second(),
241 date.nanosecond() / 1000000
242 )
243 );
244
245 let fmtd = format(&date).to_string();
246 assert_eq!("-8031171898000", &fmtd);
247 }
248
249 #[test]
250 fn epoch_millis_very_short() {
251 let date = parse::<DefaultDateMapping<EpochMillis>>("100").unwrap();
252
253 assert_eq!(
254 (1970i32, 1u32, 1u32, 0u32, 0u32, 0u32, 100u32),
255 (
256 date.year(),
257 date.month(),
258 date.day(),
259 date.hour(),
260 date.minute(),
261 date.second(),
262 date.nanosecond() / 1000000
263 )
264 );
265
266 let fmtd = format(&date).to_string();
267 assert_eq!("100", &fmtd);
268 }
269
270 #[test]
271 fn epoch_millis_short() {
272 let date = parse::<DefaultDateMapping<EpochMillis>>("5100").unwrap();
273
274 assert_eq!(
275 (1970i32, 1u32, 1u32, 0u32, 0u32, 5u32, 100u32),
276 (
277 date.year(),
278 date.month(),
279 date.day(),
280 date.hour(),
281 date.minute(),
282 date.second(),
283 date.nanosecond() / 1000000
284 )
285 );
286
287 let fmtd = format(&date).to_string();
288 assert_eq!("5100", &fmtd);
289 }
290
291 #[test]
292 fn epoch_millis_very_short_minus() {
293 let date = parse::<DefaultDateMapping<EpochMillis>>("-100").unwrap();
294
295 assert_eq!(
296 (1969i32, 12u32, 31u32, 23u32, 59u32, 59u32, 900u32),
297 (
298 date.year(),
299 date.month(),
300 date.day(),
301 date.hour(),
302 date.minute(),
303 date.second(),
304 date.nanosecond() / 1000000
305 )
306 );
307
308 let fmtd = format(&date).to_string();
309 assert_eq!("-100", &fmtd);
310 }
311
312 #[test]
313 fn epoch_millis_short_minus() {
314 let date = parse::<DefaultDateMapping<EpochMillis>>("-5100").unwrap();
315
316 assert_eq!(
317 (1969i32, 12u32, 31u32, 23u32, 59u32, 54u32, 900u32),
318 (
319 date.year(),
320 date.month(),
321 date.day(),
322 date.hour(),
323 date.minute(),
324 date.second(),
325 date.nanosecond() / 1000000
326 )
327 );
328
329 let fmtd = format(&date).to_string();
330 assert_eq!("-5100", &fmtd);
331 }
332
333 #[test]
334 fn epoch_millis_zero() {
335 let date = parse::<DefaultDateMapping<EpochMillis>>("0").unwrap();
336
337 assert_eq!(
338 (1970i32, 1u32, 1u32, 0u32, 0u32, 0u32, 0u32),
339 (
340 date.year(),
341 date.month(),
342 date.day(),
343 date.hour(),
344 date.minute(),
345 date.second(),
346 date.nanosecond() / 1000000
347 )
348 );
349
350 let fmtd = format(&date).to_string();
351 assert_eq!("0", &fmtd);
352 }
353
354 #[test]
355 fn custom_format() {
356 #[derive(Default)]
357 struct MyCustomFormat;
358 impl DateFormat for MyCustomFormat {
359 fn name() -> &'static str {
360 "yyyy-MM-dd'T'HH:mm:ssZ"
361 }
362
363 fn format<'a>(date: &'a DateValue) -> FormattedDate<'a> {
364 date.to_rfc3339().into()
365 }
366
367 fn parse(date: &str) -> Result<DateValue, ParseError> {
368 let date = DateTime::parse_from_rfc3339(date).map_err(|e| ParseError::from(e))?;
369
370 Ok(DateTime::from_utc(date.naive_local(), Utc).into())
371 }
372 }
373
374 let date = parse::<DefaultDateMapping<MyCustomFormat>>("2015-07-03T14:55:02+00:00").unwrap();
375
376 assert_eq!(
377 (2015i32, 7u32, 3u32, 14u32, 55u32, 2u32),
378 (
379 date.year(),
380 date.month(),
381 date.day(),
382 date.hour(),
383 date.minute(),
384 date.second()
385 )
386 );
387
388 let fmtd = format(&date).to_string();
389 assert_eq!("2015-07-03T14:55:02+00:00", &fmtd);
390 }
391}