1use chrono::{DateTime, Datelike, Duration, Local, NaiveDate, NaiveDateTime, TimeZone, Timelike};
2use once_cell::sync::Lazy;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum DateUnit {
7 MS, SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR, }
16
17pub enum Level {
19 YEAR,
20 MONTH,
21 DAY,
22 HOUR,
23 MINUTE,
24 SECOND,
25 MILLISECOND,
26}
27
28pub const CHINESE_ZODIAC: [&str; 12] = [
30 "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪",
31];
32
33pub const ZODIAC: [&str; 12] = [
35 "水瓶座",
36 "双鱼座",
37 "白羊座",
38 "金牛座",
39 "双子座",
40 "巨蟹座",
41 "狮子座",
42 "处女座",
43 "天秤座",
44 "天蝎座",
45 "射手座",
46 "摩羯座",
47];
48pub const ZODIAC_DATE: [u32; 12] = [20, 19, 21, 20, 21, 22, 23, 23, 23, 24, 23, 22];
49
50static DATE_FORMATS: Lazy<Vec<&'static str>> = Lazy::new(|| {
51 vec![
52 "%Y-%m-%d %H:%M:%S",
53 "%Y/%m/%d %H:%M:%S",
54 "%Y.%m.%d %H:%M:%S",
55 "%Y年%m月%d日 %H时%M分%S秒",
56 "%Y-%m-%d",
57 "%Y/%m/%d",
58 "%Y.%m.%d",
59 "%H:%M:%S",
60 "%H时%M分%S秒",
61 "%Y-%m-%d %H:%M",
62 "%Y-%m-%d %H:%M:%S.%3f",
63 "%Y%m%d%H%M%S",
64 "%Y%m%d%H%M%S%.3f",
65 "%Y%m%d",
66 "%a, %d %b %Y %H:%M:%S %Z",
67 "%a %b %d %H:%M:%S %Z %Y",
68 "%Y-%m-%dT%H:%M:%SZ",
69 "%Y-%m-%dT%H:%M:%S.%3fZ",
70 "%Y-%m-%dT%H:%M:%S%z",
71 "%Y-%m-%dT%H:%M:%S.%3f%z",
72 ]
73});
74
75pub struct DateUtil;
76
77impl DateUtil {
78 pub fn date() -> DateTime<Local> {
80 Local::now()
81 }
82
83 pub fn date_ms(timestamp_ms: i64) -> DateTime<Local> {
85 let seconds = timestamp_ms / 1000;
86 let nanos = ((timestamp_ms % 1000) * 1_000_000) as u32;
87 Local.timestamp_opt(seconds, nanos).unwrap()
88 }
89
90 pub fn now() -> String {
92 Self::format(Self::date(), "%Y-%m-%d %H:%M:%S")
93 }
94
95 pub fn today() -> String {
97 Self::format(Self::date(), "%Y-%m-%d")
98 }
99
100 pub fn parse(date_str: &str) -> Option<DateTime<Local>> {
102 for format in DATE_FORMATS.iter() {
103 if let Ok(dt) = NaiveDateTime::parse_from_str(date_str, format) {
104 return Some(Local.from_local_datetime(&dt).unwrap());
105 }
106
107 if let Ok(date) = NaiveDate::parse_from_str(date_str, format) {
109 let dt = date.and_hms_opt(0, 0, 0).unwrap();
110 return Some(Local.from_local_datetime(&dt).unwrap());
111 }
112 }
113 None
114 }
115
116 pub fn parse_with_format(date_str: &str, format: &str) -> Option<DateTime<Local>> {
118 if let Ok(dt) = NaiveDateTime::parse_from_str(date_str, format) {
119 return Some(Local.from_local_datetime(&dt).unwrap());
120 }
121
122 if let Ok(date) = NaiveDate::parse_from_str(date_str, format) {
123 let dt = date.and_hms_opt(0, 0, 0).unwrap();
124 return Some(Local.from_local_datetime(&dt).unwrap());
125 }
126
127 None
128 }
129
130 pub fn format(date: DateTime<Local>, fmt: &str) -> String {
132 date.format(fmt).to_string()
133 }
134
135 pub fn format_date(date: DateTime<Local>) -> String {
137 Self::format(date, "%Y-%m-%d")
138 }
139
140 pub fn format_datetime(date: DateTime<Local>) -> String {
142 Self::format(date, "%Y-%m-%d %H:%M:%S")
143 }
144
145 pub fn format_time(date: DateTime<Local>) -> String {
147 Self::format(date, "%H:%M:%S")
148 }
149
150 pub fn year(date: DateTime<Local>) -> i32 {
152 date.year()
153 }
154
155 pub fn month(date: DateTime<Local>) -> u32 {
157 date.month()
158 }
159
160 pub fn day(date: DateTime<Local>) -> u32 {
162 date.day()
163 }
164
165 pub fn hour(date: DateTime<Local>) -> u32 {
167 date.hour()
168 }
169
170 pub fn minute(date: DateTime<Local>) -> u32 {
172 date.minute()
173 }
174
175 pub fn second(date: DateTime<Local>) -> u32 {
177 date.second()
178 }
179
180 pub fn day_of_week(date: DateTime<Local>) -> u32 {
182 date.weekday().number_from_monday()
183 }
184
185 pub fn begin_of_day(date: DateTime<Local>) -> DateTime<Local> {
187 date.date_naive()
188 .and_hms_opt(0, 0, 0)
189 .unwrap()
190 .and_local_timezone(Local)
191 .unwrap()
192 }
193
194 pub fn end_of_day(date: DateTime<Local>) -> DateTime<Local> {
196 date.date_naive()
197 .and_hms_opt(23, 59, 59)
198 .unwrap()
199 .and_local_timezone(Local)
200 .unwrap()
201 }
202
203 pub fn begin_of_month(date: DateTime<Local>) -> DateTime<Local> {
205 let naive_date = NaiveDate::from_ymd_opt(date.year(), date.month(), 1).unwrap();
206 naive_date
207 .and_hms_opt(0, 0, 0)
208 .unwrap()
209 .and_local_timezone(Local)
210 .unwrap()
211 }
212
213 pub fn end_of_month(date: DateTime<Local>) -> DateTime<Local> {
215 let last_day = if date.month() == 12 {
216 NaiveDate::from_ymd_opt(date.year() + 1, 1, 1).unwrap()
217 } else {
218 NaiveDate::from_ymd_opt(date.year(), date.month() + 1, 1).unwrap()
219 }
220 .pred_opt()
221 .unwrap();
222
223 last_day
224 .and_hms_opt(23, 59, 59)
225 .unwrap()
226 .and_local_timezone(Local)
227 .unwrap()
228 }
229
230 pub fn offset(date: DateTime<Local>, unit: DateUnit, offset: i64) -> DateTime<Local> {
232 match unit {
233 DateUnit::MS => date + Duration::milliseconds(offset),
234 DateUnit::SECOND => date + Duration::seconds(offset),
235 DateUnit::MINUTE => date + Duration::minutes(offset),
236 DateUnit::HOUR => date + Duration::hours(offset),
237 DateUnit::DAY => date + Duration::days(offset),
238 DateUnit::WEEK => date + Duration::weeks(offset),
239 DateUnit::MONTH => {
240 let month = date.month() as i32 + offset as i32;
241 let year_offset = (month - 1) / 12;
242 let new_month = ((month - 1) % 12 + 1) as u32;
243 let new_year = date.year() + year_offset;
244
245 let new_day = date.day().min(Self::days_in_month(new_year, new_month));
246
247 NaiveDate::from_ymd_opt(new_year, new_month, new_day)
248 .unwrap()
249 .and_hms_opt(date.hour(), date.minute(), date.second())
250 .unwrap()
251 .and_local_timezone(Local)
252 .unwrap()
253 }
254 DateUnit::YEAR => {
255 let new_year = date.year() + offset as i32;
256 let naive_date = NaiveDate::from_ymd_opt(
257 new_year,
258 date.month(),
259 date.day().min(Self::days_in_month(new_year, date.month())),
260 )
261 .unwrap();
262
263 naive_date
264 .and_hms_opt(date.hour(), date.minute(), date.second())
265 .unwrap()
266 .and_local_timezone(Local)
267 .unwrap()
268 }
269 }
270 }
271
272 fn days_in_month(year: i32, month: u32) -> u32 {
274 NaiveDate::from_ymd_opt(
275 if month == 12 { year + 1 } else { year },
276 if month == 12 { 1 } else { month + 1 },
277 1,
278 )
279 .unwrap()
280 .pred_opt()
281 .unwrap()
282 .day()
283 }
284
285 pub fn offset_day(date: DateTime<Local>, offset: i64) -> DateTime<Local> {
287 Self::offset(date, DateUnit::DAY, offset)
288 }
289
290 pub fn offset_hour(date: DateTime<Local>, offset: i64) -> DateTime<Local> {
292 Self::offset(date, DateUnit::HOUR, offset)
293 }
294
295 pub fn offset_minute(date: DateTime<Local>, offset: i64) -> DateTime<Local> {
297 Self::offset(date, DateUnit::MINUTE, offset)
298 }
299
300 pub fn yesterday() -> DateTime<Local> {
302 Self::offset_day(Self::date(), -1)
303 }
304
305 pub fn tomorrow() -> DateTime<Local> {
307 Self::offset_day(Self::date(), 1)
308 }
309
310 pub fn last_week() -> DateTime<Local> {
312 Self::offset(Self::date(), DateUnit::WEEK, -1)
313 }
314
315 pub fn next_week() -> DateTime<Local> {
317 Self::offset(Self::date(), DateUnit::WEEK, 1)
318 }
319
320 pub fn last_month() -> DateTime<Local> {
322 Self::offset(Self::date(), DateUnit::MONTH, -1)
323 }
324
325 pub fn next_month() -> DateTime<Local> {
327 Self::offset(Self::date(), DateUnit::MONTH, 1)
328 }
329
330 pub fn between(start: DateTime<Local>, end: DateTime<Local>, unit: DateUnit) -> i64 {
332 let duration = end.signed_duration_since(start);
333
334 match unit {
335 DateUnit::MS => duration.num_milliseconds(),
336 DateUnit::SECOND => duration.num_seconds(),
337 DateUnit::MINUTE => duration.num_minutes(),
338 DateUnit::HOUR => duration.num_hours(),
339 DateUnit::DAY => duration.num_days(),
340 DateUnit::WEEK => duration.num_days() / 7,
341 DateUnit::MONTH => {
342 let (y1, m1, d1) = (start.year(), start.month(), start.day());
343 let (y2, m2, d2) = (end.year(), end.month(), end.day());
344
345 let months = (y2 - y1) * 12 + (m2 as i32 - m1 as i32);
346
347 let months = if d2 < d1 { months - 1 } else { months };
348 months as i64
349 }
350 DateUnit::YEAR => {
351 let (y1, m1, d1) = (start.year(), start.month(), start.day());
352 let (y2, m2, d2) = (end.year(), end.month(), end.day());
353
354 let years = y2 - y1;
355
356 let years = if m2 < m1 || (m2 == m1 && d2 < d1) {
357 years - 1
358 } else {
359 years
360 };
361
362 years as i64
363 }
364 }
365 }
366
367 pub fn format_between(between_ms: i64, level: Level) -> String {
369 let total_seconds = between_ms / 1000;
370 let days = total_seconds / (24 * 3600);
371 let hours = (total_seconds % (24 * 3600)) / 3600;
372 let minutes = (total_seconds % 3600) / 60;
373 let seconds = total_seconds % 60;
374 let ms = between_ms % 1000;
375
376 match level {
377 Level::DAY => format!("{}天", days),
378 Level::HOUR => format!("{}天{}小时", days, hours),
379 Level::MINUTE => format!("{}天{}小时{}分", days, hours, minutes),
380 Level::SECOND => format!("{}天{}小时{}分{}秒", days, hours, minutes, seconds),
381 Level::MILLISECOND => format!(
382 "{}天{}小时{}分{}秒{}毫秒",
383 days, hours, minutes, seconds, ms
384 ),
385 _ => format!("{}天", days),
386 }
387 }
388
389 pub fn get_zodiac(month: u32, day: u32) -> &'static str {
391 let idx = if day < ZODIAC_DATE[month as usize - 1] {
392 (month - 2 + 12) % 12
393 } else {
394 (month - 1) % 12
395 };
396
397 ZODIAC[idx as usize]
398 }
399
400 pub fn get_chinese_zodiac(year: i32) -> &'static str {
402 CHINESE_ZODIAC[((year - 1900) % 12) as usize]
403 }
404
405 pub fn age_of_now(birth_date_str: &str) -> Option<u32> {
407 if let Some(birth_date) = Self::parse(birth_date_str) {
408 let now = Self::date();
409 let mut age = now.year() - birth_date.year();
410
411 if now.month() < birth_date.month()
412 || (now.month() == birth_date.month() && now.day() < birth_date.day())
413 {
414 age -= 1;
415 }
416
417 Some(age as u32)
418 } else {
419 None
420 }
421 }
422
423 pub fn is_leap_year(year: i32) -> bool {
425 (year % 4 == 0 && year % 100 != 0) || year % 400 == 0
426 }
427
428 pub fn range(start: DateTime<Local>, end: DateTime<Local>, unit: DateUnit) -> DateRange {
430 DateRange {
431 start,
432 end,
433 current: start,
434 unit,
435 }
436 }
437
438 pub fn range_contains(range1: DateRange, range2: DateRange) -> Vec<DateTime<Local>> {
440 let start = if range1.start > range2.start {
441 range1.start
442 } else {
443 range2.start
444 };
445 let end = if range1.end < range2.end {
446 range1.end
447 } else {
448 range2.end
449 };
450
451 if start > end {
452 return vec![];
453 }
454
455 let unit = match (range1.unit, range2.unit) {
457 (DateUnit::MS, _) | (_, DateUnit::MS) => DateUnit::MS,
458 (DateUnit::SECOND, _) | (_, DateUnit::SECOND) => DateUnit::SECOND,
459 (DateUnit::MINUTE, _) | (_, DateUnit::MINUTE) => DateUnit::MINUTE,
460 (DateUnit::HOUR, _) | (_, DateUnit::HOUR) => DateUnit::HOUR,
461 (DateUnit::DAY, _) | (_, DateUnit::DAY) => DateUnit::DAY,
462 (DateUnit::WEEK, _) | (_, DateUnit::WEEK) => DateUnit::WEEK,
463 (DateUnit::MONTH, _) | (_, DateUnit::MONTH) => DateUnit::MONTH,
464 (DateUnit::YEAR, DateUnit::YEAR) => DateUnit::YEAR,
465 };
466
467 Self::range_to_list(start, end, unit)
468 }
469
470 pub fn range_not_contains(range1: DateRange, range2: DateRange) -> Vec<DateTime<Local>> {
472 let mut result = vec![];
473
474 let unit = range2.unit;
476
477 if range2.start < range1.start {
479 result.append(&mut Self::range_to_list(
480 range2.start,
481 range1.start,
482 unit.clone(),
483 ));
484 }
485
486 if range2.end > range1.end {
488 result.append(&mut Self::range_to_list(range1.end, range2.end, unit));
489 }
490
491 result
492 }
493
494 pub fn range_to_list(
496 start: DateTime<Local>,
497 end: DateTime<Local>,
498 unit: DateUnit,
499 ) -> Vec<DateTime<Local>> {
500 let mut result = vec![];
501 let mut current = start;
502
503 while current <= end {
504 result.push(current);
505 current = Self::offset(current, unit.clone(), 1);
506 }
507
508 result
509 }
510}
511
512impl Iterator for DateRange {
514 type Item = DateTime<Local>;
515
516 fn next(&mut self) -> Option<Self::Item> {
517 if self.current > self.end {
518 return None;
519 }
520
521 let result = self.current;
522 self.current = DateUtil::offset(self.current, self.unit.clone(), 1);
523
524 Some(result)
525 }
526}
527
528pub struct DateRange {
530 pub start: DateTime<Local>,
531 pub end: DateTime<Local>,
532 pub current: DateTime<Local>,
533 pub unit: DateUnit,
534}