mirror of
https://github.com/Llewellynvdm/exa.git
synced 2025-01-15 17:35:52 +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::io::Result as IOResult;
|
||||||
use std::os::unix::fs::{MetadataExt, PermissionsExt, FileTypeExt};
|
use std::os::unix::fs::{MetadataExt, PermissionsExt, FileTypeExt};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::{UNIX_EPOCH, Duration};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
|
|
||||||
@ -326,35 +326,26 @@ impl<'dir> File<'dir> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This file’s last modified timestamp.
|
/// This file’s last modified timestamp.
|
||||||
/// If the file's time is invalid, assume it was modified today
|
/// If the file's time is invalid, assume it was modified at the epoch
|
||||||
pub fn modified_time(&self) -> Duration {
|
pub fn modified_time(&self) -> SystemTime {
|
||||||
match self.metadata.modified() {
|
self.metadata.modified().unwrap_or(UNIX_EPOCH)
|
||||||
Ok(system_time) => system_time.duration_since(UNIX_EPOCH).unwrap(),
|
|
||||||
Err(_) => Duration::new(0, 0),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This file’s last changed timestamp.
|
/// This file’s last changed timestamp.
|
||||||
pub fn changed_time(&self) -> Duration {
|
pub fn changed_time(&self) -> SystemTime {
|
||||||
Duration::new(self.metadata.ctime() as u64, self.metadata.ctime_nsec() as u32)
|
self.metadata.modified().unwrap_or(UNIX_EPOCH)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This file’s last accessed timestamp.
|
/// This file’s last accessed timestamp.
|
||||||
/// If the file's time is invalid, assume it was accessed today
|
/// If the file's time is invalid, assume it was accessed at the epoch
|
||||||
pub fn accessed_time(&self) -> Duration {
|
pub fn accessed_time(&self) -> SystemTime {
|
||||||
match self.metadata.accessed() {
|
self.metadata.accessed().unwrap_or(UNIX_EPOCH)
|
||||||
Ok(system_time) => system_time.duration_since(UNIX_EPOCH).unwrap(),
|
|
||||||
Err(_) => Duration::new(0, 0),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This file’s created timestamp.
|
/// This file’s created timestamp.
|
||||||
/// If the file's time is invalid, assume it was created today
|
/// If the file's time is invalid, assume it was created at the epoch
|
||||||
pub fn created_time(&self) -> Duration {
|
pub fn created_time(&self) -> SystemTime {
|
||||||
match self.metadata.created() {
|
self.metadata.created().unwrap_or(UNIX_EPOCH)
|
||||||
Ok(system_time) => system_time.duration_since(UNIX_EPOCH).unwrap(),
|
|
||||||
Err(_) => Duration::new(0, 0),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This file’s ‘type’.
|
/// This file’s ‘type’.
|
||||||
|
@ -11,7 +11,7 @@ pub trait Render {
|
|||||||
format: &TimeFormat) -> TextCell;
|
format: &TimeFormat) -> TextCell;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for std::time::Duration {
|
impl Render for std::time::SystemTime {
|
||||||
fn render(self, style: Style,
|
fn render(self, style: Style,
|
||||||
tz: &Option<TimeZone>,
|
tz: &Option<TimeZone>,
|
||||||
format: &TimeFormat) -> TextCell {
|
format: &TimeFormat) -> TextCell {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Timestamp formatting.
|
//! Timestamp formatting.
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use datetime::{LocalDateTime, TimeZone, DatePiece, TimePiece};
|
use datetime::{LocalDateTime, TimeZone, DatePiece, TimePiece};
|
||||||
use datetime::fmt::DateFormat;
|
use datetime::fmt::DateFormat;
|
||||||
@ -51,7 +51,7 @@ pub enum TimeFormat {
|
|||||||
// timestamps are separate types.
|
// timestamps are separate types.
|
||||||
|
|
||||||
impl TimeFormat {
|
impl TimeFormat {
|
||||||
pub fn format_local(&self, time: Duration) -> String {
|
pub fn format_local(&self, time: SystemTime) -> String {
|
||||||
match *self {
|
match *self {
|
||||||
TimeFormat::DefaultFormat(ref fmt) => fmt.format_local(time),
|
TimeFormat::DefaultFormat(ref fmt) => fmt.format_local(time),
|
||||||
TimeFormat::ISOFormat(ref iso) => iso.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 {
|
match *self {
|
||||||
TimeFormat::DefaultFormat(ref fmt) => fmt.format_zoned(time, zone),
|
TimeFormat::DefaultFormat(ref fmt) => fmt.format_zoned(time, zone),
|
||||||
TimeFormat::ISOFormat(ref iso) => iso.format_zoned(time, zone),
|
TimeFormat::ISOFormat(ref iso) => iso.format_zoned(time, zone),
|
||||||
@ -146,11 +146,11 @@ impl DefaultFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn format_local(&self, time: Duration) -> String {
|
fn format_local(&self, time: SystemTime) -> String {
|
||||||
if time.as_nanos() == 0 {
|
if time == UNIX_EPOCH {
|
||||||
return "-".to_string();
|
return "-".to_string();
|
||||||
}
|
}
|
||||||
let date = LocalDateTime::at(time.as_secs() as i64);
|
let date = LocalDateTime::at(systemtime_epoch(time));
|
||||||
|
|
||||||
if self.is_recent(date) {
|
if self.is_recent(date) {
|
||||||
format!("{:2} {} {:02}:{:02}",
|
format!("{:2} {} {:02}:{:02}",
|
||||||
@ -163,12 +163,12 @@ impl DefaultFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn format_zoned(&self, time: Duration, zone: &TimeZone) -> String {
|
fn format_zoned(&self, time: SystemTime, zone: &TimeZone) -> String {
|
||||||
if time.as_nanos() == 0 {
|
if time == UNIX_EPOCH {
|
||||||
return "-".to_string();
|
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) {
|
if self.is_recent(date) {
|
||||||
format!("{:2} {} {:02}:{:02}",
|
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)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn long_local(time: Duration) -> String {
|
fn long_local(time: SystemTime) -> String {
|
||||||
let date = LocalDateTime::at(time.as_secs() as i64);
|
let date = LocalDateTime::at(systemtime_epoch(time));
|
||||||
format!("{:04}-{:02}-{:02} {:02}:{:02}",
|
format!("{:04}-{:02}-{:02} {:02}:{:02}",
|
||||||
date.year(), date.month() as usize, date.day(),
|
date.year(), date.month() as usize, date.day(),
|
||||||
date.hour(), date.minute())
|
date.hour(), date.minute())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn long_zoned(time: Duration, zone: &TimeZone) -> String {
|
fn long_zoned(time: SystemTime, zone: &TimeZone) -> String {
|
||||||
let date = zone.to_zoned(LocalDateTime::at(time.as_secs() as i64));
|
let date = zone.to_zoned(LocalDateTime::at(systemtime_epoch(time)));
|
||||||
format!("{:04}-{:02}-{:02} {:02}:{:02}",
|
format!("{:04}-{:02}-{:02} {:02}:{:02}",
|
||||||
date.year(), date.month() as usize, date.day(),
|
date.year(), date.month() as usize, date.day(),
|
||||||
date.hour(), date.minute())
|
date.hour(), date.minute())
|
||||||
@ -201,23 +213,23 @@ fn long_zoned(time: Duration, zone: &TimeZone) -> String {
|
|||||||
|
|
||||||
|
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn full_local(time: Duration) -> String {
|
fn full_local(time: SystemTime) -> String {
|
||||||
let date = LocalDateTime::at(time.as_secs() as i64);
|
let date = LocalDateTime::at(systemtime_epoch(time));
|
||||||
format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:09}",
|
format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:09}",
|
||||||
date.year(), date.month() as usize, date.day(),
|
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)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn full_zoned(time: Duration, zone: &TimeZone) -> String {
|
fn full_zoned(time: SystemTime, zone: &TimeZone) -> String {
|
||||||
use datetime::Offset;
|
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 date = zone.to_zoned(local);
|
||||||
let offset = Offset::of_seconds(zone.offset(local) as i32).expect("Offset out of range");
|
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}",
|
format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:09} {:+03}{:02}",
|
||||||
date.year(), date.month() as usize, date.day(),
|
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())
|
offset.hours(), offset.minutes().abs())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,8 +256,8 @@ impl ISOFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn format_local(&self, time: Duration) -> String {
|
fn format_local(&self, time: SystemTime) -> String {
|
||||||
let date = LocalDateTime::at(time.as_secs() as i64);
|
let date = LocalDateTime::at(systemtime_epoch(time));
|
||||||
|
|
||||||
if self.is_recent(date) {
|
if self.is_recent(date) {
|
||||||
format!("{:02}-{:02} {:02}:{:02}",
|
format!("{:02}-{:02} {:02}:{:02}",
|
||||||
@ -259,8 +271,8 @@ impl ISOFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn format_zoned(&self, time: Duration, zone: &TimeZone) -> String {
|
fn format_zoned(&self, time: SystemTime, zone: &TimeZone) -> 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) {
|
if self.is_recent(date) {
|
||||||
format!("{:02}-{:02} {:02}:{:02}",
|
format!("{:02}-{:02} {:02}:{:02}",
|
||||||
|
Loading…
Reference in New Issue
Block a user