mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-12-27 10:22:40 +00:00
Handle timestamps before UNIX_EPOCH (#658)
Instead of returning a Duration since the epoch from file metadata, which cannot represent times before it, return the SystemTime directly. Move conversion closer to where it's needed, and perform it infallibly.
This commit is contained in:
parent
78ba0b8973
commit
bc830b9158
@ -4,7 +4,7 @@ use std::io::Error as IOError;
|
||||
use std::io::Result as IOResult;
|
||||
use std::os::unix::fs::{MetadataExt, PermissionsExt, FileTypeExt};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::{UNIX_EPOCH, Duration};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use log::{debug, error};
|
||||
|
||||
@ -326,35 +326,26 @@ impl<'dir> File<'dir> {
|
||||
}
|
||||
|
||||
/// This file’s last modified timestamp.
|
||||
/// If the file's time is invalid, assume it was modified today
|
||||
pub fn modified_time(&self) -> Duration {
|
||||
match self.metadata.modified() {
|
||||
Ok(system_time) => system_time.duration_since(UNIX_EPOCH).unwrap(),
|
||||
Err(_) => Duration::new(0, 0),
|
||||
}
|
||||
/// If the file's time is invalid, assume it was modified at the epoch
|
||||
pub fn modified_time(&self) -> SystemTime {
|
||||
self.metadata.modified().unwrap_or(UNIX_EPOCH)
|
||||
}
|
||||
|
||||
/// This file’s last changed timestamp.
|
||||
pub fn changed_time(&self) -> Duration {
|
||||
Duration::new(self.metadata.ctime() as u64, self.metadata.ctime_nsec() as u32)
|
||||
pub fn changed_time(&self) -> SystemTime {
|
||||
self.metadata.modified().unwrap_or(UNIX_EPOCH)
|
||||
}
|
||||
|
||||
/// This file’s last accessed timestamp.
|
||||
/// If the file's time is invalid, assume it was accessed today
|
||||
pub fn accessed_time(&self) -> Duration {
|
||||
match self.metadata.accessed() {
|
||||
Ok(system_time) => system_time.duration_since(UNIX_EPOCH).unwrap(),
|
||||
Err(_) => Duration::new(0, 0),
|
||||
}
|
||||
/// If the file's time is invalid, assume it was accessed at the epoch
|
||||
pub fn accessed_time(&self) -> SystemTime {
|
||||
self.metadata.accessed().unwrap_or(UNIX_EPOCH)
|
||||
}
|
||||
|
||||
/// This file’s created timestamp.
|
||||
/// If the file's time is invalid, assume it was created today
|
||||
pub fn created_time(&self) -> Duration {
|
||||
match self.metadata.created() {
|
||||
Ok(system_time) => system_time.duration_since(UNIX_EPOCH).unwrap(),
|
||||
Err(_) => Duration::new(0, 0),
|
||||
}
|
||||
/// If the file's time is invalid, assume it was created at the epoch
|
||||
pub fn created_time(&self) -> SystemTime {
|
||||
self.metadata.created().unwrap_or(UNIX_EPOCH)
|
||||
}
|
||||
|
||||
/// This file’s ‘type’.
|
||||
|
@ -11,7 +11,7 @@ pub trait Render {
|
||||
format: &TimeFormat) -> TextCell;
|
||||
}
|
||||
|
||||
impl Render for std::time::Duration {
|
||||
impl Render for std::time::SystemTime {
|
||||
fn render(self, style: Style,
|
||||
tz: &Option<TimeZone>,
|
||||
format: &TimeFormat) -> TextCell {
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Timestamp formatting.
|
||||
|
||||
use std::time::Duration;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use datetime::{LocalDateTime, TimeZone, DatePiece, TimePiece};
|
||||
use datetime::fmt::DateFormat;
|
||||
@ -51,7 +51,7 @@ pub enum TimeFormat {
|
||||
// timestamps are separate types.
|
||||
|
||||
impl TimeFormat {
|
||||
pub fn format_local(&self, time: Duration) -> String {
|
||||
pub fn format_local(&self, time: SystemTime) -> String {
|
||||
match *self {
|
||||
TimeFormat::DefaultFormat(ref fmt) => fmt.format_local(time),
|
||||
TimeFormat::ISOFormat(ref iso) => iso.format_local(time),
|
||||
@ -60,7 +60,7 @@ impl TimeFormat {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_zoned(&self, time: Duration, zone: &TimeZone) -> String {
|
||||
pub fn format_zoned(&self, time: SystemTime, zone: &TimeZone) -> String {
|
||||
match *self {
|
||||
TimeFormat::DefaultFormat(ref fmt) => fmt.format_zoned(time, zone),
|
||||
TimeFormat::ISOFormat(ref iso) => iso.format_zoned(time, zone),
|
||||
@ -146,11 +146,11 @@ impl DefaultFormat {
|
||||
}
|
||||
|
||||
#[allow(trivial_numeric_casts)]
|
||||
fn format_local(&self, time: Duration) -> String {
|
||||
if time.as_nanos() == 0 {
|
||||
fn format_local(&self, time: SystemTime) -> String {
|
||||
if time == UNIX_EPOCH {
|
||||
return "-".to_string();
|
||||
}
|
||||
let date = LocalDateTime::at(time.as_secs() as i64);
|
||||
let date = LocalDateTime::at(systemtime_epoch(time));
|
||||
|
||||
if self.is_recent(date) {
|
||||
format!("{:2} {} {:02}:{:02}",
|
||||
@ -163,12 +163,12 @@ impl DefaultFormat {
|
||||
}
|
||||
|
||||
#[allow(trivial_numeric_casts)]
|
||||
fn format_zoned(&self, time: Duration, zone: &TimeZone) -> String {
|
||||
if time.as_nanos() == 0 {
|
||||
fn format_zoned(&self, time: SystemTime, zone: &TimeZone) -> String {
|
||||
if time == UNIX_EPOCH {
|
||||
return "-".to_string();
|
||||
}
|
||||
|
||||
let date = zone.to_zoned(LocalDateTime::at(time.as_secs() as i64));
|
||||
let date = zone.to_zoned(LocalDateTime::at(systemtime_epoch(time)));
|
||||
|
||||
if self.is_recent(date) {
|
||||
format!("{:2} {} {:02}:{:02}",
|
||||
@ -181,19 +181,31 @@ impl DefaultFormat {
|
||||
}
|
||||
}
|
||||
|
||||
fn systemtime_epoch(time: SystemTime) -> i64 {
|
||||
time
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|t| t.as_secs() as i64)
|
||||
.unwrap_or_else(|e| -(e.duration().as_secs() as i64))
|
||||
}
|
||||
|
||||
fn systemtime_nanos(time: SystemTime) -> u32 {
|
||||
time
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|t| t.subsec_nanos())
|
||||
.unwrap_or_else(|e| e.duration().subsec_nanos())
|
||||
}
|
||||
|
||||
#[allow(trivial_numeric_casts)]
|
||||
fn long_local(time: Duration) -> String {
|
||||
let date = LocalDateTime::at(time.as_secs() as i64);
|
||||
fn long_local(time: SystemTime) -> String {
|
||||
let date = LocalDateTime::at(systemtime_epoch(time));
|
||||
format!("{:04}-{:02}-{:02} {:02}:{:02}",
|
||||
date.year(), date.month() as usize, date.day(),
|
||||
date.hour(), date.minute())
|
||||
}
|
||||
|
||||
#[allow(trivial_numeric_casts)]
|
||||
fn long_zoned(time: Duration, zone: &TimeZone) -> String {
|
||||
let date = zone.to_zoned(LocalDateTime::at(time.as_secs() as i64));
|
||||
fn long_zoned(time: SystemTime, zone: &TimeZone) -> String {
|
||||
let date = zone.to_zoned(LocalDateTime::at(systemtime_epoch(time)));
|
||||
format!("{:04}-{:02}-{:02} {:02}:{:02}",
|
||||
date.year(), date.month() as usize, date.day(),
|
||||
date.hour(), date.minute())
|
||||
@ -201,23 +213,23 @@ fn long_zoned(time: Duration, zone: &TimeZone) -> String {
|
||||
|
||||
|
||||
#[allow(trivial_numeric_casts)]
|
||||
fn full_local(time: Duration) -> String {
|
||||
let date = LocalDateTime::at(time.as_secs() as i64);
|
||||
fn full_local(time: SystemTime) -> String {
|
||||
let date = LocalDateTime::at(systemtime_epoch(time));
|
||||
format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:09}",
|
||||
date.year(), date.month() as usize, date.day(),
|
||||
date.hour(), date.minute(), date.second(), time.subsec_nanos())
|
||||
date.hour(), date.minute(), date.second(), systemtime_nanos(time))
|
||||
}
|
||||
|
||||
#[allow(trivial_numeric_casts)]
|
||||
fn full_zoned(time: Duration, zone: &TimeZone) -> String {
|
||||
fn full_zoned(time: SystemTime, zone: &TimeZone) -> String {
|
||||
use datetime::Offset;
|
||||
|
||||
let local = LocalDateTime::at(time.as_secs() as i64);
|
||||
let local = LocalDateTime::at(systemtime_epoch(time));
|
||||
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.subsec_nanos(),
|
||||
date.hour(), date.minute(), date.second(), systemtime_nanos(time),
|
||||
offset.hours(), offset.minutes().abs())
|
||||
}
|
||||
|
||||
@ -244,8 +256,8 @@ impl ISOFormat {
|
||||
}
|
||||
|
||||
#[allow(trivial_numeric_casts)]
|
||||
fn format_local(&self, time: Duration) -> String {
|
||||
let date = LocalDateTime::at(time.as_secs() as i64);
|
||||
fn format_local(&self, time: SystemTime) -> String {
|
||||
let date = LocalDateTime::at(systemtime_epoch(time));
|
||||
|
||||
if self.is_recent(date) {
|
||||
format!("{:02}-{:02} {:02}:{:02}",
|
||||
@ -259,8 +271,8 @@ impl ISOFormat {
|
||||
}
|
||||
|
||||
#[allow(trivial_numeric_casts)]
|
||||
fn format_zoned(&self, time: Duration, zone: &TimeZone) -> String {
|
||||
let date = zone.to_zoned(LocalDateTime::at(time.as_secs() as i64));
|
||||
fn format_zoned(&self, time: SystemTime, zone: &TimeZone) -> String {
|
||||
let date = zone.to_zoned(LocalDateTime::at(systemtime_epoch(time)));
|
||||
|
||||
if self.is_recent(date) {
|
||||
format!("{:02}-{:02} {:02}:{:02}",
|
||||
|
Loading…
Reference in New Issue
Block a user