1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use std::fmt;
use std::cmp;

#[derive(Clone, Debug, PartialEq, Eq, Copy)]
pub struct Date {
    pub weekday: i64,
    pub year: i64,
    pub month: i64,
    pub day: i64,
    pub hour: i64,
    pub minute: i64,
    pub second: i64,
}

impl Date {
    pub fn from<S: Into<String>>(wd: S, d: S, t: S) -> Result<Date, String> {
        let weekday = wd.into();
        let date = d.into();
        let time = t.into();

        // Parses from `weekday year/month/day hour:minute:second` format as
        // specified in OpenBSD man page
        let mut result = Date::new();
        result.weekday = weekday.parse::<i64>().expect("Error parsing weekday");
        if result.weekday < 0 || result.weekday > 6 {
            return Err(format!("Weekday should be a number between 0 and 6. {} is not", weekday));
        }

        let d: Vec<&str> = date.split('/').collect();
        if d.len() != 3 {
            return Err(format!("{} does not have expected date format (YYYY/MM/DD)", date));
        }
        result.year = d[0].to_string().parse::<i64>().expect("Year should be a number");
        result.month = d[1].to_string().parse::<i64>().expect("Month should be a number");
        if result.month < 1 {
            return Err(format!("Month should be a number >= 1. {} is not", result.month));
        }
        result.day = d[2].to_string().parse::<i64>().expect("Day should be a number");
        if result.day < 1 {
            return Err(format!("Day should be a number between >= 1. {} is not", result.day));
        }

        let t: Vec<&str> = time.split(':').collect();
        if t.len() != 3 {
            return Err(format!("{} does not have expected time format (HH:mm:ss)", time));
        }
        result.hour = t[0].to_string().parse::<i64>().expect("Hour should be a number");
        if result.hour < 0 || result.hour > 23 {
            return Err(format!("Hour should be a number between 0 and 23. {} is not", result.hour));
        }
        result.minute = t[1].to_string().parse::<i64>().expect("Minute should be a number");
        if result.minute < 0 || result.hour > 59 {
            return Err(format!("Minute should be a number between 0 and 59. {} is not", result.minute));
        }
        result.second = t[2].to_string().parse::<i64>().expect("Second should be a number");
        if result.hour < 0 || result.hour > 59 {
            return Err(format!("Second should be a number between 0 and 59. {} is not", result.second));
        }

        Ok(result)
    }

    /// Transforms UTC datetime in RFC3339 format into `Date` object
    pub fn from_rfc3339<S: AsRef<str>>(weekday: u8, input: S) -> Result<Date, String> {
        let input_s = input.as_ref();
        let parts: Vec<&str> = input_s.split('T').collect();

        if parts.len() != 2 || parts[1].len() < 8 {
            return Err(format!("This doesn't seem like a correct RFC3339 date: {:?}", input_s));
        }

        let date = parts[0].replace("-", "/");
        let time = parts[1].split_at(8).0.to_string();

        Date::from(weekday.to_string(), date, time)
    }

    pub fn new() -> Date {
        Date {
            weekday: 0,
            year: 1970,
            month: 1,
            day: 1,
            hour: 0,
            minute: 0,
            second: 0,
        }
    }
    fn weekday_to_string(self) -> String {
        match self.weekday {
            0 => "Sunday".to_owned(),
            1 => "Monday".to_owned(),
            2 => "Tuesday".to_owned(),
            3 => "Wednesday".to_owned(),
            4 => "Thursday".to_owned(),
            5 => "Friday".to_owned(),
            6 => "Saturday".to_owned(),
            _ => "Not a valid weekday".to_owned(),
        }
    }
}

impl fmt::Display for Date {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,"{} {}/{:0>2}/{:0>2} {:0>2}:{:0>2}:{:0>2}",
            self.weekday_to_string(),
            self.year,
            self.month,
            self.day,
            self.hour,
            self.minute,
            self.second,
        )
    }
}

impl cmp::PartialOrd for Date {
    fn partial_cmp(&self, other: &Date) -> Option<cmp::Ordering> {
        if self.year != other.year {
            return self.year.partial_cmp(&other.year);
        }

        if self.month != other.month {
            return self.month.partial_cmp(&other.month);
        }

        if self.day != other.day {
            return self.day.partial_cmp(&other.day);
        }

        if self.hour != other.hour {
            return self.hour.partial_cmp(&other.hour);
        }

        if self.minute != other.minute {
            return self.minute.partial_cmp(&other.minute);
        }

        if self.second != other.second {
            return self.second.partial_cmp(&other.second);
        }

        None
    }
}

impl cmp::Ord for Date {
    fn cmp(&self, other: &Date) -> cmp::Ordering {
        return self.partial_cmp(other).unwrap();
    }
}