Extract time formatter

This commit collects all the time-related fields from the Environment and bundles them all together in their own encapsulated struct.
This commit is contained in:
Benjamin Sago 2017-07-03 08:45:14 +01:00
parent fc60838ff3
commit 652e27e6dd
4 changed files with 115 additions and 86 deletions

View File

@ -8,6 +8,7 @@ pub mod file_name;
pub mod grid_details;
pub mod grid;
pub mod lines;
pub mod time;
mod cell;
mod colours;

View File

@ -1,45 +1,25 @@
use datetime::TimeZone;
use fs::fields as f;
use output::cell::TextCell;
use output::colours::Colours;
use fs::fields as f;
use datetime::{LocalDateTime, TimeZone, DatePiece};
use datetime::fmt::DateFormat;
use locale;
use output::time::TimeFormat;
#[allow(trivial_numeric_casts)]
impl f::Time {
pub fn render(&self, colours: &Colours, tz: &Option<TimeZone>,
date_and_time: &DateFormat<'static>, date_and_year: &DateFormat<'static>,
time: &locale::Time, current_year: i64) -> TextCell {
// TODO(ogham): This method needs some serious de-duping!
// zoned and local times have different types at the moment,
// so it's tricky.
pub fn render(&self, colours: &Colours,
tz: &Option<TimeZone>,
style: &TimeFormat) -> TextCell {
if let Some(ref tz) = *tz {
let date = tz.to_zoned(LocalDateTime::at(self.0 as i64));
let datestamp = if date.year() == current_year {
date_and_time.format(&date, time)
}
else {
date_and_year.format(&date, time)
};
let datestamp = style.format_zoned(self.0 as i64, tz);
TextCell::paint(colours.date, datestamp)
}
else {
let date = LocalDateTime::at(self.0 as i64);
let datestamp = if date.year() == current_year {
date_and_time.format(&date, time)
}
else {
date_and_year.format(&date, time)
};
let datestamp = style.format_local(self.0 as i64);
TextCell::paint(colours.date, datestamp)
}
}
}

View File

@ -1,8 +1,6 @@
use std::cmp::max;
use std::sync::{Mutex, MutexGuard};
use datetime::fmt::DateFormat;
use datetime::{LocalDateTime, DatePiece};
use datetime::TimeZone;
use zoneinfo_compiled::{CompiledData, Result as TZResult};
@ -13,6 +11,7 @@ use users::UsersCache;
use output::cell::TextCell;
use output::colours::Colours;
use output::column::{Alignment, Column};
use output::time::TimeFormat;
use fs::{File, fields as f};
@ -23,21 +22,11 @@ use fs::{File, fields as f};
/// Any environment field should be able to be mocked up for test runs.
pub struct Environment {
/// The year of the current time. This gets used to determine which date
/// format to use.
current_year: i64,
/// Localisation rules for formatting numbers.
numeric: locale::Numeric,
/// Localisation rules for formatting timestamps.
time: locale::Time,
/// Date format for printing out timestamps that are in the current year.
date_and_time: DateFormat<'static>,
/// Date format for printing out timestamps that *arent*.
date_and_year: DateFormat<'static>,
/// Rules for formatting timestamps.
time_format: TimeFormat,
/// The computer's current time zone. This gets used to determine how to
/// offset files' timestamps.
@ -55,43 +44,22 @@ impl Environment {
impl Default for Environment {
fn default() -> Self {
use unicode_width::UnicodeWidthStr;
let tz = match determine_time_zone() {
Ok(t) => Some(t),
Err(ref e) => {
println!("Unable to determine time zone: {}", e);
None
}
};
let tz = determine_time_zone();
if let Err(ref e) = tz {
println!("Unable to determine time zone: {}", e);
}
let time_format = TimeFormat::deduce();
let numeric = locale::Numeric::load_user_locale()
.unwrap_or_else(|_| locale::Numeric::english());
let time = locale::Time::load_user_locale()
.unwrap_or_else(|_| locale::Time::english());
let users = Mutex::new(UsersCache::new());
// Some locales use a three-character wide month name (Jan to Dec);
// others vary between three and four (1月 to 12月). We assume that
// December is the month with the maximum width, and use the width of
// that to determine how to pad the other months.
let december_width = UnicodeWidthStr::width(&*time.short_month_name(11));
let date_and_time = match december_width {
4 => DateFormat::parse("{2>:D} {4>:M} {2>:h}:{02>:m}").unwrap(),
_ => DateFormat::parse("{2>:D} {:M} {2>:h}:{02>:m}").unwrap(),
};
let date_and_year = match december_width {
4 => DateFormat::parse("{2>:D} {4>:M} {5>:Y}").unwrap(),
_ => DateFormat::parse("{2>:D} {:M} {5>:Y}").unwrap()
};
Environment {
current_year: LocalDateTime::now().year(),
numeric: numeric,
date_and_time: date_and_time,
date_and_year: date_and_year,
time: time,
tz: tz.ok(),
users: Mutex::new(UsersCache::new()),
}
Environment { tz, time_format, numeric, users }
}
}
@ -171,17 +139,18 @@ impl<'a, 'f> Table<'a> {
use output::column::TimeType::*;
match *column {
Column::Permissions => self.permissions_plus(file, xattrs).render(&self.colours),
Column::FileSize(fmt) => file.size().render(&self.colours, fmt, &self.env.numeric),
Column::Timestamp(Modified) => file.modified_time().render(&self.colours, &self.env.tz, &self.env.date_and_time, &self.env.date_and_year, &self.env.time, self.env.current_year),
Column::Timestamp(Created) => file.created_time().render( &self.colours, &self.env.tz, &self.env.date_and_time, &self.env.date_and_year, &self.env.time, self.env.current_year),
Column::Timestamp(Accessed) => file.accessed_time().render(&self.colours, &self.env.tz, &self.env.date_and_time, &self.env.date_and_year, &self.env.time, self.env.current_year),
Column::HardLinks => file.links().render(&self.colours, &self.env.numeric),
Column::Inode => file.inode().render(&self.colours),
Column::Blocks => file.blocks().render(&self.colours),
Column::User => file.user().render(&self.colours, &*self.env.lock_users()),
Column::Group => file.group().render(&self.colours, &*self.env.lock_users()),
Column::GitStatus => file.git_status().render(&self.colours),
Column::Permissions => self.permissions_plus(file, xattrs).render(&self.colours),
Column::FileSize(fmt) => file.size().render(&self.colours, fmt, &self.env.numeric),
Column::HardLinks => file.links().render(&self.colours, &self.env.numeric),
Column::Inode => file.inode().render(&self.colours),
Column::Blocks => file.blocks().render(&self.colours),
Column::User => file.user().render(&self.colours, &*self.env.lock_users()),
Column::Group => file.group().render(&self.colours, &*self.env.lock_users()),
Column::GitStatus => file.git_status().render(&self.colours),
Column::Timestamp(Modified) => file.modified_time().render(&self.colours, &self.env.tz, &self.env.time_format),
Column::Timestamp(Created) => file.created_time().render( &self.colours, &self.env.tz, &self.env.time_format),
Column::Timestamp(Accessed) => file.accessed_time().render(&self.colours, &self.env.tz, &self.env.time_format),
}
}

79
src/output/time.rs Normal file
View File

@ -0,0 +1,79 @@
use datetime::{LocalDateTime, TimeZone, DatePiece};
use datetime::fmt::DateFormat;
use locale;
use fs::fields::time_t;
#[derive(Debug, Clone)]
pub struct TimeFormat {
/// 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 *arent*.
pub date_and_year: DateFormat<'static>,
}
impl TimeFormat {
fn is_recent(&self, date: LocalDateTime) -> bool {
date.year() == self.current_year
}
#[allow(trivial_numeric_casts)]
pub fn format_local(&self, time: time_t) -> String {
let date = LocalDateTime::at(time as i64);
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)]
pub fn format_zoned(&self, time: time_t, zone: &TimeZone) -> String {
let date = zone.to_zoned(LocalDateTime::at(time as i64));
if self.is_recent(date) {
self.date_and_time.format(&date, &self.locale)
}
else {
self.date_and_year.format(&date, &self.locale)
}
}
pub fn deduce() -> TimeFormat {
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);
// others vary between three and four (1月 to 12月). We assume that
// December is the month with the maximum width, and use the width of
// that to determine how to pad the other months.
let december_width = UnicodeWidthStr::width(&*locale.short_month_name(11));
let date_and_time = match december_width {
4 => DateFormat::parse("{2>:D} {4>:M} {2>:h}:{02>:m}").unwrap(),
_ => DateFormat::parse("{2>:D} {:M} {2>:h}:{02>:m}").unwrap(),
};
let date_and_year = match december_width {
4 => DateFormat::parse("{2>:D} {4>:M} {5>:Y}").unwrap(),
_ => DateFormat::parse("{2>:D} {:M} {5>:Y}").unwrap()
};
TimeFormat { current_year, locale, date_and_time, date_and_year }
}
}