Initial commit
This commit is contained in:
parent
aa103829c6
commit
20589405e2
|
@ -0,0 +1 @@
|
||||||
|
use_nix
|
|
@ -0,0 +1,25 @@
|
||||||
|
[package]
|
||||||
|
name = "international-fixed-calendar"
|
||||||
|
description = "chrono-compatible-ish date objects for the International Fixed Calendar"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Savanni D'Gerinel <savanni@luminescent-dreams.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
keywords = ["date", "time", "calendar"]
|
||||||
|
categories = ["date-and-time"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono = "0.4"
|
||||||
|
chrono-tz = "0.4"
|
||||||
|
iron = "0.6.1"
|
||||||
|
mustache = "0.9.0"
|
||||||
|
params = "*"
|
||||||
|
router = "*"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "ifc-today"
|
||||||
|
path = "src/today.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "ifc-web"
|
||||||
|
path = "src/web.rs"
|
|
@ -0,0 +1,8 @@
|
||||||
|
# International Fixed Calendar
|
||||||
|
|
||||||
|
This is a fun project implementing a library for the [International Fixed Calendar](https://en.wikipedia.org/wiki/International_Fixed_Calendar).
|
||||||
|
|
||||||
|
This is at least somewhat compatible with [Chrono](https://github.com/chronotope/chrono), in that I have implemented these traits:
|
||||||
|
|
||||||
|
* `From<NaiveDate>`
|
||||||
|
* `Datelike`
|
|
@ -0,0 +1,20 @@
|
||||||
|
let
|
||||||
|
rust_overlay = import (builtins.fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz");
|
||||||
|
pkgs = import <pkgs-21.05> { overlays = [ rust_overlay ]; };
|
||||||
|
unstable = import <unstable> {};
|
||||||
|
rust = pkgs.rust-bin.stable."1.55.0".default.override {
|
||||||
|
extensions = [ "rust-src" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
in pkgs.mkShell {
|
||||||
|
name = "ifc";
|
||||||
|
|
||||||
|
buildInputs = [
|
||||||
|
rust
|
||||||
|
unstable.rust-analyzer
|
||||||
|
];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
if [ -e ~/.nixpkgs/shellhook.sh ]; then . ~/.nixpkgs/shellhook.sh; fi
|
||||||
|
'';
|
||||||
|
}
|
|
@ -0,0 +1,664 @@
|
||||||
|
extern crate chrono;
|
||||||
|
extern crate chrono_tz;
|
||||||
|
|
||||||
|
use chrono::{Datelike, NaiveDate};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum DayOfWeek {
|
||||||
|
Sunday,
|
||||||
|
Monday,
|
||||||
|
Tuesday,
|
||||||
|
Wednesday,
|
||||||
|
Thursday,
|
||||||
|
Friday,
|
||||||
|
Saturday,
|
||||||
|
LeapDay,
|
||||||
|
YearDay,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DayOfWeek> for String {
|
||||||
|
fn from(day: DayOfWeek) -> Self {
|
||||||
|
match day {
|
||||||
|
DayOfWeek::Sunday => "Sunday",
|
||||||
|
DayOfWeek::Monday => "Monday",
|
||||||
|
DayOfWeek::Tuesday => "Tuesday",
|
||||||
|
DayOfWeek::Wednesday => "Wednesday",
|
||||||
|
DayOfWeek::Thursday => "Thursday",
|
||||||
|
DayOfWeek::Friday => "Friday",
|
||||||
|
DayOfWeek::Saturday => "Saturday",
|
||||||
|
DayOfWeek::LeapDay => "LeapDay",
|
||||||
|
DayOfWeek::YearDay => "YearDay",
|
||||||
|
}
|
||||||
|
.to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum Month {
|
||||||
|
January,
|
||||||
|
February,
|
||||||
|
March,
|
||||||
|
April,
|
||||||
|
May,
|
||||||
|
June,
|
||||||
|
Sol,
|
||||||
|
July,
|
||||||
|
August,
|
||||||
|
September,
|
||||||
|
October,
|
||||||
|
November,
|
||||||
|
December,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for Month {
|
||||||
|
fn from(val: u32) -> Month {
|
||||||
|
match val {
|
||||||
|
1 => Month::January,
|
||||||
|
2 => Month::February,
|
||||||
|
3 => Month::March,
|
||||||
|
4 => Month::April,
|
||||||
|
5 => Month::May,
|
||||||
|
6 => Month::June,
|
||||||
|
7 => Month::Sol,
|
||||||
|
8 => Month::July,
|
||||||
|
9 => Month::August,
|
||||||
|
10 => Month::September,
|
||||||
|
11 => Month::October,
|
||||||
|
12 => Month::November,
|
||||||
|
13 => Month::December,
|
||||||
|
_ => panic!("invalid month number"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Month> for String {
|
||||||
|
fn from(val: Month) -> String {
|
||||||
|
match val {
|
||||||
|
Month::January => "January",
|
||||||
|
Month::February => "February",
|
||||||
|
Month::March => "March",
|
||||||
|
Month::April => "April",
|
||||||
|
Month::May => "May",
|
||||||
|
Month::June => "June",
|
||||||
|
Month::Sol => "Sol",
|
||||||
|
Month::July => "July",
|
||||||
|
Month::August => "August",
|
||||||
|
Month::September => "September",
|
||||||
|
Month::October => "October",
|
||||||
|
Month::November => "November",
|
||||||
|
Month::December => "December",
|
||||||
|
}
|
||||||
|
.to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct IFC {
|
||||||
|
year: u32,
|
||||||
|
ordinal: u32,
|
||||||
|
leap_year: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_leap_year(year: i32) -> bool {
|
||||||
|
NaiveDate::from_ymd(year, 12, 31).ordinal() == 366
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IFC {
|
||||||
|
pub fn ymd(year: u32, month: u8, day: u8) -> IFC {
|
||||||
|
let leap_year = is_leap_year(year as i32 - 10000);
|
||||||
|
|
||||||
|
let ordinal = if is_leap_year(year as i32 - 10000) {
|
||||||
|
if month == 6 && day == 29 {
|
||||||
|
168
|
||||||
|
} else if month > 6 {
|
||||||
|
(month as u32 - 1) * 28 + (day as u32)
|
||||||
|
} else {
|
||||||
|
(month as u32 - 1) * 28 + (day as u32) - 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(month as u32 - 1) * 28 + (day as u32) - 1
|
||||||
|
};
|
||||||
|
|
||||||
|
IFC {
|
||||||
|
year,
|
||||||
|
ordinal,
|
||||||
|
leap_year,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn weekday_ifc(&self) -> DayOfWeek {
|
||||||
|
match self.day() % 7 {
|
||||||
|
0 => DayOfWeek::Saturday,
|
||||||
|
1 => DayOfWeek::Sunday,
|
||||||
|
2 => DayOfWeek::Monday,
|
||||||
|
3 => DayOfWeek::Tuesday,
|
||||||
|
4 => DayOfWeek::Wednesday,
|
||||||
|
5 => DayOfWeek::Thursday,
|
||||||
|
6 => DayOfWeek::Friday,
|
||||||
|
_ => panic!("impossible condition"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn month_ifc(&self) -> Month {
|
||||||
|
Month::from(self.month())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<chrono::Date<chrono::Utc>> for IFC {
|
||||||
|
fn from(d: chrono::Date<chrono::Utc>) -> IFC {
|
||||||
|
IFC::from(d.naive_utc())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<chrono::NaiveDate> for IFC {
|
||||||
|
fn from(d: NaiveDate) -> IFC {
|
||||||
|
//println!("d: {} [{}]", d.format("%Y-%m-%d"), d.ordinal());
|
||||||
|
IFC {
|
||||||
|
year: (d.year() + 10000) as u32,
|
||||||
|
ordinal: d.ordinal0(),
|
||||||
|
leap_year: is_leap_year(d.year()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Datelike for IFC {
|
||||||
|
fn year(&self) -> i32 {
|
||||||
|
self.year as i32
|
||||||
|
}
|
||||||
|
fn month(&self) -> u32 {
|
||||||
|
self.month0() + 1
|
||||||
|
}
|
||||||
|
fn month0(&self) -> u32 {
|
||||||
|
if self.leap_year && self.ordinal == 365 {
|
||||||
|
12
|
||||||
|
} else if self.leap_year && self.ordinal == 168 {
|
||||||
|
5
|
||||||
|
} else {
|
||||||
|
self.ordinal / 28
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn day(&self) -> u32 {
|
||||||
|
self.day0() + 1
|
||||||
|
}
|
||||||
|
fn day0(&self) -> u32 {
|
||||||
|
if self.leap_year {
|
||||||
|
if self.ordinal == 365 {
|
||||||
|
28
|
||||||
|
} else if self.ordinal == 168 {
|
||||||
|
28
|
||||||
|
} else if self.ordinal > 168 {
|
||||||
|
(self.ordinal - 1).rem_euclid(28) as u32
|
||||||
|
} else {
|
||||||
|
self.ordinal.rem_euclid(28) as u32
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if self.ordinal == 364 {
|
||||||
|
28
|
||||||
|
} else {
|
||||||
|
self.ordinal.rem_euclid(28) as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn ordinal(&self) -> u32 {
|
||||||
|
self.ordinal + 1
|
||||||
|
}
|
||||||
|
fn ordinal0(&self) -> u32 {
|
||||||
|
self.ordinal
|
||||||
|
}
|
||||||
|
fn weekday(&self) -> chrono::Weekday {
|
||||||
|
if self.day0() == 28 {
|
||||||
|
chrono::Weekday::Sat
|
||||||
|
} else {
|
||||||
|
match self.day0().rem_euclid(7) {
|
||||||
|
0 => chrono::Weekday::Sun,
|
||||||
|
1 => chrono::Weekday::Mon,
|
||||||
|
2 => chrono::Weekday::Tue,
|
||||||
|
3 => chrono::Weekday::Wed,
|
||||||
|
4 => chrono::Weekday::Thu,
|
||||||
|
5 => chrono::Weekday::Fri,
|
||||||
|
6 => chrono::Weekday::Sat,
|
||||||
|
_ => panic!("rem_euclid should not return anything outside the 0..6 range"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn iso_week(&self) -> chrono::IsoWeek {
|
||||||
|
panic!("iso_week is not implemented because chrono does not expose any constructors for IsoWeek!");
|
||||||
|
}
|
||||||
|
fn with_year(&self, year: i32) -> Option<IFC> {
|
||||||
|
Some(IFC {
|
||||||
|
year: (year as u32) + 10000,
|
||||||
|
ordinal: self.ordinal,
|
||||||
|
leap_year: is_leap_year(year),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn with_month(&self, month: u32) -> Option<IFC> {
|
||||||
|
Some(IFC::ymd(self.year, month as u8, self.day() as u8))
|
||||||
|
}
|
||||||
|
fn with_month0(&self, month: u32) -> Option<IFC> {
|
||||||
|
Some(IFC::ymd(self.year, month as u8 + 1, self.day() as u8))
|
||||||
|
}
|
||||||
|
fn with_day(&self, day: u32) -> Option<IFC> {
|
||||||
|
Some(IFC::ymd(self.year, self.month() as u8, day as u8))
|
||||||
|
}
|
||||||
|
fn with_day0(&self, day: u32) -> Option<IFC> {
|
||||||
|
Some(IFC::ymd(self.year, self.month() as u8, day as u8 + 1))
|
||||||
|
}
|
||||||
|
fn with_ordinal(&self, ordinal: u32) -> Option<IFC> {
|
||||||
|
Some(IFC {
|
||||||
|
year: self.year,
|
||||||
|
ordinal,
|
||||||
|
leap_year: self.leap_year,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn with_ordinal0(&self, ordinal: u32) -> Option<IFC> {
|
||||||
|
Some(IFC {
|
||||||
|
year: self.year,
|
||||||
|
ordinal: ordinal + 1,
|
||||||
|
leap_year: self.leap_year,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use chrono::NaiveDate;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_start_of_month() {
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12019, 1, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12019,
|
||||||
|
ordinal: 0,
|
||||||
|
leap_year: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12019, 2, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12019,
|
||||||
|
ordinal: 28,
|
||||||
|
leap_year: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12019, 3, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12019,
|
||||||
|
ordinal: 56,
|
||||||
|
leap_year: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12019, 4, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12019,
|
||||||
|
ordinal: 84,
|
||||||
|
leap_year: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12019, 5, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12019,
|
||||||
|
ordinal: 112,
|
||||||
|
leap_year: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12019, 6, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12019,
|
||||||
|
ordinal: 140,
|
||||||
|
leap_year: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12019, 7, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12019,
|
||||||
|
ordinal: 168,
|
||||||
|
leap_year: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12019, 8, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12019,
|
||||||
|
ordinal: 196,
|
||||||
|
leap_year: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12019, 9, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12019,
|
||||||
|
ordinal: 224,
|
||||||
|
leap_year: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12019, 10, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12019,
|
||||||
|
ordinal: 252,
|
||||||
|
leap_year: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12019, 11, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12019,
|
||||||
|
ordinal: 280,
|
||||||
|
leap_year: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12019, 12, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12019,
|
||||||
|
ordinal: 308,
|
||||||
|
leap_year: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12019, 13, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12019,
|
||||||
|
ordinal: 336,
|
||||||
|
leap_year: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_start_of_month_leap_year() {
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12020, 1, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12020,
|
||||||
|
ordinal: 0,
|
||||||
|
leap_year: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12020, 2, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12020,
|
||||||
|
ordinal: 28,
|
||||||
|
leap_year: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12020, 3, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12020,
|
||||||
|
ordinal: 56,
|
||||||
|
leap_year: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12020, 4, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12020,
|
||||||
|
ordinal: 84,
|
||||||
|
leap_year: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12020, 5, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12020,
|
||||||
|
ordinal: 112,
|
||||||
|
leap_year: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12020, 6, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12020,
|
||||||
|
ordinal: 140,
|
||||||
|
leap_year: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12020, 6, 29),
|
||||||
|
IFC {
|
||||||
|
year: 12020,
|
||||||
|
ordinal: 168,
|
||||||
|
leap_year: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12020, 7, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12020,
|
||||||
|
ordinal: 169,
|
||||||
|
leap_year: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12020, 8, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12020,
|
||||||
|
ordinal: 197,
|
||||||
|
leap_year: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12020, 9, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12020,
|
||||||
|
ordinal: 225,
|
||||||
|
leap_year: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12020, 10, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12020,
|
||||||
|
ordinal: 253,
|
||||||
|
leap_year: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12020, 11, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12020,
|
||||||
|
ordinal: 281,
|
||||||
|
leap_year: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12020, 12, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12020,
|
||||||
|
ordinal: 309,
|
||||||
|
leap_year: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::ymd(12020, 13, 1),
|
||||||
|
IFC {
|
||||||
|
year: 12020,
|
||||||
|
ordinal: 337,
|
||||||
|
leap_year: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn it_matches_january_1() {
|
||||||
|
assert_eq!(
|
||||||
|
IFC::from(NaiveDate::from_ymd(2019, 1, 1)),
|
||||||
|
IFC::ymd(12019, 1, 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_matches_february_1() {
|
||||||
|
assert_eq!(
|
||||||
|
IFC::from(NaiveDate::from_ymd(2019, 1, 29)),
|
||||||
|
IFC::ymd(12019, 2, 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_matches_sol_1() {
|
||||||
|
assert_eq!(
|
||||||
|
IFC::from(NaiveDate::from_ymd(2019, 6, 18)),
|
||||||
|
IFC::ymd(12019, 7, 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_matches_year_day() {
|
||||||
|
assert_eq!(
|
||||||
|
IFC::from(NaiveDate::from_ymd(2019, 12, 31)),
|
||||||
|
IFC::ymd(12019, 13, 29)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_matches_leap_day() {
|
||||||
|
assert_eq!(
|
||||||
|
IFC::from(NaiveDate::from_ymd(2019, 6, 18)),
|
||||||
|
IFC::ymd(12019, 7, 1),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::from(NaiveDate::from_ymd(2020, 6, 17)),
|
||||||
|
IFC::ymd(12020, 6, 29),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::from(NaiveDate::from_ymd(2020, 6, 18)),
|
||||||
|
IFC::ymd(12020, 7, 1),
|
||||||
|
);
|
||||||
|
assert_ne!(IFC::ymd(12020, 6, 29), IFC::ymd(12020, 7, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_handles_gregorian_leap_day() {
|
||||||
|
assert_eq!(
|
||||||
|
IFC::from(NaiveDate::from_ymd(2019, 3, 1)),
|
||||||
|
IFC::ymd(12019, 3, 4)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::from(NaiveDate::from_ymd(2020, 2, 29)),
|
||||||
|
IFC::ymd(12020, 3, 4)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_handles_days_between_leap_days() {
|
||||||
|
assert_eq!(
|
||||||
|
IFC::from(NaiveDate::from_ymd(2020, 4, 8)),
|
||||||
|
IFC::ymd(12020, 4, 15),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_handles_days_after_ifc_leap_day() {
|
||||||
|
assert_eq!(
|
||||||
|
IFC::from(NaiveDate::from_ymd(2020, 10, 8)),
|
||||||
|
IFC::ymd(12020, 11, 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_matches_year_day_in_leap_year() {
|
||||||
|
assert_eq!(NaiveDate::from_ymd(2020, 12, 31).ordinal(), 366);
|
||||||
|
assert_eq!(
|
||||||
|
IFC::from(NaiveDate::from_ymd(2020, 12, 31)),
|
||||||
|
IFC::ymd(12020, 13, 29)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_reports_correct_month() {
|
||||||
|
assert_eq!(IFC::ymd(12019, 1, 1).month(), 1);
|
||||||
|
assert_eq!(IFC::ymd(12019, 1, 1).month0(), 0);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12020, 6, 28).month(), 6);
|
||||||
|
assert_eq!(IFC::ymd(12020, 6, 28).month0(), 5);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12020, 7, 1).month(), 7);
|
||||||
|
assert_eq!(IFC::ymd(12020, 7, 1).month0(), 6);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12019, 13, 1).month(), 13);
|
||||||
|
assert_eq!(IFC::ymd(12019, 13, 1).month0(), 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_reports_correct_day() {
|
||||||
|
assert_eq!(IFC::ymd(12019, 1, 1).day(), 1);
|
||||||
|
assert_eq!(IFC::ymd(12019, 1, 1).day0(), 0);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12020, 3, 1).day(), 1);
|
||||||
|
assert_eq!(IFC::ymd(12020, 3, 1).day0(), 0);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12020, 6, 28).day(), 28);
|
||||||
|
assert_eq!(IFC::ymd(12020, 6, 28).day0(), 27);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12020, 7, 1).day(), 1);
|
||||||
|
assert_eq!(IFC::ymd(12020, 7, 1).day0(), 0);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12019, 13, 1).day(), 1);
|
||||||
|
assert_eq!(IFC::ymd(12019, 13, 1).day0(), 0);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12019, 13, 29).day(), 29);
|
||||||
|
assert_eq!(IFC::ymd(12019, 13, 29).day0(), 28);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_reports_correct_month_in_leap_year() {
|
||||||
|
assert_eq!(IFC::ymd(12020, 1, 1).month(), 1);
|
||||||
|
assert_eq!(IFC::ymd(12020, 1, 1).month0(), 0);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12020, 3, 1).month(), 3);
|
||||||
|
assert_eq!(IFC::ymd(12020, 3, 1).month0(), 2);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12020, 6, 29).month(), 6);
|
||||||
|
assert_eq!(IFC::ymd(12020, 6, 29).month0(), 5);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12020, 7, 1).month(), 7);
|
||||||
|
assert_eq!(IFC::ymd(12020, 7, 1).month0(), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_reports_correct_day_in_leap_year() {
|
||||||
|
assert_eq!(IFC::ymd(12019, 1, 1).day(), 1);
|
||||||
|
assert_eq!(IFC::ymd(12019, 1, 1).day0(), 0);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12020, 6, 28).day(), 28);
|
||||||
|
assert_eq!(IFC::ymd(12020, 6, 28).day0(), 27);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12020, 6, 29).day(), 29);
|
||||||
|
assert_eq!(IFC::ymd(12020, 6, 29).day0(), 28);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12020, 7, 1).day(), 1);
|
||||||
|
assert_eq!(IFC::ymd(12020, 7, 1).day0(), 0);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12019, 13, 1).day(), 1);
|
||||||
|
assert_eq!(IFC::ymd(12019, 13, 1).day0(), 0);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12019, 13, 29).day(), 29);
|
||||||
|
assert_eq!(IFC::ymd(12019, 13, 29).day0(), 28);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_reports_correct_day_of_week() {
|
||||||
|
assert_eq!(IFC::ymd(12019, 1, 1).weekday(), chrono::Weekday::Sun);
|
||||||
|
assert_eq!(IFC::ymd(12019, 6, 1).weekday(), chrono::Weekday::Sun);
|
||||||
|
assert_eq!(IFC::ymd(12019, 6, 28).weekday(), chrono::Weekday::Sat);
|
||||||
|
assert_eq!(IFC::ymd(12019, 7, 1).weekday(), chrono::Weekday::Sun);
|
||||||
|
assert_eq!(IFC::ymd(12019, 13, 28).weekday(), chrono::Weekday::Sat);
|
||||||
|
assert_eq!(IFC::ymd(12019, 13, 29).weekday(), chrono::Weekday::Sat);
|
||||||
|
|
||||||
|
assert_eq!(IFC::ymd(12020, 6, 28).weekday(), chrono::Weekday::Sat);
|
||||||
|
assert_eq!(IFC::ymd(12020, 6, 29).weekday(), chrono::Weekday::Sat);
|
||||||
|
assert_eq!(IFC::ymd(12020, 7, 1).weekday(), chrono::Weekday::Sun);
|
||||||
|
assert_eq!(IFC::ymd(12020, 13, 28).weekday(), chrono::Weekday::Sat);
|
||||||
|
assert_eq!(IFC::ymd(12020, 13, 29).weekday(), chrono::Weekday::Sat);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title> {{month}} {{year}} </title>
|
||||||
|
<link href="/css" rel="stylesheet" type="text/css" media="screen" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1> IFC Fixed Calendar: {{month}}, {{year}} years after the invention of agriculture </h1>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th> Sunday </th>
|
||||||
|
<th> Monday </th>
|
||||||
|
<th> Tuesday </th>
|
||||||
|
<th> Wednesday </th>
|
||||||
|
<th> Thursday </th>
|
||||||
|
<th> Friday </th>
|
||||||
|
<th> Saturday </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{{#weeks}}
|
||||||
|
<tr>
|
||||||
|
{{#days}}
|
||||||
|
<td class="{{highlight}}"> {{day}} </td>
|
||||||
|
{{/days}}
|
||||||
|
</tr>
|
||||||
|
{{/weeks}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,35 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>{{month}} {{year}}</title>
|
||||||
|
<link href="/css" rel="stylesheet" type="text/css" media="screen" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>
|
||||||
|
IFC Fixed Calendar: {{month}}, {{year}} years after the invention of
|
||||||
|
agriculture
|
||||||
|
</h1>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Sunday</th>
|
||||||
|
<th>Monday</th>
|
||||||
|
<th>Tuesday</th>
|
||||||
|
<th>Wednesday</th>
|
||||||
|
<th>Thursday</th>
|
||||||
|
<th>Friday</th>
|
||||||
|
<th>Saturday</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{{#weeks}}
|
||||||
|
<tr>
|
||||||
|
{{#days}}
|
||||||
|
<td class="{{highlight}}">{{day}}</td>
|
||||||
|
{{/days}}
|
||||||
|
</tr>
|
||||||
|
{{/weeks}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>{{day_out_of_time}} {{year}}</title>
|
||||||
|
<link href="/css" rel="stylesheet" type="text/css" media="screen" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>
|
||||||
|
IFC Fixed Calendar: {{day_out_of_time}}, {{year}} years after the
|
||||||
|
invention of agriculture
|
||||||
|
</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 98%;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
width: 14%;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: larger;
|
||||||
|
border: 1px solid black;
|
||||||
|
padding: 1em 0em 5em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.today {
|
||||||
|
background-color: rgb(200, 200, 255);
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
extern crate chrono;
|
||||||
|
extern crate chrono_tz;
|
||||||
|
extern crate international_fixed_calendar as IFC;
|
||||||
|
|
||||||
|
use chrono::{Datelike, Utc};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let d = IFC::IFC::from(Utc::today());
|
||||||
|
println!("{} {}, {}", d.month(), d.day(), d.year());
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
use chrono::{Datelike, Utc};
|
||||||
|
use international_fixed_calendar as IFC;
|
||||||
|
use iron::headers;
|
||||||
|
use iron::middleware::Handler;
|
||||||
|
use iron::modifiers::Header;
|
||||||
|
use iron::prelude::*;
|
||||||
|
use iron::status;
|
||||||
|
use mustache::{compile_str, Template};
|
||||||
|
use router::Router;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
pub const STYLES: &'static str = include_str!("static/styles.css");
|
||||||
|
pub const INDEX: &'static str = include_str!("static/index.html");
|
||||||
|
|
||||||
|
pub struct IndexHandler {
|
||||||
|
pub template: Template,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct DayEntry {
|
||||||
|
day: u8,
|
||||||
|
highlight: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct Week {
|
||||||
|
days: Vec<DayEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Week {
|
||||||
|
fn new(start_day: u8, today: u8) -> Week {
|
||||||
|
Week {
|
||||||
|
days: (1..8)
|
||||||
|
.map(|d| DayEntry {
|
||||||
|
day: d + start_day,
|
||||||
|
highlight: if today == (d + start_day) {
|
||||||
|
String::from("today")
|
||||||
|
} else {
|
||||||
|
String::from("")
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct IndexTemplateParams {
|
||||||
|
month: String,
|
||||||
|
year: i32,
|
||||||
|
weeks: Vec<Week>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexTemplateParams {
|
||||||
|
fn new(date: IFC::IFC) -> IndexTemplateParams {
|
||||||
|
let day = date.day() as u8;
|
||||||
|
IndexTemplateParams {
|
||||||
|
month: String::from(IFC::Month::from(date.month())),
|
||||||
|
year: date.year(),
|
||||||
|
weeks: (0..4).map(|wn| Week::new(wn * 7, day)).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler for IndexHandler {
|
||||||
|
fn handle(&self, _: &mut Request) -> IronResult<Response> {
|
||||||
|
let d = IFC::IFC::from(Utc::today());
|
||||||
|
Ok(Response::with((
|
||||||
|
status::Ok,
|
||||||
|
Header(headers::ContentType(iron::mime::Mime(
|
||||||
|
iron::mime::TopLevel::Text,
|
||||||
|
iron::mime::SubLevel::Html,
|
||||||
|
vec![],
|
||||||
|
))),
|
||||||
|
self.template
|
||||||
|
.render_to_string(&IndexTemplateParams::new(d))
|
||||||
|
.expect("the template to render"),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn css(_: &mut Request) -> IronResult<Response> {
|
||||||
|
Ok(Response::with((
|
||||||
|
status::Ok,
|
||||||
|
Header(headers::ContentType(iron::mime::Mime(
|
||||||
|
iron::mime::TopLevel::Text,
|
||||||
|
iron::mime::SubLevel::Css,
|
||||||
|
vec![],
|
||||||
|
))),
|
||||||
|
STYLES,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut router = Router::new();
|
||||||
|
router.get(
|
||||||
|
"/",
|
||||||
|
IndexHandler {
|
||||||
|
template: compile_str(INDEX).expect("the template to compile"),
|
||||||
|
},
|
||||||
|
"index",
|
||||||
|
);
|
||||||
|
|
||||||
|
router.get("/css", css, "styles");
|
||||||
|
|
||||||
|
Iron::new(router).http("127.0.0.1:3000").unwrap();
|
||||||
|
}
|
Loading…
Reference in New Issue