use chrono::{DateTime, Local, NaiveDateTime, TimeZone, Utc};
use serde::Deserialize;
use std::time::{SystemTime, UNIX_EPOCH};
pub struct TimeUtils;
impl TimeUtils {
pub fn current_timestamp() -> i64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as i64
}
pub fn current_timestamp_millis() -> i64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as i64
}
pub fn current_datetime_str() -> String {
Local::now().format("%Y-%m-%d %H:%M:%S").to_string()
}
pub fn current_date_str() -> String {
Local::now().format("%Y-%m-%d").to_string()
}
pub fn current_time_str() -> String {
Local::now().format("%H:%M:%S").to_string()
}
pub fn timestamp_to_datetime_str(timestamp: i64) -> String {
DateTime::<Utc>::from_timestamp(timestamp, 0)
.unwrap()
.with_timezone(&Local)
.format("%Y-%m-%d %H:%M:%S")
.to_string()
}
pub fn datetime_str_to_timestamp(datetime_str: &str) -> Option<i64> {
let naive = NaiveDateTime::parse_from_str(datetime_str, "%Y-%m-%d %H:%M:%S").ok()?;
let datetime = Local.from_local_datetime(&naive).single()?;
Some(datetime.timestamp())
}
pub fn format_datetime(datetime: DateTime<Local>, format: &str) -> String {
datetime.format(format).to_string()
}
pub fn parse_datetime(datetime_str: &str, format: &str) -> Option<DateTime<Local>> {
let naive = NaiveDateTime::parse_from_str(datetime_str, format).ok()?;
Local.from_local_datetime(&naive).single()
}
pub fn diff_seconds(start: DateTime<Local>, end: DateTime<Local>) -> i64 {
end.timestamp() - start.timestamp()
}
pub fn add_seconds(datetime: DateTime<Local>, seconds: i64) -> DateTime<Local> {
datetime + chrono::Duration::seconds(seconds)
}
pub fn add_minutes(datetime: DateTime<Local>, minutes: i64) -> DateTime<Local> {
datetime + chrono::Duration::minutes(minutes)
}
pub fn add_hours(datetime: DateTime<Local>, hours: i64) -> DateTime<Local> {
datetime + chrono::Duration::hours(hours)
}
pub fn add_days(datetime: DateTime<Local>, days: i64) -> DateTime<Local> {
datetime + chrono::Duration::days(days)
}
pub fn is_same_day(dt1: DateTime<Local>, dt2: DateTime<Local>) -> bool {
dt1.date_naive() == dt2.date_naive()
}
pub fn is_today(dt: DateTime<Local>) -> bool {
Self::is_same_day(dt, Local::now())
}
pub fn start_of_day(dt: DateTime<Local>) -> DateTime<Local> {
dt.date_naive()
.and_hms_opt(0, 0, 0)
.unwrap()
.and_local_timezone(Local)
.unwrap()
}
pub fn end_of_day(dt: DateTime<Local>) -> DateTime<Local> {
dt.date_naive()
.and_hms_opt(23, 59, 59)
.unwrap()
.and_local_timezone(Local)
.unwrap()
}
#[allow(dead_code)]
pub fn current_datetime() -> DateTime<Utc> {
Utc::now()
}
pub fn to_local(dt: &DateTime<Utc>) -> DateTime<Local> {
dt.with_timezone(&Local)
}
pub fn serialize_optional_datetime<S>(
value: &Option<DateTime<Utc>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if let Some(dt) = value {
serializer.serialize_str(&dt.format("%Y-%m-%d %H:%M:%S").to_string())
} else {
serializer.serialize_none()
}
}
pub fn deserialize_optional_datetime<'de, D>(
deserializer: D,
) -> Result<Option<DateTime<Utc>>, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: Option<String> = Option::deserialize(deserializer)?;
if let Some(s) = s {
if s.is_empty() {
return Ok(None);
}
match chrono::NaiveDate::parse_from_str(&s, "%Y-%m-%d") {
Ok(date) => {
let datetime = chrono::DateTime::<Utc>::from_naive_utc_and_offset(
date.and_hms_opt(0, 0, 0).unwrap(),
Utc,
);
Ok(Some(datetime))
}
Err(_) => {
match chrono::NaiveDateTime::parse_from_str(&s, "%Y-%m-%d %H:%M:%S") {
Ok(datetime) => {
let datetime =
chrono::DateTime::<Utc>::from_naive_utc_and_offset(datetime, Utc);
Ok(Some(datetime))
}
Err(_) => Err(serde::de::Error::custom("无法解析日期时间字符串")),
}
}
}
} else {
Ok(None)
}
}
pub fn format_duration(duration: std::time::Duration) -> String {
let years = duration.as_secs() / 3600 / 24 / 365;
let months = (duration.as_secs() % (3600 * 24 * 365)) / (3600 * 24 * 30);
let days = (duration.as_secs() % (3600 * 24 * 365)) / (3600 * 24);
let hours = (duration.as_secs() % (3600 * 24)) / 3600;
let minutes = (duration.as_secs() % 3600) / 60;
let seconds = duration.as_secs() % 60;
if years > 0 {
format!(
"{}年{}月{}日 {}小时{}分钟{}秒",
years, months, days, hours, minutes, seconds
)
} else if months > 0 {
format!(
"{}月{}日 {}小时{}分钟{}秒",
months, days, hours, minutes, seconds
)
} else if days > 0 {
format!("{}日 {}小时{}分钟{}秒", days, hours, minutes, seconds)
} else {
format!("{}小时{}分钟{}秒", hours, minutes, seconds)
}
}
}