use chrono::{DateTime, Datelike, Duration, FixedOffset, NaiveDate, NaiveDateTime, TimeZone, Utc}; /// 获取一个类型为`chrono::DateTime`类型的当前日期时间的实例。时间时区将自动被设置为东八区。 pub fn now_asia_shanghai() -> DateTime { let utc_now = Utc::now(); shift_to_asia_shanghai(utc_now) } /// 将一个类型为`chrono::DateTime`类型的日期时间转换到指定时区的时间实例。 pub fn shift_tz(datetime: DateTime, zone: i64) -> DateTime { if zone.is_positive() { datetime.with_timezone( &FixedOffset::east_opt(Duration::hours(zone.abs()).num_seconds() as i32).unwrap(), ) } else { datetime.with_timezone( &FixedOffset::west_opt(Duration::hours(zone.abs()).num_seconds() as i32).unwrap(), ) } } /// 将一个类型为`chrono::DateTime`类型的日期时间转换到东八区的时间实例。 pub fn shift_to_asia_shanghai(datetime: DateTime) -> DateTime { shift_tz(datetime, 8) } /// 直接给一个原生日期时间附加东八区的时区信息。 pub fn attach_asia_shanghai(datetime: NaiveDateTime) -> DateTime { DateTime::::from_local( datetime, FixedOffset::east_opt(Duration::hours(8).num_seconds() as i32).unwrap(), ) } /// 从一个64位时间戳生成东八区的时间实例。这个函数主要用于处理使用`timestamp`方法直接返回的时间戳。 /// /// - `timestamp`:64位时间戳。 pub fn from_utc_timestamp(timestamp: i64) -> DateTime { let request_time = NaiveDateTime::from_timestamp_micros(timestamp).unwrap(); DateTime::::from_utc( request_time, FixedOffset::east_opt(Duration::hours(8).num_seconds() as i32).unwrap(), ) } /// 根据指定的日期生成一个时间对象,如果给定的日期不合法将返回空白内容。 /// /// - `year`:日期的年份。 /// - `month`:日期的月份,从`1`开始。 /// - `day`:日期的天数。 pub fn date(year: i32, month: u32, day: u32) -> Option { NaiveDate::from_ymd_opt(year, month, day) } /// 根据指定日期生成一个指定日期最开始时间的时间,精度为毫秒。 /// /// - `year`:指定日期的年份。 /// - `month`:指定日期的月份,从`1`开始。 /// - `day`:指定日期的天数。 pub fn date_beginning(year: i32, month: u32, day: u32) -> Option> { let timezone = FixedOffset::east_opt(Duration::hours(8).num_seconds() as i32).unwrap(); NaiveDate::from_ymd_opt(year, month, day) .map(|d| d.and_hms_micro_opt(0, 0, 0, 0).unwrap()) .map(|dt| DateTime::::from_local(dt, timezone)) } /// 根据给定的日期,返回其当天最开始的时间,精度为毫秒。 /// /// - `date`:给定的原始日期,注意:原始日期将被消耗掉。 pub fn begin_of_date(date: NaiveDate) -> Option> { let timezone = FixedOffset::east_opt(Duration::hours(8).num_seconds() as i32).unwrap(); date.and_hms_micro_opt(0, 0, 0, 0) .map(|dt| DateTime::::from_local(dt, timezone)) } /// 根据给定的日期,返回其当天即将结束的时间,精度为毫秒。 /// /// - `date`:给定的原始日期,注意:原始日期将被消耗掉。 pub fn end_of_date(date: NaiveDate) -> Option> { let timezone = FixedOffset::east_opt(Duration::hours(8).num_seconds() as i32).unwrap(); date.and_hms_micro_opt(23, 59, 59, 999_999) .map(|dt| DateTime::::from_local(dt, timezone)) } /// 根据指定日期生成一个指定日期结束时间的时间,精度为毫秒。 /// /// - `year`:指定日期的年份。 /// - `month`:指定日期的月份,从`1`开始。 /// - `day`:指定日期的天数。 pub fn date_ending(year: i32, month: u32, day: u32) -> Option> { let timezone = FixedOffset::east_opt(Duration::hours(8).num_seconds() as i32).unwrap(); NaiveDate::from_ymd_opt(year, month, day) .map(|d| d.and_hms_micro_opt(23, 59, 59, 999_999).unwrap()) .map(|dt| DateTime::::from_local(dt, timezone)) } /// 返回两个日期之间的月份差值。 /// /// - `control`:基准月份。 /// - `test`:测试月份。 pub fn difference_month(control: NaiveDate, test: NaiveDate) -> i32 { let difference_year = test.year() - control.year(); let difference_month = (test.month() - control.month()) as i32; difference_year * 12 + difference_month } /// 测试指定月份是否是基准月份的前一个月份。 /// /// - `control`:基准月份。 /// - `test`:待测试的指定月份。 pub fn is_previous_month(control: NaiveDate, test: NaiveDate) -> bool { difference_month(control, test) == 1 } /// 测试指定月份是否是基准月份的下一个月份。 /// /// - `control`:基准月份。 /// - `test`:待测试的指定月份。 pub fn is_next_month(control: NaiveDate, test: NaiveDate) -> bool { difference_month(control, test) == -1 } /// 生成符合Postgresql中日期类型最小值的日期。 pub fn min_date() -> NaiveDate { NaiveDate::from_ymd_opt(1970, 1, 1).unwrap() } /// 生成符合Postgresql中日期类型最小值的日期时间。 pub fn min_datetime() -> DateTime { NaiveDate::from_ymd_opt(1970, 1, 1) .map(begin_of_date) .flatten() .unwrap() } /// 生成符合Postgresql中日期类型最大值的日期。 pub fn max_date() -> NaiveDate { NaiveDate::from_ymd_opt(2099, 12, 31).unwrap() } /// 生成符合Postgresql中日期类型最大值的日期时间。 pub fn max_datetime() -> DateTime { NaiveDate::from_ymd_opt(2099, 12, 31) .map(end_of_date) .flatten() .unwrap() }