#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_possible_wrap)]
#![allow(clippy::cast_sign_loss)]
use chrono::prelude::{DateTime, TimeZone};
use chrono::{Datelike, Duration, Local, LocalResult, Utc};
pub const DEFAULT_DATE_FORMAT: &str = "%Y-%m-%d %H:%M:%S";
pub const DATE_FORMAT: &str = "%Y-%m-%d";
pub const TIME_FORMAT: &str = "%H:%M:%S";
const DAYS_IN_MONTH: [u32; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
#[derive(Clone, PartialEq, Debug, Eq)]
pub struct EasyTime<F: TimeZone> {
_marker: std::marker::PhantomData<F>,
}
#[inline]
const fn is_leap_year(year: i32) -> bool {
(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
}
#[inline]
fn days_in_month(year: i32, month: u32) -> u32 {
if month == 2 && is_leap_year(year) {
29
} else {
DAYS_IN_MONTH[(month - 1) as usize]
}
}
fn build_datetime_from_naive<F: TimeZone>(tz: &F, naive: chrono::NaiveDateTime) -> DateTime<F> {
match tz.from_local_datetime(&naive) {
LocalResult::Single(dt) => dt,
LocalResult::Ambiguous(a, _b) => a,
LocalResult::None => panic!("Invalid or non-existent local time."),
}
}
fn add_months_to_datetime<F: TimeZone>(time: &DateTime<F>, months: i32) -> DateTime<F> {
let naive = time.naive_local();
let (year, month, day) = (naive.year(), naive.month() as i32, naive.day());
let total_months = year * 12 + (month - 1) + months;
let target_year = total_months.div_euclid(12);
let target_month = total_months.rem_euclid(12) + 1;
let days_in_target = days_in_month(target_year, target_month as u32);
let target_day = std::cmp::min(day, days_in_target);
let target_date =
chrono::NaiveDate::from_ymd_opt(target_year, target_month as u32, target_day)
.expect("Invalid date after adding months");
let target_naive_dt = target_date.and_time(naive.time());
build_datetime_from_naive(&time.timezone(), target_naive_dt)
}
fn add_years_to_datetime<F: TimeZone>(time: &DateTime<F>, years: i32) -> DateTime<F> {
let naive = time.naive_local();
let (year, month, day) = (naive.year() + years, naive.month(), naive.day());
let days_in_target = days_in_month(year, month);
let target_day = std::cmp::min(day, days_in_target);
let target_date = chrono::NaiveDate::from_ymd_opt(year, month, target_day)
.expect("Invalid date after adding years");
let target_naive_dt = target_date.and_time(naive.time());
build_datetime_from_naive(&time.timezone(), target_naive_dt)
}
impl EasyTime<Local> {
#[inline]
#[must_use]
pub fn seconds_from_now(value: i64) -> DateTime<Local> {
Local::now() + Duration::seconds(value)
}
#[inline]
#[must_use]
pub fn seconds_ago(value: i64) -> DateTime<Local> {
Local::now() - Duration::seconds(value)
}
#[inline]
#[must_use]
pub fn minutes_from_now(value: i64) -> DateTime<Local> {
Local::now() + Duration::minutes(value)
}
#[inline]
#[must_use]
pub fn minutes_ago(value: i64) -> DateTime<Local> {
Local::now() - Duration::minutes(value)
}
#[inline]
#[must_use]
pub fn hours_from_now(value: i64) -> DateTime<Local> {
Local::now() + Duration::hours(value)
}
#[inline]
#[must_use]
pub fn hours_ago(value: i64) -> DateTime<Local> {
Local::now() - Duration::hours(value)
}
#[inline]
#[must_use]
pub fn days_from_now(value: i64) -> DateTime<Local> {
Local::now() + Duration::days(value)
}
#[inline]
#[must_use]
pub fn days_ago(value: i64) -> DateTime<Local> {
Local::now() - Duration::days(value)
}
#[inline]
#[must_use]
pub fn weeks_from_now(value: i64) -> DateTime<Local> {
Local::now() + Duration::weeks(value)
}
#[inline]
#[must_use]
pub fn weeks_ago(value: i64) -> DateTime<Local> {
Local::now() - Duration::weeks(value)
}
#[must_use]
pub fn months_from_now(value: i64) -> DateTime<Local> {
add_months_to_datetime(&Local::now(), value as i32)
}
#[must_use]
pub fn months_ago(value: i64) -> DateTime<Local> {
add_months_to_datetime(&Local::now(), -(value as i32))
}
#[must_use]
pub fn years_from_now(value: i64) -> DateTime<Local> {
add_years_to_datetime(&Local::now(), value as i32)
}
#[must_use]
pub fn years_ago(value: i64) -> DateTime<Local> {
add_years_to_datetime(&Local::now(), -(value as i32))
}
#[must_use]
pub fn decades_from_now(value: i64) -> DateTime<Local> {
add_years_to_datetime(&Local::now(), value as i32 * 10)
}
#[must_use]
pub fn decades_ago(value: i64) -> DateTime<Local> {
add_years_to_datetime(&Local::now(), -(value as i32) * 10)
}
#[must_use]
pub fn centuries_from_now(value: i64) -> DateTime<Local> {
add_years_to_datetime(&Local::now(), value as i32 * 100)
}
#[must_use]
pub fn centuries_ago(value: i64) -> DateTime<Local> {
add_years_to_datetime(&Local::now(), -(value as i32) * 100)
}
#[must_use]
pub fn millenniums_from_now(value: i64) -> DateTime<Local> {
add_years_to_datetime(&Local::now(), value as i32 * 1000)
}
#[must_use]
pub fn millenniums_ago(value: i64) -> DateTime<Local> {
add_years_to_datetime(&Local::now(), -(value as i32) * 1000)
}
#[inline]
#[must_use]
pub fn seconds_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
base + Duration::seconds(value)
}
#[inline]
#[must_use]
pub fn seconds_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
base - Duration::seconds(value)
}
#[inline]
#[must_use]
pub fn minutes_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
base + Duration::minutes(value)
}
#[inline]
#[must_use]
pub fn minutes_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
base - Duration::minutes(value)
}
#[inline]
#[must_use]
pub fn hours_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
base + Duration::hours(value)
}
#[inline]
#[must_use]
pub fn hours_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
base - Duration::hours(value)
}
#[inline]
#[must_use]
pub fn days_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
base + Duration::days(value)
}
#[inline]
#[must_use]
pub fn days_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
base - Duration::days(value)
}
#[inline]
#[must_use]
pub fn weeks_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
base + Duration::weeks(value)
}
#[inline]
#[must_use]
pub fn weeks_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
base - Duration::weeks(value)
}
#[must_use]
pub fn months_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
add_months_to_datetime(&base, value as i32)
}
#[must_use]
pub fn months_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
add_months_to_datetime(&base, -(value as i32))
}
#[must_use]
pub fn years_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
add_years_to_datetime(&base, value as i32)
}
#[must_use]
pub fn years_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
add_years_to_datetime(&base, -(value as i32))
}
#[must_use]
pub fn decades_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
add_years_to_datetime(&base, value as i32 * 10)
}
#[must_use]
pub fn decades_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
add_years_to_datetime(&base, -(value as i32) * 10)
}
#[must_use]
pub fn centuries_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
add_years_to_datetime(&base, value as i32 * 100)
}
#[must_use]
pub fn centuries_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
add_years_to_datetime(&base, -(value as i32) * 100)
}
#[must_use]
pub fn millenniums_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
add_years_to_datetime(&base, value as i32 * 1000)
}
#[must_use]
pub fn millenniums_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
add_years_to_datetime(&base, -(value as i32) * 1000)
}
}
impl EasyTime<Utc> {
#[inline]
#[must_use]
pub fn seconds_from_now(value: i64) -> DateTime<Utc> {
Utc::now() + Duration::seconds(value)
}
#[inline]
#[must_use]
pub fn seconds_ago(value: i64) -> DateTime<Utc> {
Utc::now() - Duration::seconds(value)
}
#[inline]
#[must_use]
pub fn minutes_from_now(value: i64) -> DateTime<Utc> {
Utc::now() + Duration::minutes(value)
}
#[inline]
#[must_use]
pub fn minutes_ago(value: i64) -> DateTime<Utc> {
Utc::now() - Duration::minutes(value)
}
#[inline]
#[must_use]
pub fn hours_from_now(value: i64) -> DateTime<Utc> {
Utc::now() + Duration::hours(value)
}
#[inline]
#[must_use]
pub fn hours_ago(value: i64) -> DateTime<Utc> {
Utc::now() - Duration::hours(value)
}
#[inline]
#[must_use]
pub fn days_from_now(value: i64) -> DateTime<Utc> {
Utc::now() + Duration::days(value)
}
#[inline]
#[must_use]
pub fn days_ago(value: i64) -> DateTime<Utc> {
Utc::now() - Duration::days(value)
}
#[inline]
#[must_use]
pub fn weeks_from_now(value: i64) -> DateTime<Utc> {
Utc::now() + Duration::weeks(value)
}
#[inline]
#[must_use]
pub fn weeks_ago(value: i64) -> DateTime<Utc> {
Utc::now() - Duration::weeks(value)
}
#[must_use]
pub fn months_from_now(value: i64) -> DateTime<Utc> {
add_months_to_datetime(&Utc::now(), value as i32)
}
#[must_use]
pub fn months_ago(value: i64) -> DateTime<Utc> {
add_months_to_datetime(&Utc::now(), -(value as i32))
}
#[must_use]
pub fn years_from_now(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), value as i32)
}
#[must_use]
pub fn years_ago(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), -(value as i32))
}
#[must_use]
pub fn decades_from_now(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), value as i32 * 10)
}
#[must_use]
pub fn decades_ago(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), -(value as i32) * 10)
}
#[must_use]
pub fn centuries_from_now(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), value as i32 * 100)
}
#[must_use]
pub fn centuries_ago(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), -(value as i32) * 100)
}
#[must_use]
pub fn millenniums_from_now(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), value as i32 * 1000)
}
#[must_use]
pub fn millenniums_ago(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), -(value as i32) * 1000)
}
#[inline]
#[must_use]
pub fn seconds_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
base + Duration::seconds(value)
}
#[inline]
#[must_use]
pub fn seconds_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
base - Duration::seconds(value)
}
#[inline]
#[must_use]
pub fn minutes_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
base + Duration::minutes(value)
}
#[inline]
#[must_use]
pub fn minutes_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
base - Duration::minutes(value)
}
#[inline]
#[must_use]
pub fn hours_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
base + Duration::hours(value)
}
#[inline]
#[must_use]
pub fn hours_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
base - Duration::hours(value)
}
#[inline]
#[must_use]
pub fn days_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
base + Duration::days(value)
}
#[inline]
#[must_use]
pub fn days_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
base - Duration::days(value)
}
#[inline]
#[must_use]
pub fn weeks_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
base + Duration::weeks(value)
}
#[inline]
#[must_use]
pub fn weeks_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
base - Duration::weeks(value)
}
#[must_use]
pub fn months_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
add_months_to_datetime(&base, value as i32)
}
#[must_use]
pub fn months_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
add_months_to_datetime(&base, -(value as i32))
}
#[must_use]
pub fn years_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
add_years_to_datetime(&base, value as i32)
}
#[must_use]
pub fn years_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
add_years_to_datetime(&base, -(value as i32))
}
#[must_use]
pub fn decades_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
add_years_to_datetime(&base, value as i32 * 10)
}
#[must_use]
pub fn decades_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
add_years_to_datetime(&base, -(value as i32) * 10)
}
#[must_use]
pub fn centuries_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
add_years_to_datetime(&base, value as i32 * 100)
}
#[must_use]
pub fn centuries_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
add_years_to_datetime(&base, -(value as i32) * 100)
}
#[must_use]
pub fn millenniums_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
add_years_to_datetime(&base, value as i32 * 1000)
}
#[must_use]
pub fn millenniums_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
add_years_to_datetime(&base, -(value as i32) * 1000)
}
}
impl<F: TimeZone> EasyTime<F> {
#[inline]
#[must_use]
pub fn utc_seconds_from_now(value: i64) -> DateTime<Utc> {
Utc::now() + Duration::seconds(value)
}
#[inline]
#[must_use]
pub fn utc_seconds_ago(value: i64) -> DateTime<Utc> {
Utc::now() - Duration::seconds(value)
}
#[inline]
#[must_use]
pub fn utc_minutes_from_now(value: i64) -> DateTime<Utc> {
Utc::now() + Duration::minutes(value)
}
#[inline]
#[must_use]
pub fn utc_minutes_ago(value: i64) -> DateTime<Utc> {
Utc::now() - Duration::minutes(value)
}
#[inline]
#[must_use]
pub fn utc_hours_from_now(value: i64) -> DateTime<Utc> {
Utc::now() + Duration::hours(value)
}
#[inline]
#[must_use]
pub fn utc_hours_ago(value: i64) -> DateTime<Utc> {
Utc::now() - Duration::hours(value)
}
#[inline]
#[must_use]
pub fn utc_days_from_now(value: i64) -> DateTime<Utc> {
Utc::now() + Duration::days(value)
}
#[inline]
#[must_use]
pub fn utc_days_ago(value: i64) -> DateTime<Utc> {
Utc::now() - Duration::days(value)
}
#[inline]
#[must_use]
pub fn utc_weeks_from_now(value: i64) -> DateTime<Utc> {
Utc::now() + Duration::weeks(value)
}
#[inline]
#[must_use]
pub fn utc_weeks_ago(value: i64) -> DateTime<Utc> {
Utc::now() - Duration::weeks(value)
}
#[must_use]
pub fn utc_months_from_now(value: i64) -> DateTime<Utc> {
add_months_to_datetime(&Utc::now(), value as i32)
}
#[must_use]
pub fn utc_months_ago(value: i64) -> DateTime<Utc> {
add_months_to_datetime(&Utc::now(), -(value as i32))
}
#[must_use]
pub fn utc_years_from_now(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), value as i32)
}
#[must_use]
pub fn utc_years_ago(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), -(value as i32))
}
#[must_use]
pub fn utc_decades_from_now(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), value as i32 * 10)
}
#[must_use]
pub fn utc_decades_ago(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), -(value as i32) * 10)
}
#[must_use]
pub fn utc_centuries_from_now(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), value as i32 * 100)
}
#[must_use]
pub fn utc_centuries_ago(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), -(value as i32) * 100)
}
#[must_use]
pub fn utc_millenniums_from_now(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), value as i32 * 1000)
}
#[must_use]
pub fn utc_millenniums_ago(value: i64) -> DateTime<Utc> {
add_years_to_datetime(&Utc::now(), -(value as i32) * 1000)
}
#[inline]
#[must_use]
pub fn is_leap_year(year: i32) -> bool {
is_leap_year(year)
}
#[inline]
#[must_use]
pub fn days_in_month(year: i32, month: u32) -> u32 {
days_in_month(year, month)
}
}
#[must_use]
pub fn format_datetime<F: TimeZone>(time: &DateTime<F>) -> String
where
F::Offset: std::fmt::Display,
{
time.format(DEFAULT_DATE_FORMAT).to_string()
}
#[must_use]
pub fn format_datetime_with<F: TimeZone>(time: &DateTime<F>, format_str: &str) -> String
where
F::Offset: std::fmt::Display,
{
time.format(format_str).to_string()
}
#[must_use]
pub fn format_datetime_with_timezone<F: TimeZone>(time: &DateTime<F>) -> String
where
F::Offset: std::fmt::Display,
{
format!("{} {}", time.format(DEFAULT_DATE_FORMAT), time.offset())
}
#[must_use]
pub fn format_datetime_with_timezone_format<F: TimeZone>(
time: &DateTime<F>,
format_str: &str,
) -> String
where
F::Offset: std::fmt::Display,
{
format!("{} {}", time.format(format_str), time.offset())
}
#[must_use]
pub fn to_date<F: TimeZone>(time: &DateTime<F>) -> String
where
F::Offset: std::fmt::Display,
{
time.format(DATE_FORMAT).to_string()
}
#[must_use]
pub fn to_time<F: TimeZone>(time: &DateTime<F>) -> String
where
F::Offset: std::fmt::Display,
{
time.format(TIME_FORMAT).to_string()
}
#[must_use]
pub fn to_timestamp<F: TimeZone>(time: &DateTime<F>) -> i64 {
time.timestamp()
}