use std::cmp::{Ordering, PartialOrd};
use std::error::Error as ErrorTrait;
use std::fmt;
use std::ops::{Add, Sub};
use std::ops::Deref;
use std::ops::{Range, RangeFrom, RangeTo, RangeFull};
use std::slice::Iter as SliceIter;
use cal::{DatePiece, TimePiece};
use cal::fmt::ISO;
use duration::Duration;
use instant::Instant;
use system::sys_time;
use util::RangeExt;
use self::Month::*;
use self::Weekday::*;
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct Year(pub i64);
impl Year {
pub fn is_leap_year(self) -> bool {
self.leap_year_calculations().1
}
pub fn months<S: MonthSpan>(self, span: S) -> YearMonths {
YearMonths {
year: self,
iter: span.get_slice().iter(),
}
}
pub fn month(self, month: Month) -> YearMonth {
YearMonth {
year: self,
month,
}
}
fn leap_year_calculations(self) -> (i64, bool) {
let year = self.0 - 2000;
let (num_400y_cycles, mut remainder) = split_cycles(year, 400);
let currently_leap_year = remainder == 0 || (remainder % 100 != 0 && remainder % 4 == 0);
let num_100y_cycles = remainder / 100;
remainder -= num_100y_cycles * 100;
let leap_years_elapsed = remainder / 4
+ 97 * num_400y_cycles + 24 * num_100y_cycles - if currently_leap_year { 1 } else { 0 };
(leap_years_elapsed, currently_leap_year)
}
}
impl Deref for Year {
type Target = i64;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub trait MonthSpan {
fn get_slice(&self) -> &'static [Month];
}
static MONTHS: &[Month] = &[
January, February, March,
April, May, June,
July, August, September,
October, November, December,
];
impl MonthSpan for RangeFull {
fn get_slice(&self) -> &'static [Month] {
MONTHS
}
}
impl MonthSpan for RangeFrom<Month> {
fn get_slice(&self) -> &'static [Month] {
&MONTHS[self.start.months_from_january() ..]
}
}
impl MonthSpan for RangeTo<Month> {
fn get_slice(&self) -> &'static [Month] {
&MONTHS[.. self.end.months_from_january()]
}
}
impl MonthSpan for Range<Month> {
fn get_slice(&self) -> &'static [Month] {
&MONTHS[self.start.months_from_january() .. self.end.months_from_january()]
}
}
pub struct YearMonths {
year: Year,
iter: SliceIter<'static, Month>,
}
impl Iterator for YearMonths {
type Item = YearMonth;
fn next(&mut self) -> Option<YearMonth> {
self.iter.next().map(|m| YearMonth {
year: self.year,
month: *m,
})
}
}
impl DoubleEndedIterator for YearMonths {
fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back().map(|m| YearMonth {
year: self.year,
month: *m,
})
}
}
impl fmt::Debug for YearMonths {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "YearMonths({}, {:?})", self.year.0, self.iter.as_slice())
}
}
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct YearMonth {
pub year: Year,
pub month: Month,
}
impl YearMonth {
pub fn day_count(&self) -> i8 {
self.month.days_in_month(self.year.is_leap_year())
}
pub fn days<S: DaySpan>(&self, span: S) -> MonthDays {
MonthDays {
ym: *self,
range: span.get_range(self)
}
}
pub fn day(&self, day: i8) -> Result<LocalDate, Error> {
LocalDate::ymd(self.year.0, self.month, day)
}
}
pub trait DaySpan {
fn get_range(&self, ym: &YearMonth) -> Range<i8>;
}
impl DaySpan for RangeFull {
fn get_range(&self, ym: &YearMonth) -> Range<i8> {
1 .. ym.day_count() + 1
}
}
impl DaySpan for RangeFrom<i8> {
fn get_range(&self, ym: &YearMonth) -> Range<i8> {
self.start .. ym.day_count() + 1
}
}
impl DaySpan for RangeTo<i8> {
fn get_range(&self, _ym: &YearMonth) -> Range<i8> {
1 .. self.end
}
}
impl DaySpan for Range<i8> {
fn get_range(&self, _ym: &YearMonth) -> Range<i8> {
self.clone()
}
}
#[derive(PartialEq, Debug)]
pub struct MonthDays {
ym: YearMonth,
range: Range<i8>,
}
impl Iterator for MonthDays {
type Item = LocalDate;
fn next(&mut self) -> Option<Self::Item> {
self.range.next().and_then(|d| LocalDate::ymd(self.ym.year.0, self.ym.month, d).ok())
}
}
impl DoubleEndedIterator for MonthDays {
fn next_back(&mut self) -> Option<Self::Item> {
self.range.next_back().and_then(|d| LocalDate::ymd(self.ym.year.0, self.ym.month, d).ok())
}
}
const DAYS_IN_4Y: i64 = 365 * 4 + 1;
const DAYS_IN_100Y: i64 = 365 * 100 + 24;
const DAYS_IN_400Y: i64 = 365 * 400 + 97;
const SECONDS_IN_DAY: i64 = 86400;
const EPOCH_DIFFERENCE: i64 = 30 * 365 + 7 + 31 + 29;
const TIME_TRIANGLE: &[i64; 11] =
&[31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31 + 31, 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, 31 + 30 + 31 + 30 + 31 + 31 + 30, 31 + 30 + 31 + 30 + 31 + 31, 31 + 30 + 31 + 30 + 31, 31 + 30 + 31 + 30, 31 + 30 + 31, 31 + 30, 31];
#[derive(Eq, Clone, Copy)]
pub struct LocalDate {
ymd: YMD,
yearday: i16,
weekday: Weekday,
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub struct LocalTime {
hour: i8,
minute: i8,
second: i8,
millisecond: i16,
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub struct LocalDateTime {
date: LocalDate,
time: LocalTime,
}
impl LocalDate {
pub fn ymd(year: i64, month: Month, day: i8) -> Result<Self, Error> {
YMD { year, month, day }
.to_days_since_epoch()
.map(|days| Self::from_days_since_epoch(days - EPOCH_DIFFERENCE))
}
pub fn yd(year: i64, yearday: i64) -> Result<Self, Error> {
if yearday.is_within(0..367) {
let jan_1 = YMD { year, month: January, day: 1 };
let days = jan_1.to_days_since_epoch()?;
Ok(Self::from_days_since_epoch(days + yearday - 1 - EPOCH_DIFFERENCE))
}
else {
Err(Error::OutOfRange)
}
}
pub fn ywd(year: i64, week: i64, weekday: Weekday) -> Result<Self, Error> {
let jan_4 = YMD { year, month: January, day: 4 };
let correction = days_to_weekday(jan_4.to_days_since_epoch().unwrap() - EPOCH_DIFFERENCE).days_from_monday_as_one() as i64 + 3;
let yearday = 7 * week + weekday.days_from_monday_as_one() as i64 - correction;
if yearday <= 0 {
let days_in_year = if Year(year - 1).is_leap_year() { 366 } else { 365 };
Self::yd(year - 1, days_in_year + yearday)
}
else {
let days_in_year = if Year(year).is_leap_year() { 366 } else { 365 };
if yearday >= days_in_year {
Self::yd(year + 1, yearday - days_in_year)
}
else {
Self::yd(year, yearday)
}
}
}
fn from_days_since_epoch(days: i64) -> Self {
let (num_400y_cycles, mut remainder) = split_cycles(days, DAYS_IN_400Y);
let num_100y_cycles = remainder / DAYS_IN_100Y;
remainder -= num_100y_cycles * DAYS_IN_100Y;
let num_4y_cycles = remainder / DAYS_IN_4Y;
remainder -= num_4y_cycles * DAYS_IN_4Y;
let mut years = std::cmp::min(remainder / 365, 3);
remainder -= years * 365;
let days_this_year =
if years == 0 && !(num_4y_cycles == 0 && num_100y_cycles != 0) { 366 }
else { 365 };
let mut day_of_year = remainder + days_this_year - 306;
if day_of_year >= days_this_year {
day_of_year -= days_this_year; }
years += 4 * num_4y_cycles
+ 100 * num_100y_cycles
+ 400 * num_400y_cycles;
let result = TIME_TRIANGLE.iter()
.enumerate()
.find(|&(_, days)| *days <= remainder);
let (mut month, month_days) = match result {
Some((index, days)) => (11 - index, remainder - *days),
None => (0, remainder), };
month += 2;
if month >= 12 {
years += 1; month -= 12; }
let month_variant = Month::from_zero(month as i8).unwrap();
Self {
yearday: (day_of_year + 1) as i16,
weekday: days_to_weekday(days),
ymd: YMD {
year: years + 2000,
month: month_variant,
day: (month_days + 1) as i8,
},
}
}
pub unsafe fn _new_with_prefilled_values(year: i64, month: Month, day: i8, weekday: Weekday, yearday: i16) -> Self {
Self {
ymd: YMD { year, month, day },
weekday,
yearday,
}
}
}
impl DatePiece for LocalDate {
fn year(&self) -> i64 { self.ymd.year }
fn month(&self) -> Month { self.ymd.month }
fn day(&self) -> i8 { self.ymd.day }
fn yearday(&self) -> i16 { self.yearday }
fn weekday(&self) -> Weekday { self.weekday }
}
impl fmt::Debug for LocalDate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LocalDate({})", self.iso())
}
}
impl PartialEq for LocalDate {
fn eq(&self, other: &Self) -> bool {
self.ymd == other.ymd
}
}
impl PartialOrd for LocalDate {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.ymd.partial_cmp(&other.ymd)
}
}
impl Ord for LocalDate {
fn cmp(&self, other: &Self) -> Ordering {
self.ymd.cmp(&other.ymd)
}
}
impl LocalTime {
pub fn from_seconds_since_midnight(seconds: i64) -> Self {
Self::from_seconds_and_milliseconds_since_midnight(seconds, 0)
}
pub fn from_seconds_and_milliseconds_since_midnight(seconds: i64, millisecond_of_second: i16) -> Self {
Self {
hour: (seconds / 60 / 60) as i8,
minute: (seconds / 60 % 60) as i8,
second: (seconds % 60) as i8,
millisecond: millisecond_of_second,
}
}
pub fn midnight() -> Self {
Self { hour: 0, minute: 0, second: 0, millisecond: 0 }
}
pub fn hm(hour: i8, minute: i8) -> Result<Self, Error> {
if (hour.is_within(0..24) && minute.is_within(0..60))
|| (hour == 24 && minute == 00) {
Ok(Self { hour, minute, second: 0, millisecond: 0 })
}
else {
Err(Error::OutOfRange)
}
}
pub fn hms(hour: i8, minute: i8, second: i8) -> Result<Self, Error> {
if (hour.is_within(0..24) && minute.is_within(0..60) && second.is_within(0..60))
|| (hour == 24 && minute == 00 && second == 00) {
Ok(Self { hour, minute, second, millisecond: 0 })
}
else {
Err(Error::OutOfRange)
}
}
pub fn hms_ms(hour: i8, minute: i8, second: i8, millisecond: i16) -> Result<Self, Error> {
if hour.is_within(0..24) && minute.is_within(0..60)
&& second.is_within(0..60) && millisecond.is_within(0..1000)
{
Ok(Self { hour, minute, second, millisecond })
}
else {
Err(Error::OutOfRange)
}
}
pub fn to_seconds(self) -> i64 {
self.hour as i64 * 3600
+ self.minute as i64 * 60
+ self.second as i64
}
}
impl TimePiece for LocalTime {
fn hour(&self) -> i8 { self.hour }
fn minute(&self) -> i8 { self.minute }
fn second(&self) -> i8 { self.second }
fn millisecond(&self) -> i16 { self.millisecond }
}
impl fmt::Debug for LocalTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LocalTime({})", self.iso())
}
}
impl LocalDateTime {
pub fn from_instant(instant: Instant) -> Self {
Self::at_ms(instant.seconds(), instant.milliseconds())
}
pub fn at(seconds_since_1970_epoch: i64) -> Self {
Self::at_ms(seconds_since_1970_epoch, 0)
}
pub fn at_ms(seconds_since_1970_epoch: i64, millisecond_of_second: i16) -> Self {
let seconds = seconds_since_1970_epoch - EPOCH_DIFFERENCE * SECONDS_IN_DAY;
let (days, secs) = split_cycles(seconds, SECONDS_IN_DAY);
Self {
date: LocalDate::from_days_since_epoch(days),
time: LocalTime::from_seconds_and_milliseconds_since_midnight(secs, millisecond_of_second),
}
}
pub fn new(date: LocalDate, time: LocalTime) -> Self {
Self {
date,
time,
}
}
pub fn date(&self) -> LocalDate {
self.date
}
pub fn time(&self) -> LocalTime {
self.time
}
#[cfg_attr(target_os = "redox", allow(unused_unsafe))]
pub fn now() -> Self {
let (s, ms) = unsafe { sys_time() };
Self::at_ms(s, ms)
}
pub fn to_instant(&self) -> Instant {
let seconds = self.date.ymd.to_days_since_epoch().unwrap() * SECONDS_IN_DAY + self.time.to_seconds();
Instant::at_ms(seconds, self.time.millisecond)
}
pub fn add_seconds(&self, seconds: i64) -> Self {
Self::from_instant(self.to_instant() + Duration::of(seconds))
}
}
impl DatePiece for LocalDateTime {
fn year(&self) -> i64 { self.date.ymd.year }
fn month(&self) -> Month { self.date.ymd.month }
fn day(&self) -> i8 { self.date.ymd.day }
fn yearday(&self) -> i16 { self.date.yearday }
fn weekday(&self) -> Weekday { self.date.weekday }
}
impl TimePiece for LocalDateTime {
fn hour(&self) -> i8 { self.time.hour }
fn minute(&self) -> i8 { self.time.minute }
fn second(&self) -> i8 { self.time.second }
fn millisecond(&self) -> i16 { self.time.millisecond }
}
impl fmt::Debug for LocalDateTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LocalDateTime({})", self.iso())
}
}
impl Add<Duration> for LocalDateTime {
type Output = Self;
fn add(self, duration: Duration) -> Self {
Self::from_instant(self.to_instant() + duration)
}
}
impl Sub<Duration> for LocalDateTime {
type Output = Self;
fn sub(self, duration: Duration) -> Self {
Self::from_instant(self.to_instant() - duration)
}
}
#[derive(PartialEq, PartialOrd, Eq, Ord, Clone, Debug, Copy)]
struct YMD {
year: i64,
month: Month,
day: i8,
}
impl YMD {
fn to_days_since_epoch(&self) -> Result<i64, Error> {
let years = self.year - 2000;
let (leap_days_elapsed, is_leap_year) = Year(self.year).leap_year_calculations();
if !self.is_valid(is_leap_year) {
return Err(Error::OutOfRange);
}
let days = years * 365
+ 10958
+ leap_days_elapsed
+ self.month.days_before_start() as i64
+ if is_leap_year && self.month >= March { 1 } else { 0 }
+ (self.day - 1) as i64;
Ok(days)
}
fn is_valid(&self, is_leap_year: bool) -> bool {
self.day >= 1 && self.day <= self.month.days_in_month(is_leap_year)
}
}
fn days_to_weekday(days: i64) -> Weekday {
let weekday = (days + 3) % 7;
Weekday::from_zero(if weekday < 0 { weekday + 7 } else { weekday } as i8).unwrap()
}
fn split_cycles(number_of_periods: i64, cycle_length: i64) -> (i64, i64) {
let mut cycles = number_of_periods / cycle_length;
let mut remainder = number_of_periods % cycle_length;
if remainder < 0 {
remainder += cycle_length;
cycles -= 1;
}
(cycles, remainder)
}
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum Error {
OutOfRange,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "datetime field out of range")
}
}
impl ErrorTrait for Error {
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
pub enum Month {
January = 1, February = 2, March = 3,
April = 4, May = 5, June = 6,
July = 7, August = 8, September = 9,
October = 10, November = 11, December = 12,
}
#[allow(clippy::match_same_arms)]
impl Month {
pub fn days_in_month(self, leap_year: bool) -> i8 {
match self {
January => 31, February => if leap_year { 29 } else { 28 },
March => 31, April => 30,
May => 31, June => 30,
July => 31, August => 31,
September => 30, October => 31,
November => 30, December => 31,
}
}
fn days_before_start(self) -> i16 {
match self {
January => 0, February => 31, March => 59,
April => 90, May => 120, June => 151,
July => 181, August => 212, September => 243,
October => 273, November => 304, December => 334,
}
}
pub fn months_from_january(self) -> usize {
match self {
January => 0, February => 1, March => 2,
April => 3, May => 4, June => 5,
July => 6, August => 7, September => 8,
October => 9, November => 10, December => 11,
}
}
pub fn from_one(month: i8) -> Result<Self, Error> {
Ok(match month {
1 => January, 2 => February, 3 => March,
4 => April, 5 => May, 6 => June,
7 => July, 8 => August, 9 => September,
10 => October, 11 => November, 12 => December,
_ => return Err(Error::OutOfRange),
})
}
pub fn from_zero(month: i8) -> Result<Self, Error> {
Ok(match month {
0 => January, 1 => February, 2 => March,
3 => April, 4 => May, 5 => June,
6 => July, 7 => August, 8 => September,
9 => October, 10 => November, 11 => December,
_ => return Err(Error::OutOfRange),
})
}
}
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum Weekday {
Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday,
}
impl Weekday {
fn days_from_monday_as_one(self) -> i8 {
match self {
Sunday => 7, Monday => 1,
Tuesday => 2, Wednesday => 3,
Thursday => 4, Friday => 5,
Saturday => 6,
}
}
pub fn from_zero(weekday: i8) -> Result<Self, Error> {
Ok(match weekday {
0 => Sunday, 1 => Monday, 2 => Tuesday,
3 => Wednesday, 4 => Thursday, 5 => Friday,
6 => Saturday, _ => return Err(Error::OutOfRange),
})
}
pub fn from_one(weekday: i8) -> Result<Self, Error> {
Ok(match weekday {
7 => Sunday, 1 => Monday, 2 => Tuesday,
3 => Wednesday, 4 => Thursday, 5 => Friday,
6 => Saturday, _ => return Err(Error::OutOfRange),
})
}
}
#[cfg(test)]
mod test {
pub(crate) use super::{LocalDateTime, LocalDate, LocalTime, Month};
#[test]
fn some_leap_years() {
for year in [2004,2008,2012,2016].iter() {
assert!(LocalDate::ymd(*year, Month::February, 29).is_ok());
assert!(LocalDate::ymd(*year + 1, Month::February, 29).is_err());
}
assert!(LocalDate::ymd(1600,Month::February,29).is_ok());
assert!(LocalDate::ymd(1601,Month::February,29).is_err());
assert!(LocalDate::ymd(1602,Month::February,29).is_err());
}
#[test]
fn new() {
for year in 1..3000 {
assert!(LocalDate::ymd(year, Month::from_one( 1).unwrap(), 32).is_err()); assert!(LocalDate::ymd(year, Month::from_one( 2).unwrap(), 30).is_err()); assert!(LocalDate::ymd(year, Month::from_one( 3).unwrap(), 32).is_err());
assert!(LocalDate::ymd(year, Month::from_one( 4).unwrap(), 31).is_err()); assert!(LocalDate::ymd(year, Month::from_one( 5).unwrap(), 32).is_err()); assert!(LocalDate::ymd(year, Month::from_one( 6).unwrap(), 31).is_err());
assert!(LocalDate::ymd(year, Month::from_one( 7).unwrap(), 32).is_err()); assert!(LocalDate::ymd(year, Month::from_one( 8).unwrap(), 32).is_err()); assert!(LocalDate::ymd(year, Month::from_one( 9).unwrap(), 31).is_err());
assert!(LocalDate::ymd(year, Month::from_one(10).unwrap(), 32).is_err()); assert!(LocalDate::ymd(year, Month::from_one(11).unwrap(), 31).is_err()); assert!(LocalDate::ymd(year, Month::from_one(12).unwrap(), 32).is_err());
}
}
#[test]
fn to_from_days_since_epoch() {
let epoch_difference: i64 = 30 * 365 + 7 + 31 + 29; for date in vec![
LocalDate::ymd(1970, Month::from_one(01).unwrap(), 01).unwrap(),
LocalDate::ymd( 01, Month::from_one(01).unwrap(), 01).unwrap(),
LocalDate::ymd(1971, Month::from_one(01).unwrap(), 01).unwrap(),
LocalDate::ymd(1973, Month::from_one(01).unwrap(), 01).unwrap(),
LocalDate::ymd(1977, Month::from_one(01).unwrap(), 01).unwrap(),
LocalDate::ymd(1989, Month::from_one(11).unwrap(), 10).unwrap(),
LocalDate::ymd(1990, Month::from_one( 7).unwrap(), 8).unwrap(),
LocalDate::ymd(2014, Month::from_one( 7).unwrap(), 13).unwrap(),
LocalDate::ymd(2001, Month::from_one( 2).unwrap(), 03).unwrap()
]{
assert_eq!( date,
LocalDate::from_days_since_epoch(
date.ymd.to_days_since_epoch().unwrap() - epoch_difference));
}
}
mod debug {
use super::*;
#[test]
fn recently() {
let date = LocalDate::ymd(1600, Month::February, 28).unwrap();
let debugged = format!("{:?}", date);
assert_eq!(debugged, "LocalDate(1600-02-28)");
}
#[test]
fn just_then() {
let date = LocalDate::ymd(-753, Month::December, 1).unwrap();
let debugged = format!("{:?}", date);
assert_eq!(debugged, "LocalDate(-0753-12-01)");
}
#[test]
fn far_far_future() {
let date = LocalDate::ymd(10601, Month::January, 31).unwrap();
let debugged = format!("{:?}", date);
assert_eq!(debugged, "LocalDate(+10601-01-31)");
}
#[test]
fn midday() {
let time = LocalTime::hms(12, 0, 0).unwrap();
let debugged = format!("{:?}", time);
assert_eq!(debugged, "LocalTime(12:00:00.000)");
}
#[test]
fn ascending() {
let then = LocalDateTime::new(
LocalDate::ymd(2009, Month::February, 13).unwrap(),
LocalTime::hms(23, 31, 30).unwrap());
let debugged = format!("{:?}", then);
assert_eq!(debugged, "LocalDateTime(2009-02-13T23:31:30.000)");
}
}
}