use crate::duration::TimeDelta;
use crate::time::Time;
use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum Weekday {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday,
}
impl Weekday {
pub fn from_u8(n: u8) -> Option<Weekday> {
match n {
1 => Some(Weekday::Monday),
2 => Some(Weekday::Tuesday),
3 => Some(Weekday::Wednesday),
4 => Some(Weekday::Thursday),
5 => Some(Weekday::Friday),
6 => Some(Weekday::Saturday),
7 => Some(Weekday::Sunday),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
match self {
Weekday::Monday => 1,
Weekday::Tuesday => 2,
Weekday::Wednesday => 3,
Weekday::Thursday => 4,
Weekday::Friday => 5,
Weekday::Saturday => 6,
Weekday::Sunday => 7,
}
}
}
impl fmt::Display for Weekday {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Weekday::Monday => "Monday",
Weekday::Tuesday => "Tuesday",
Weekday::Wednesday => "Wednesday",
Weekday::Thursday => "Thursday",
Weekday::Friday => "Friday",
Weekday::Saturday => "Saturday",
Weekday::Sunday => "Sunday",
};
write!(f, "{}", s)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Date {
pub year: i32,
pub month: u8,
pub day: u8,
}
impl Date {
pub fn new(year: i32, month: u8, day: u8) -> Self {
Date { year, month, day }
}
pub fn from_ymd(year: i32, month: u8, day: u8) -> Self {
Date::new(year, month, day)
}
pub fn is_valid(&self) -> bool {
if self.month < 1 || self.month > 12 {
return false;
}
if self.day < 1 || self.day > days_in_month(self.year, self.month) {
return false;
}
true
}
pub fn year(&self) -> i32 {
self.year
}
pub fn month(&self) -> u8 {
self.month
}
pub fn day(&self) -> u8 {
self.day
}
pub fn is_leap_year(&self) -> bool {
is_leap_year(self.year)
}
pub fn weekday(&self) -> Weekday {
let (mut y, m) = if self.month <= 2 {
(self.year - 1, self.month + 12)
} else {
(self.year, self.month)
};
let c = y / 100;
y %= 100;
let w = (self.day as i32 + (13 * (m as i32 + 1)) / 5 + y + y / 4 + c / 4 - 2 * c) % 7;
let w = ((w + 7) % 7) as u8;
match w {
0 => Weekday::Saturday,
1 => Weekday::Sunday,
2 => Weekday::Monday,
3 => Weekday::Tuesday,
4 => Weekday::Wednesday,
5 => Weekday::Thursday,
6 => Weekday::Friday,
_ => Weekday::Monday,
}
}
pub fn day_of_year(&self) -> u16 {
let mut days = 0u16;
for m in 1..self.month {
days += days_in_month(self.year, m) as u16;
}
days + self.day as u16
}
pub fn days_in_month(&self) -> u8 {
days_in_month(self.year, self.month)
}
pub fn at_time(&self, time: Time) -> crate::datetime::DateTime {
crate::datetime::DateTime::new(*self, time)
}
}
pub fn is_leap_year(year: i32) -> bool {
(year % 4 == 0 && year % 100 != 0) || year % 400 == 0
}
pub fn days_in_month(year: i32, month: u8) -> u8 {
match month {
1 => 31,
2 => {
if is_leap_year(year) {
29
} else {
28
}
}
3 => 31,
4 => 30,
5 => 31,
6 => 30,
7 => 31,
8 => 31,
9 => 30,
10 => 31,
11 => 30,
12 => 31,
_ => 0,
}
}
pub fn days_before_year(year: i32) -> i64 {
let y = year as i64 - 1;
y * 365 + y / 4 - y / 100 + y / 400
}
pub fn days_from_epoch(date: Date) -> i64 {
let mut days = days_before_year(date.year);
for m in 1..date.month {
days += days_in_month(date.year, m) as i64;
}
days + date.day as i64 - 1
}
pub fn date_from_days(days: i64) -> Date {
let mut y = (days / 146097 * 400 + 1) as i64;
while y > i32::MAX as i64 {
y -= 1;
}
let mut yi = y as i32;
while days_before_year(yi) > days {
yi -= 1;
}
while days_before_year(yi + 1) <= days {
yi += 1;
}
let mut remaining = (days - days_before_year(yi)) as u16;
let mut m: u8 = 1;
while m <= 12 {
let dm = days_in_month(yi, m) as u16;
if remaining < dm {
break;
}
remaining -= dm;
m += 1;
}
Date {
year: yi,
month: m,
day: (remaining + 1) as u8,
}
}
impl fmt::Display for Date {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
}
}
impl std::ops::Add<TimeDelta> for Date {
type Output = Date;
fn add(self, delta: TimeDelta) -> Date {
date_from_days(days_from_epoch(self) + delta.total_seconds() as i64 / 86400)
}
}
impl std::ops::Sub<TimeDelta> for Date {
type Output = Date;
fn sub(self, delta: TimeDelta) -> Date {
date_from_days(days_from_epoch(self) - delta.total_seconds() as i64 / 86400)
}
}
impl std::ops::Sub<Date> for Date {
type Output = TimeDelta;
fn sub(self, rhs: Date) -> TimeDelta {
let days = days_from_epoch(self) - days_from_epoch(rhs);
TimeDelta::new(days * 86400, 0)
}
}