dayjs/lib.rs
1use chrono::{
2 DateTime, Datelike, FixedOffset, Local, Offset, TimeZone as CTimeZone, Timelike, Utc, Weekday,
3};
4use std::fmt::{Display, Formatter};
5
6/// re-export chrono
7pub use chrono;
8
9/// TimeZone enum for representing different time zone formats.
10///
11/// It can represent:
12/// - Time zone offset as a string (e.g., "+08:00")
13/// - Time zone city as a string (e.g., "Asia/Shanghai")
14/// - Time zone number as an integer (-12 to +12)
15///
16/// # Examples
17///
18/// ```
19/// use dayjs::TimeZone;
20///
21/// let tz_offset = TimeZone::TimeZoneTime("+08:00".to_string());
22///
23/// let tz_city = TimeZone::TimeZoneCity("Asia/Shanghai".to_string());
24///
25/// let tz_number = TimeZone::TimeZoneNumber(8);
26///
27#[derive(Clone, Debug, PartialEq)]
28pub enum TimeZone {
29 // 时区偏移, 如: "+08:00"
30 TimeZoneTime(String),
31 // 时区城市, 如: "Asia/Shanghai"
32 TimeZoneCity(String),
33 // 时区编号, -12 ~ +12
34 TimeZoneNumber(i8),
35}
36
37impl TimeZone {
38 /// Get the current time zone as a string.
39 pub fn current() -> Self {
40 let offset = Local::now().offset().fix();
41 TimeZone::TimeZoneTime(offset.to_string())
42 }
43}
44
45/// Dayjs struct representing a date and time with a time zone.
46///
47/// It contains:
48/// - `tz`: The time zone information as a `TimeZone` enum.
49/// - `time`: The UTC time as a `DateTime<Utc>`.
50///
51/// # Examples
52/// ```
53/// use dayjs::{dayjs, Dayjs, TimeZone};
54/// let now = dayjs();
55/// ```
56#[derive(Clone, Debug, PartialEq)]
57pub struct Dayjs {
58 /// Time zone information
59 pub(crate) tz: TimeZone,
60 /// UTC time
61 pub(crate) time: DateTime<Utc>,
62}
63
64impl Default for Dayjs {
65 fn default() -> Self {
66 Dayjs {
67 tz: TimeZone::current(),
68 time: Utc::now(),
69 }
70 }
71}
72
73/// UTC Timestamp, eg: 143164800
74///
75/// # Examples
76/// ```
77/// use dayjs::timestamp;
78/// let ts = timestamp();
79/// println!("Current UTC timestamp: {}", ts);
80/// ```
81///
82pub fn timestamp() -> i64 {
83 Utc::now().timestamp()
84}
85
86/// Create a new Dayjs instance with the current time.
87///
88/// # Examples
89/// ```
90/// use dayjs::dayjs;
91/// let now = dayjs();
92/// println!("Current time: {}", now);
93/// ```
94pub fn dayjs() -> Dayjs {
95 Dayjs::default()
96}
97
98/// Create a new Dayjs instance with the current time.
99/// This is an alias for `dayjs()`.
100/// # Examples
101/// ```
102/// use dayjs::now;
103/// let current_time = now();
104/// println!("Current time: {}", current_time);
105/// ```
106pub fn now() -> Dayjs {
107 Dayjs::default()
108}
109
110/// Get Dayjs instance from a string representation of date time.
111///
112/// # Parameters
113/// - `s`: A string representing the date time, which can be in various formats such as ISO 8601, RFC 3339, or RFC 2822.
114/// # Returns
115/// - `Ok(Dayjs)`: If the string is successfully parsed into a `DateTime<Utc>`.
116/// - `Err(String)`: If the string cannot be parsed, an error message is returned.
117///
118/// # Examples
119/// ```
120/// use dayjs::from_str;
121/// let date_str = "2023-10-01T12:00:00Z";
122/// let dayjs_instance = from_str(date_str);
123/// match dayjs_instance {
124/// Ok(dayjs) => println!("Parsed Dayjs: {}", dayjs),
125/// Err(e) => println!("Error parsing date: {}", e),
126/// }
127/// ```
128pub fn from_str(s: &str) -> Result<Dayjs, String> {
129 let time: DateTime<Utc> =
130 parse_date_time(s).ok_or_else(|| format!("Failed to parse date time from string: {s}"))?;
131 Ok(Dayjs {
132 time,
133 ..Default::default()
134 })
135}
136
137/// Get Dayjs instance from an integer timestamp.
138/// # Parameters
139/// - `n`: An integer representing the timestamp, which can be in seconds (10 digits) or milliseconds (13 digits).
140/// # Returns
141/// - `Ok(Dayjs)`: If the integer is successfully converted to a `DateTime<Utc>`.
142/// - `Err(String)`: If the integer is not a valid timestamp or does not match the expected length (10 or 13 digits).
143///
144/// # Examples
145/// ```
146/// use dayjs::from_int64;
147/// let timestamp = 1633072800; // Example timestamp in seconds
148/// let dayjs_instance = from_int64(timestamp);
149/// match dayjs_instance {
150/// Ok(dayjs) => println!("Parsed Dayjs: {}", dayjs),
151/// Err(e) => println!("Error parsing timestamp: {}", e),
152/// }
153/// ```
154pub fn from_int64(n: i64) -> Result<Dayjs, String> {
155 let len = format!("{n}").len();
156 match len {
157 10 => {
158 let r = Utc.timestamp_opt(n, 0);
159 let r = r
160 .single()
161 .ok_or_else(|| format!("{n} is not a valid timestamp"))?;
162 Ok(Dayjs {
163 time: r,
164 ..Default::default()
165 })
166 }
167 13 => {
168 let r = Utc.timestamp_millis_opt(n);
169 let r = r
170 .single()
171 .ok_or_else(|| format!("{n} is not a valid timestamp"))?;
172 Ok(Dayjs {
173 time: r,
174 ..Default::default()
175 })
176 }
177 _ => Err(format!("{n} is not a safe time number"))?,
178 }
179}
180
181/// Get the current time zone of the Dayjs instance.
182///
183/// # Parameters
184/// - `tz`: A `TimeZone` enum representing the desired time zone.
185/// # Returns
186/// A `Dayjs` instance with the specified time zone and the current UTC time.
187///
188/// # Examples
189/// ```
190/// use dayjs::{from_timezone, TimeZone};
191/// let tz = TimeZone::TimeZoneTime("+08:00".to_string());
192/// let dayjs_instance = from_timezone(tz);
193/// println!("Current time in specified timezone: {}", dayjs_instance);
194/// ```
195pub fn from_timezone(tz: TimeZone) -> Dayjs {
196 Dayjs {
197 tz,
198 time: Utc::now(),
199 }
200}
201
202/// Parse date time string, supports ISO 8601 format (with timezone offset and 'Z' suffix) and UTC time
203///
204/// # Parameters
205/// - `s`: The date time string to be parsed
206///
207/// # Returns
208/// Returns `DateTime<Utc>` on successful parsing, `None` on failure
209pub fn parse_date_time(s: &str) -> Option<DateTime<Utc>> {
210 if s.ends_with("UTC") || s.ends_with("utc") {
211 let s = s.replace("UTC", "").replace("utc", "");
212 let s = s.trim_end();
213 let s = format!("{} {}", s, "+00:00");
214 if let Ok(dt) = DateTime::parse_from_str(&s, "%Y-%m-%d %H:%M:%S %:z") {
215 return Some(dt.with_timezone(&Utc));
216 }
217 }
218 if let Ok(dt) = DateTime::parse_from_rfc3339(s) {
219 return Some(dt.with_timezone(&Utc));
220 }
221 if let Ok(dt) = DateTime::parse_from_rfc2822(s) {
222 return Some(dt.with_timezone(&Utc));
223 }
224 if let Some(offset) = FixedOffset::east_opt(0) {
225 if let Ok(dt) = DateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S%.f %z") {
226 return Some(dt.with_timezone(&offset).with_timezone(&Utc));
227 }
228 if let Ok(dt) = DateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.fZ") {
229 return Some(dt.with_timezone(&offset).with_timezone(&Utc));
230 }
231 if let Ok(dt) = DateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S %:z") {
232 return Some(dt.with_timezone(&offset).with_timezone(&Utc));
233 }
234 let s = format!("{} {}", s, "+00:00");
235 if let Ok(dt) = DateTime::parse_from_str(&s, "%Y-%m-%d %H:%M:%S %:z") {
236 return Some(dt.with_timezone(&offset).with_timezone(&Utc));
237 }
238 }
239 None
240}
241
242impl Display for Dayjs {
243 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
244 let s = self.time.to_utc();
245 let v = s.to_string();
246 write!(f, "{v}")
247 }
248}
249
250impl Dayjs {
251 /// Format the date time according to the given template.
252 ///
253 /// # Parameters
254 /// - `template`: %Y-%m-%d %H:%M:%S
255 ///
256 /// # Examples
257 /// ```
258 /// let now = dayjs::dayjs();
259 /// let formatted = now.format("%Y-%m-%d %H:%M:%S");
260 /// println!("{}", formatted);
261 /// // 2025-03-25 17:21:47
262 /// ```
263 ///
264 pub fn format(&self, template: &str) -> String {
265 self.time.format(template).to_string()
266 }
267
268 /// Set the time zone for the Dayjs instance.
269 pub fn set_timezone(&mut self, tz: TimeZone) {
270 self.tz = tz;
271 }
272
273 /// Get the current time zone of the Dayjs instance.
274 pub fn get_timezone(&self) -> &TimeZone {
275 &self.tz
276 }
277
278 /// Get the current time in UTC.
279 pub fn get_timestamp(&self) -> i64 {
280 self.time.timestamp()
281 }
282
283 /// Get the current time in second.
284 pub fn timestamp(&self) -> i64 {
285 self.time.timestamp()
286 }
287
288 /// Get the current time in milliseconds since the Unix epoch.
289 pub fn millisecond(&self) -> i64 {
290 self.time.timestamp_millis()
291 }
292
293 /// Get the current time in seconds since the Unix epoch.
294 pub fn second(&self) -> i64 {
295 self.time.timestamp()
296 }
297
298 /// Get the current time in nanoseconds since the Unix epoch.
299 pub fn minute(&self) -> u32 {
300 self.time.minute()
301 }
302
303 /// Get the current time in hours since the Unix epoch.
304 pub fn hour(&self) -> u32 {
305 self.time.hour()
306 }
307
308 /// Get the date of month 1 ~ 31
309 pub fn date(&self) -> u32 {
310 self.time.day()
311 }
312
313 /// Get the week number 1 ~ 7
314 pub fn day(&self) -> Weekday {
315 self.time.weekday()
316 }
317
318 /// Get the month number 1 ~ 366
319 pub fn day_of_year(&self) -> u32 {
320 self.time.ordinal()
321 }
322
323 /// Get the week number 1 ~ 53
324 pub fn week_of_year(&self) -> u32 {
325 self.time.iso_week().week()
326 }
327
328 /// Get the month number 1 ~ 12
329 pub fn month_of_year(&self) -> u32 {
330 self.time.month()
331 }
332}
333
334/// Trait for displaying time in various formats.
335pub trait DisplayTime {
336 /// Formats to array string. [ 2019, 0, 25, 0, 0, 0, 0 ]
337 fn to_array(&self) -> String;
338
339 /// Formats to iso string. "2019-01-25T02:00:00.000Z"
340 fn to_iso(&self) -> String;
341
342 /// Formats to utc string. "2019-01-25 00:00:00 +00:00"
343 fn to_utc(&self) -> String;
344
345 /// Formats to gmt string. "Fri, 25 Jan 2019 00:00:00 GMT"
346 fn to_gmt(&self) -> String;
347
348 /// Converts the time to a timestamp in seconds.
349 fn to_timestamp(&self) -> i64;
350}
351
352impl DisplayTime for Dayjs {
353 /// Formats the date time to an array string.
354 fn to_array(&self) -> String {
355 let dt = self.time;
356 format!(
357 "[ {}, {}, {}, {}, {}, {}, {} ]",
358 dt.year(),
359 dt.month0(),
360 dt.day0(),
361 dt.hour(),
362 dt.minute(),
363 dt.second(),
364 dt.nanosecond() / 1_000_000
365 )
366 }
367
368 /// Formats the date time to an ISO 8601 string.
369 fn to_iso(&self) -> String {
370 let dt = self.time;
371 format!(
372 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03}Z",
373 dt.year(),
374 dt.month(),
375 dt.day(),
376 dt.hour(),
377 dt.minute(),
378 dt.second(),
379 dt.nanosecond() / 1_000_000
380 )
381 }
382
383 /// Formats the date time to a UTC string.
384 fn to_utc(&self) -> String {
385 let dt = self.time;
386 format!(
387 "{:04}-{:02}-{:02} {:02}:{:02}:{:02} +00:00",
388 dt.year(),
389 dt.month(),
390 dt.day(),
391 dt.hour(),
392 dt.minute(),
393 dt.second()
394 )
395 }
396
397 /// Formats the date time to a GMT string.
398 fn to_gmt(&self) -> String {
399 let dt = self.time;
400 format!(
401 "{}, {:02} {} {:04} {:02}:{:02}:{:02} GMT",
402 dt.weekday(),
403 dt.day(),
404 dt.format("%b"),
405 dt.year(),
406 dt.hour(),
407 dt.minute(),
408 dt.second()
409 )
410 }
411
412 /// Converts the time to a timestamp in seconds.
413 fn to_timestamp(&self) -> i64 {
414 let dt = self.time;
415 dt.timestamp()
416 }
417}
418
419/// Trait for operations on time, such as adding or subtracting durations.
420pub trait OperationTime {
421 /// Add a duration to the current time.
422 fn add(&mut self, timestamp: i32);
423
424 /// Add a duration to the current time in year.
425 fn add_years(&mut self, years: i32);
426
427 /// Add a duration to the current time in month.
428 fn add_months(&mut self, months: i32);
429
430 /// Add a duration to the current time in week.
431 fn add_weeks(&mut self, weeks: i32);
432
433 /// Add a duration to the current time in day.
434 fn add_days(&mut self, days: i32);
435
436 /// Add a duration to the current time in hours.
437 fn add_hours(&mut self, hours: i32);
438
439 /// Add a duration to the current time in minutes.
440 fn add_minutes(&mut self, minutes: i32);
441
442 /// Add a duration to the current time in seconds.
443 fn add_seconds(&mut self, seconds: i32);
444
445 /// Add a duration to the current time in milliseconds.
446 fn add_milliseconds(&mut self, milliseconds: i32);
447
448 /// Subtract a duration from the current time.
449 fn subtract(&mut self, timestamp: i32);
450
451 /// Subtract a duration from the current time in year.
452 fn subtract_years(&mut self, years: i32);
453
454 /// Subtract a duration from the current time in month.
455 fn subtract_months(&mut self, months: i32);
456
457 /// Subtract a duration from the current time in week.
458 fn subtract_weeks(&mut self, weeks: i32);
459
460 /// Subtract a duration from the current time in day.
461 fn subtract_days(&mut self, days: i32);
462
463 /// Subtract a duration from the current time in hours.
464 fn subtract_hours(&mut self, hours: i32);
465
466 /// Subtract a duration from the current time in minutes.
467 fn subtract_minutes(&mut self, minutes: i32);
468
469 /// Subtract a duration from the current time in seconds.
470 fn subtract_seconds(&mut self, seconds: i32);
471
472 /// Subtract a duration from the current time in milliseconds.
473 fn subtract_milliseconds(&mut self, milliseconds: i32);
474}
475
476impl OperationTime for Dayjs {
477 /// Add a duration to the current time.
478 fn add(&mut self, timestamp: i32) {
479 let dt = self.time + chrono::Duration::seconds(timestamp as i64);
480 self.time = dt;
481 }
482
483 /// Add a duration to the current time in year.
484 fn add_years(&mut self, years: i32) {
485 let dt = self.time + chrono::Duration::days((years * 365) as i64);
486 self.time = dt;
487 }
488
489 /// Add a duration to the current time in month.
490 fn add_months(&mut self, months: i32) {
491 let mut dt = self.time;
492 for _ in 0..months {
493 dt = dt.with_month(dt.month() + 1).unwrap_or(dt);
494 }
495 self.time = dt;
496 }
497
498 /// Add a duration to the current time in week.
499 fn add_weeks(&mut self, weeks: i32) {
500 let dt = self.time + chrono::Duration::weeks(weeks as i64);
501 self.time = dt;
502 }
503
504 /// Add a duration to the current time in day.
505 fn add_days(&mut self, days: i32) {
506 let dt = self.time + chrono::Duration::days(days as i64);
507 self.time = dt;
508 }
509
510 /// Add a duration to the current time in hours.
511 fn add_hours(&mut self, hours: i32) {
512 let dt = self.time + chrono::Duration::hours(hours as i64);
513 self.time = dt;
514 }
515
516 /// Add a duration to the current time in minutes.
517 fn add_minutes(&mut self, minutes: i32) {
518 let dt = self.time + chrono::Duration::minutes(minutes as i64);
519 self.time = dt;
520 }
521
522 /// Add a duration to the current time in seconds.
523 fn add_seconds(&mut self, seconds: i32) {
524 let dt = self.time + chrono::Duration::seconds(seconds as i64);
525 self.time = dt;
526 }
527
528 /// Add a duration to the current time in milliseconds.
529 fn add_milliseconds(&mut self, milliseconds: i32) {
530 let dt = self.time + chrono::Duration::milliseconds(milliseconds as i64);
531 self.time = dt;
532 }
533
534 /// Subtract a duration from the current time.
535 fn subtract(&mut self, timestamp: i32) {
536 let dt = self.time - chrono::Duration::seconds(timestamp as i64);
537 self.time = dt;
538 }
539
540 /// Subtract a duration from the current time in year.
541 fn subtract_years(&mut self, years: i32) {
542 let dt = self.time - chrono::Duration::days(years as i64);
543 self.time = dt;
544 }
545
546 /// Subtract a duration from the current time in month.
547 fn subtract_months(&mut self, months: i32) {
548 let mut dt = self.time;
549 for _ in 0..months {
550 dt = dt.with_month(dt.month() - 1).unwrap_or(dt);
551 }
552 self.time = dt;
553 }
554
555 /// Subtract a duration from the current time in week.
556 fn subtract_weeks(&mut self, weeks: i32) {
557 let dt = self.time - chrono::Duration::weeks(weeks as i64);
558 self.time = dt;
559 }
560
561 /// Subtract a duration from the current time in day.
562 fn subtract_days(&mut self, days: i32) {
563 let dt = self.time - chrono::Duration::days(days as i64);
564 self.time = dt;
565 }
566
567 /// Subtract a duration from the current time in hours.
568 fn subtract_hours(&mut self, hours: i32) {
569 let dt = self.time - chrono::Duration::hours(hours as i64);
570 self.time = dt;
571 }
572
573 /// Subtract a duration from the current time in minutes.
574 fn subtract_minutes(&mut self, minutes: i32) {
575 let dt = self.time - chrono::Duration::minutes(minutes as i64);
576 self.time = dt;
577 }
578
579 /// Subtract a duration from the current time in seconds.
580 fn subtract_seconds(&mut self, seconds: i32) {
581 let dt = self.time - chrono::Duration::seconds(seconds as i64);
582 self.time = dt;
583 }
584
585 /// Subtract a duration from the current time in milliseconds.
586 fn subtract_milliseconds(&mut self, milliseconds: i32) {
587 let dt = self.time - chrono::Duration::milliseconds(milliseconds as i64);
588 self.time = dt;
589 }
590}