time.rs 6.6 KB
Newer Older
1
use datetime::{LocalDateTime, TimeZone, DatePiece, TimePiece};
B
Benjamin Sago 已提交
2 3
use datetime::fmt::DateFormat;
use locale;
4
use std::cmp;
B
Benjamin Sago 已提交
5

6
use fs::fields::Time;
B
Benjamin Sago 已提交
7 8


B
Benjamin Sago 已提交
9 10
pub enum TimeFormat {
    DefaultFormat(DefaultFormat),
B
Benjamin Sago 已提交
11
    ISOFormat(ISOFormat),
B
Benjamin Sago 已提交
12 13
    LongISO,
    FullISO,
B
Benjamin Sago 已提交
14 15 16 17 18 19
}

impl TimeFormat {
    pub fn format_local(&self, time: Time) -> String {
        match *self {
            TimeFormat::DefaultFormat(ref fmt) => fmt.format_local(time),
B
Benjamin Sago 已提交
20
            TimeFormat::ISOFormat(ref iso)     => iso.format_local(time),
B
Benjamin Sago 已提交
21 22
            TimeFormat::LongISO                => long_local(time),
            TimeFormat::FullISO                => full_local(time),
B
Benjamin Sago 已提交
23 24 25 26 27 28
        }
    }

    pub fn format_zoned(&self, time: Time, zone: &TimeZone) -> String {
        match *self {
            TimeFormat::DefaultFormat(ref fmt) => fmt.format_zoned(time, zone),
B
Benjamin Sago 已提交
29
            TimeFormat::ISOFormat(ref iso)     => iso.format_zoned(time, zone),
B
Benjamin Sago 已提交
30 31
            TimeFormat::LongISO                => long_zoned(time, zone),
            TimeFormat::FullISO                => full_zoned(time, zone),
B
Benjamin Sago 已提交
32 33 34 35 36
        }
    }
}


B
Benjamin Sago 已提交
37
#[derive(Debug, Clone)]
B
Benjamin Sago 已提交
38
pub struct DefaultFormat {
B
Benjamin Sago 已提交
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53

    /// The year of the current time. This gets used to determine which date
    /// format to use.
    pub current_year: i64,

    /// Localisation rules for formatting timestamps.
    pub locale: locale::Time,

    /// Date format for printing out timestamps that are in the current year.
    pub date_and_time: DateFormat<'static>,

    /// Date format for printing out timestamps that *aren’t*.
    pub date_and_year: DateFormat<'static>,
}

B
Benjamin Sago 已提交
54 55 56 57 58 59 60 61 62 63
impl DefaultFormat {
    pub fn new() -> DefaultFormat {
        use unicode_width::UnicodeWidthStr;

        let locale = locale::Time::load_user_locale()
                       .unwrap_or_else(|_| locale::Time::english());

        let current_year = LocalDateTime::now().year();

        // Some locales use a three-character wide month name (Jan to Dec);
64 65 66 67 68 69 70 71 72 73 74
        // others vary between three to four (1月 to 12月, juil.). We check each month width
        // to detect the longest and set the output format accordingly.
        let mut maximum_month_width = 0;
        for i in 0..11 {
            let current_month_width = UnicodeWidthStr::width(&*locale.short_month_name(i));
            maximum_month_width = cmp::max(maximum_month_width, current_month_width);
        }

        let date_and_time = match maximum_month_width {
            4  => DateFormat::parse("{2>:D} {4<:M} {2>:h}:{02>:m}").unwrap(),
            5  => DateFormat::parse("{2>:D} {5<:M} {2>:h}:{02>:m}").unwrap(),
B
Benjamin Sago 已提交
75 76 77
            _  => DateFormat::parse("{2>:D} {:M} {2>:h}:{02>:m}").unwrap(),
        };

78 79 80
        let date_and_year = match maximum_month_width {
            4 => DateFormat::parse("{2>:D} {4<:M} {5>:Y}").unwrap(),
            5 => DateFormat::parse("{2>:D} {5<:M} {5>:Y}").unwrap(),
B
Benjamin Sago 已提交
81 82 83 84 85 86
            _ => DateFormat::parse("{2>:D} {:M} {5>:Y}").unwrap()
        };

        DefaultFormat { current_year, locale, date_and_time, date_and_year }
    }

B
Benjamin Sago 已提交
87 88 89 90 91
    fn is_recent(&self, date: LocalDateTime) -> bool {
        date.year() == self.current_year
    }

    #[allow(trivial_numeric_casts)]
B
Benjamin Sago 已提交
92
    fn format_local(&self, time: Time) -> String {
93
        let date = LocalDateTime::at(time.seconds as i64);
B
Benjamin Sago 已提交
94 95 96 97 98 99 100 101 102 103

        if self.is_recent(date) {
            self.date_and_time.format(&date, &self.locale)
        }
        else {
            self.date_and_year.format(&date, &self.locale)
        }
    }

    #[allow(trivial_numeric_casts)]
B
Benjamin Sago 已提交
104
    fn format_zoned(&self, time: Time, zone: &TimeZone) -> String {
105
        let date = zone.to_zoned(LocalDateTime::at(time.seconds as i64));
B
Benjamin Sago 已提交
106 107 108 109 110 111 112 113 114

        if self.is_recent(date) {
            self.date_and_time.format(&date, &self.locale)
        }
        else {
            self.date_and_year.format(&date, &self.locale)
        }
    }
}
115 116


B
Benjamin Sago 已提交
117 118 119 120 121 122 123
#[allow(trivial_numeric_casts)]
fn long_local(time: Time) -> String {
    let date = LocalDateTime::at(time.seconds as i64);
    format!("{:04}-{:02}-{:02} {:02}:{:02}",
            date.year(), date.month() as usize, date.day(),
            date.hour(), date.minute())
}
124

B
Benjamin Sago 已提交
125 126 127 128 129 130 131
#[allow(trivial_numeric_casts)]
fn long_zoned(time: Time, zone: &TimeZone) -> String {
    let date = zone.to_zoned(LocalDateTime::at(time.seconds as i64));
    format!("{:04}-{:02}-{:02} {:02}:{:02}",
            date.year(), date.month() as usize, date.day(),
            date.hour(), date.minute())
}
132

B
Benjamin Sago 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152

#[allow(trivial_numeric_casts)]
fn full_local(time: Time) -> String {
    let date = LocalDateTime::at(time.seconds as i64);
    format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:09}",
            date.year(), date.month() as usize, date.day(),
            date.hour(), date.minute(), date.second(), time.nanoseconds)
}

#[allow(trivial_numeric_casts)]
fn full_zoned(time: Time, zone: &TimeZone) -> String {
    use datetime::Offset;

    let local = LocalDateTime::at(time.seconds as i64);
    let date = zone.to_zoned(local);
    let offset = Offset::of_seconds(zone.offset(local) as i32).expect("Offset out of range");
    format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:09} {:+03}{:02}",
            date.year(), date.month() as usize, date.day(),
            date.hour(), date.minute(), date.second(), time.nanoseconds,
            offset.hours(), offset.minutes().abs())
153
}
B
Benjamin Sago 已提交
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183



#[derive(Debug, Clone)]
pub struct ISOFormat {

    /// The year of the current time. This gets used to determine which date
    /// format to use.
    pub current_year: i64,
}

impl ISOFormat {
    pub fn new() -> Self {
        let current_year = LocalDateTime::now().year();
        ISOFormat { current_year }
    }

    fn is_recent(&self, date: LocalDateTime) -> bool {
        date.year() == self.current_year
    }

    #[allow(trivial_numeric_casts)]
    fn format_local(&self, time: Time) -> String {
        let date = LocalDateTime::at(time.seconds as i64);

        if self.is_recent(date) {
            format!("{:02}-{:02} {:02}:{:02}",
                    date.month() as usize, date.day(),
                    date.hour(), date.minute())
        }
184 185 186 187
        else {
            format!("{:04}-{:02}-{:02}",
                    date.year(), date.month() as usize, date.day())
        }
B
Benjamin Sago 已提交
188 189 190 191 192 193 194 195 196 197 198
    }

    #[allow(trivial_numeric_casts)]
    fn format_zoned(&self, time: Time, zone: &TimeZone) -> String {
        let date = zone.to_zoned(LocalDateTime::at(time.seconds as i64));

        if self.is_recent(date) {
            format!("{:02}-{:02} {:02}:{:02}",
                    date.month() as usize, date.day(),
                    date.hour(), date.minute())
        }
199 200 201 202
        else {
            format!("{:04}-{:02}-{:02}",
                    date.year(), date.month() as usize, date.day())
        }
B
Benjamin Sago 已提交
203 204
    }
}