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
use std::time::UNIX_EPOCH;
use std::time::SystemTime;

static DAY_NAMES:[&str; 7] = ["Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed"];
static DAYS_PER_MONTH: [i8; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
static MONTH_NAMES:[&str; 12] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

#[derive(Debug)]
pub struct RFC2822Date {
  year: i16,
  month: i8,
  day: i8,
  hour: i8,
  minute: i8,
  second: i8,
  millis: i16,
  weekday: String,
}

impl RFC2822Date {
  pub fn to_string(&self) -> String {
    let day: String = pad_two(self.day + 1);
    let month = MONTH_NAMES[self.month as usize];
    let hour = pad_two(self.hour);
    let minute = pad_two(self.minute);
    let second = pad_two(self.second);
    
    format!("{}, {} {} {} {}:{}:{} +0000", self.weekday, day, month, self.year, hour, minute, second)
  }

  pub fn now() -> RFC2822Date {
    let millis = SystemTime::now()
                    .duration_since(UNIX_EPOCH)
                    .expect("error")
                    .as_millis()
                    .try_into()
                    .unwrap();
    RFC2822Date::new(millis)
  }
  
  pub fn new(millis: i64) -> RFC2822Date {
    let mut d = RFC2822Date {
      year: 0,
      month: 0,
      day: 0,
      hour: 0,
      minute: 0,
      second: 0,
      millis: 0,
      weekday: "".to_string(),
    };
    
    d.millis = (millis % 1000) as i16;
    let seconds = millis / 1000;
    d.second = (seconds % 60) as i8;
    let minutes = seconds / 60;
    d.minute = (minutes % 60) as i8;
    let hours = minutes / 60;
    d.hour = (hours % 24) as i8;
    
    let mut days = hours / 24;
    let day_of_week = days % 7;
    d.weekday = DAY_NAMES[day_of_week as usize].to_string();
    
    let mut year = 0;
    let mut leap;
    loop  {
      let num_days = num_days(year+1970) as i64;
      leap = num_days == 366;
      if days < num_days { break; }
      year += 1;
      days -= num_days;
    }
    d.year = year + 1970 as i16;
    
    let mut month = 0;
    loop {
      let mut num_days = DAYS_PER_MONTH[month] as i64;
      if month == 1 && leap { num_days += 1; }
      if days < num_days { break; }
      month += 1;
      days -= num_days;
    }
    d.month = month as i8;
    d.day = days as i8;
    
    d
  }
}

fn pad_two(i: i8) -> String {
  let mut s = i.to_string();
  if s.len() < 2 { s = "0".to_string() + &s; }
  s
}

fn num_days(year:i16) -> i16 {
  if year % 4 == 0 {
    if year % 400 == 0 || year % 100 != 0 {
      return 366;
    }
  }
  365
}