Merge pull request #670 from Freaky/fix-pre-epoch-times

Handle timestamps before UNIX_EPOCH (#658)
This commit is contained in:
Benjamin Sago 2020-10-08 13:55:24 +01:00 committed by GitHub
commit 4b459631aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 62 deletions

View File

@ -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::{Duration, SystemTime, UNIX_EPOCH};
use log::{debug, error};
@ -325,36 +325,36 @@ impl<'dir> File<'dir> {
}
}
/// This files 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),
}
/// This files last modified timestamp, if available on this platform.
pub fn modified_time(&self) -> Option<SystemTime> {
self.metadata.modified().ok()
}
/// This files last changed timestamp.
pub fn changed_time(&self) -> Duration {
Duration::new(self.metadata.ctime() as u64, self.metadata.ctime_nsec() as u32)
/// This files last changed timestamp, if available on this platform.
pub fn changed_time(&self) -> Option<SystemTime> {
let (mut sec, mut nsec) = (self.metadata.ctime(), self.metadata.ctime_nsec());
Some(
if sec < 0 {
if nsec > 0 {
sec += 1;
nsec = nsec - 1_000_000_000;
}
UNIX_EPOCH - Duration::new(sec.abs() as u64, nsec.abs() as u32)
} else {
UNIX_EPOCH + Duration::new(sec as u64, nsec as u32)
}
)
}
/// This files 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),
}
/// This files last accessed timestamp, if available on this platform.
pub fn accessed_time(&self) -> Option<SystemTime> {
self.metadata.accessed().ok()
}
/// This files 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),
}
/// This files created timestamp, if available on this platform.
pub fn created_time(&self) -> Option<SystemTime> {
self.metadata.created().ok()
}
/// This files type.

View File

@ -11,18 +11,21 @@ pub trait Render {
format: &TimeFormat) -> TextCell;
}
impl Render for std::time::Duration {
impl Render for Option<std::time::SystemTime> {
fn render(self, style: Style,
tz: &Option<TimeZone>,
format: &TimeFormat) -> TextCell {
if let Some(ref tz) = *tz {
let datestamp = format.format_zoned(self, tz);
TextCell::paint(style, datestamp)
}
else {
let datestamp = format.format_local(self);
TextCell::paint(style, datestamp)
}
let datestamp = if let Some(time) = self {
if let Some(ref tz) = tz {
format.format_zoned(time, tz)
} else {
format.format_local(time)
}
} else {
String::from("-")
};
TextCell::paint(style, datestamp)
}
}

View File

@ -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,8 @@ impl DefaultFormat {
}
#[allow(trivial_numeric_casts)]
fn format_local(&self, time: Duration) -> String {
if time.as_nanos() == 0 {
return "-".to_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!("{:2} {} {:02}:{:02}",
@ -163,12 +160,8 @@ impl DefaultFormat {
}
#[allow(trivial_numeric_casts)]
fn format_zoned(&self, time: Duration, zone: &TimeZone) -> String {
if time.as_nanos() == 0 {
return "-".to_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!("{:2} {} {:02}:{:02}",
@ -181,19 +174,45 @@ impl DefaultFormat {
}
}
fn systemtime_epoch(time: SystemTime) -> i64 {
time
.duration_since(UNIX_EPOCH)
.map(|t| t.as_secs() as i64)
.unwrap_or_else(|e| {
let diff = e.duration();
let mut secs = diff.as_secs();
if diff.subsec_nanos() > 0 {
secs += 1;
}
-(secs as i64)
})
}
fn systemtime_nanos(time: SystemTime) -> u32 {
time
.duration_since(UNIX_EPOCH)
.map(|t| t.subsec_nanos())
.unwrap_or_else(|e| {
let nanos = e.duration().subsec_nanos();
if nanos > 0 {
1_000_000_000 - nanos
} else {
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 +220,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 +263,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 +278,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}",