mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-09-27 20:59:02 +00:00
Merge pull request #457 from ariasuni/fix-sorting-by-created-time
Fix sorting by created time
This commit is contained in:
commit
faed8f9b82
@ -62,8 +62,8 @@ These options are available when running with --long (`-l`):
|
|||||||
- **--time-style**: how to format timestamps
|
- **--time-style**: how to format timestamps
|
||||||
|
|
||||||
- Valid **--color** options are **always**, **automatic**, and **never**.
|
- Valid **--color** options are **always**, **automatic**, and **never**.
|
||||||
- Valid sort fields are **accessed**, **created**, **extension**, **Extension**, **inode**, **modified**, **name**, **Name**, **size**, **type**, and **none**. Fields starting with a capital letter sort uppercase before lowercase. The modified field has the aliases **date**, **time**, and **newest**, while its reverse has the aliases **age** and **oldest**.
|
- Valid sort fields are **accessed**, **changed**, **created**, **extension**, **Extension**, **inode**, **modified**, **name**, **Name**, **size**, **type**, and **none**. Fields starting with a capital letter sort uppercase before lowercase. The modified field has the aliases **date**, **time**, and **newest**, while its reverse has the aliases **age** and **oldest**.
|
||||||
- Valid time fields are **modified**, **accessed**, and **created**.
|
- Valid time fields are **modified**, **changed**, **accessed**, and **created**.
|
||||||
- Valid time styles are **default**, **iso**, **long-iso**, and **full-iso**.
|
- Valid time styles are **default**, **iso**, **long-iso**, and **full-iso**.
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,12 +14,12 @@ _exa()
|
|||||||
;;
|
;;
|
||||||
|
|
||||||
-s|--sort)
|
-s|--sort)
|
||||||
COMPREPLY=( $( compgen -W 'name filename Name Filename size filesize extension Extension date time modified accessed created type inode oldest newest age none --' -- "$cur" ) )
|
COMPREPLY=( $( compgen -W 'name filename Name Filename size filesize extension Extension date time modified changed accessed created type inode oldest newest age none --' -- "$cur" ) )
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-t|--time)
|
-t|--time)
|
||||||
COMPREPLY=( $( compgen -W 'accessed modified created --' -- $cur ) )
|
COMPREPLY=( $( compgen -W 'modified changed accessed created --' -- $cur ) )
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ complete -c exa -s 'r' -l 'reverse' -d "Reverse the sort order"
|
|||||||
complete -c exa -s 's' -l 'sort' -x -d "Which field to sort by" -a "
|
complete -c exa -s 's' -l 'sort' -x -d "Which field to sort by" -a "
|
||||||
accessed\t'Sort by file accessed time'
|
accessed\t'Sort by file accessed time'
|
||||||
age\t'Sort by file modified time (newest first)'
|
age\t'Sort by file modified time (newest first)'
|
||||||
|
changed\t'Sort by changed time'
|
||||||
created\t'Sort by file modified time'
|
created\t'Sort by file modified time'
|
||||||
date\t'Sort by file modified time'
|
date\t'Sort by file modified time'
|
||||||
ext\t'Sort by file extension'
|
ext\t'Sort by file extension'
|
||||||
@ -54,13 +55,15 @@ complete -c exa -s 'g' -l 'group' -d "List each file's group"
|
|||||||
complete -c exa -s 'h' -l 'header' -d "Add a header row to each column"
|
complete -c exa -s 'h' -l 'header' -d "Add a header row to each column"
|
||||||
complete -c exa -s 'h' -l 'links' -d "List each file's number of hard links"
|
complete -c exa -s 'h' -l 'links' -d "List each file's number of hard links"
|
||||||
complete -c exa -s 'g' -l 'group' -d "List each file's inode number"
|
complete -c exa -s 'g' -l 'group' -d "List each file's inode number"
|
||||||
complete -c exa -s 'm' -l 'modified' -d "Use the modified timestamp field"
|
|
||||||
complete -c exa -s 'S' -l 'blocks' -d "List each file's number of filesystem blocks"
|
complete -c exa -s 'S' -l 'blocks' -d "List each file's number of filesystem blocks"
|
||||||
complete -c exa -s 't' -l 'time' -x -d "Which timestamp field to list" -a "
|
complete -c exa -s 't' -l 'time' -x -d "Which timestamp field to list" -a "
|
||||||
|
modified\t'Display modified time'
|
||||||
|
changed\t'Display changed time'
|
||||||
accessed\t'Display accessed time'
|
accessed\t'Display accessed time'
|
||||||
created\t'Display created time'
|
created\t'Display created time'
|
||||||
modified\t'Display modified time'
|
|
||||||
"
|
"
|
||||||
|
complete -c exa -s 'm' -l 'modified' -d "Use the modified timestamp field"
|
||||||
|
complete -c exa -l 'changed' -d "Use the changed timestamp field"
|
||||||
complete -c exa -s 'u' -l 'accessed' -d "Use the accessed timestamp field"
|
complete -c exa -s 'u' -l 'accessed' -d "Use the accessed timestamp field"
|
||||||
complete -c exa -s 'U' -l 'created' -d "Use the created timestamp field"
|
complete -c exa -s 'U' -l 'created' -d "Use the created timestamp field"
|
||||||
complete -c exa -l 'time-style' -x -d "How to format timestamps" -a "
|
complete -c exa -l 'time-style' -x -d "How to format timestamps" -a "
|
||||||
|
@ -27,7 +27,7 @@ __exa() {
|
|||||||
{-d,--list-dirs}"[List directories like regular files]" \
|
{-d,--list-dirs}"[List directories like regular files]" \
|
||||||
{-L,--level}"+[Limit the depth of recursion]" \
|
{-L,--level}"+[Limit the depth of recursion]" \
|
||||||
{-r,--reverse}"[Reverse the sort order]" \
|
{-r,--reverse}"[Reverse the sort order]" \
|
||||||
{-s,--sort}="[Which field to sort by]:(sort field):(accessed age created date extension Extension filename Filename inode modified oldest name Name newest none size time type)" \
|
{-s,--sort}="[Which field to sort by]:(sort field):(accessed age changed created date extension Extension filename Filename inode modified oldest name Name newest none size time type)" \
|
||||||
{-I,--ignore-glob}"[Ignore files that match these glob patterns]" \
|
{-I,--ignore-glob}"[Ignore files that match these glob patterns]" \
|
||||||
{-b,--binary}"[List file sizes with binary prefixes]" \
|
{-b,--binary}"[List file sizes with binary prefixes]" \
|
||||||
{-B,--bytes}"[List file sizes in bytes, without any prefixes]" \
|
{-B,--bytes}"[List file sizes in bytes, without any prefixes]" \
|
||||||
@ -37,7 +37,7 @@ __exa() {
|
|||||||
{-i,--inode}"[List each file's inode number]" \
|
{-i,--inode}"[List each file's inode number]" \
|
||||||
{-m,--modified}"[Use the modified timestamp field]" \
|
{-m,--modified}"[Use the modified timestamp field]" \
|
||||||
{-S,--blocks}"[List each file's number of filesystem blocks]" \
|
{-S,--blocks}"[List each file's number of filesystem blocks]" \
|
||||||
{-t,--time}="[Which time field to show]:(time field):(accessed created modified)" \
|
{-t,--time}="[Which time field to show]:(time field):(accessed changed created modified)" \
|
||||||
--time-style="[How to format timestamps]:(time style):(default iso long-iso full-iso)" \
|
--time-style="[How to format timestamps]:(time style):(default iso long-iso full-iso)" \
|
||||||
{-u,--accessed}"[Use the accessed timestamp field]" \
|
{-u,--accessed}"[Use the accessed timestamp field]" \
|
||||||
{-U,--created}"[Use the created timestamp field]" \
|
{-U,--created}"[Use the created timestamp field]" \
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.hy
|
.hy
|
||||||
.TH "exa" "1" "2017\-07\-07" "exa 0.7.0" ""
|
.TH "exa" "1" "2018\-12\-17" "exa 0.9.0" ""
|
||||||
.SH NAME
|
.SH NAME
|
||||||
.PP
|
.PP
|
||||||
exa \- a modern replacement for ls
|
exa \- a modern replacement for ls
|
||||||
@ -86,7 +86,7 @@ reverse the sort order
|
|||||||
.TP
|
.TP
|
||||||
.B \-s, \-\-sort=\f[I]SORT_FIELD\f[]
|
.B \-s, \-\-sort=\f[I]SORT_FIELD\f[]
|
||||||
which field to sort by.
|
which field to sort by.
|
||||||
Valid fields are name, Name, extension, Extension, size, modified, accessed, created, inode, type, and none.
|
Valid fields are name, Name, extension, Extension, size, modified, changed, accessed, created, inode, type, and none.
|
||||||
The modified field has the aliases date, time, and newest, and its reverse order has the aliases age and oldest.
|
The modified field has the aliases date, time, and newest, and its reverse order has the aliases age and oldest.
|
||||||
Fields starting with a capital letter will sort uppercase before lowercase: 'A' then 'B' then 'a' then 'b'.
|
Fields starting with a capital letter will sort uppercase before lowercase: 'A' then 'B' then 'a' then 'b'.
|
||||||
Fields starting with a lowercase letter will mix them: 'A' then 'a' then 'B' then 'b'.
|
Fields starting with a lowercase letter will mix them: 'A' then 'a' then 'B' then 'b'.
|
||||||
@ -158,7 +158,7 @@ list each file\[aq]s number of file system blocks
|
|||||||
.RE
|
.RE
|
||||||
.TP
|
.TP
|
||||||
.B \-t, \-\-time=\f[I]WORD\f[]
|
.B \-t, \-\-time=\f[I]WORD\f[]
|
||||||
which timestamp field to list (modified, accessed, created)
|
which timestamp field to list (modified, changed, accessed, created)
|
||||||
.RS
|
.RS
|
||||||
.RE
|
.RE
|
||||||
.TP
|
.TP
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
//! Files, and methods and fields to access their metadata.
|
//! Files, and methods and fields to access their metadata.
|
||||||
|
|
||||||
use std::fs;
|
use std::fs::{self, metadata};
|
||||||
use std::io::Error as IOError;
|
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 fs::dir::Dir;
|
use fs::dir::Dir;
|
||||||
use fs::fields as f;
|
use fs::fields as f;
|
||||||
|
use options::Misfire;
|
||||||
|
|
||||||
|
|
||||||
/// A **File** is a wrapper around one of Rust's Path objects, along with
|
/// A **File** is a wrapper around one of Rust's Path objects, along with
|
||||||
@ -325,27 +327,23 @@ impl<'dir> File<'dir> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This file’s last modified timestamp.
|
/// This file’s last modified timestamp.
|
||||||
pub fn modified_time(&self) -> f::Time {
|
pub fn modified_time(&self) -> Duration {
|
||||||
f::Time {
|
self.metadata.modified().unwrap().duration_since(UNIX_EPOCH).unwrap()
|
||||||
seconds: self.metadata.mtime(),
|
|
||||||
nanoseconds: self.metadata.mtime_nsec()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This file’s created timestamp.
|
/// This file’s last changed timestamp.
|
||||||
pub fn created_time(&self) -> f::Time {
|
pub fn changed_time(&self) -> Duration {
|
||||||
f::Time {
|
Duration::new(self.metadata.ctime() as u64, self.metadata.ctime_nsec() as u32)
|
||||||
seconds: self.metadata.ctime(),
|
|
||||||
nanoseconds: self.metadata.ctime_nsec()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This file’s last accessed timestamp.
|
/// This file’s last accessed timestamp.
|
||||||
pub fn accessed_time(&self) -> f::Time {
|
pub fn accessed_time(&self) -> Duration {
|
||||||
f::Time {
|
self.metadata.accessed().unwrap().duration_since(UNIX_EPOCH).unwrap()
|
||||||
seconds: self.metadata.atime(),
|
}
|
||||||
nanoseconds: self.metadata.atime_nsec()
|
|
||||||
}
|
/// This file’s created timestamp.
|
||||||
|
pub fn created_time(&self) -> Duration {
|
||||||
|
self.metadata.created().unwrap().duration_since(UNIX_EPOCH).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This file’s ‘type’.
|
/// This file’s ‘type’.
|
||||||
@ -462,6 +460,41 @@ impl<'dir> FileTarget<'dir> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub enum PlatformMetadata {
|
||||||
|
ModifiedTime,
|
||||||
|
ChangedTime,
|
||||||
|
AccessedTime,
|
||||||
|
CreatedTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformMetadata {
|
||||||
|
pub fn check_supported(&self) -> Result<(), Misfire> {
|
||||||
|
use std::env::temp_dir;
|
||||||
|
let result = match self {
|
||||||
|
// Call the functions that return a Result to see if it works
|
||||||
|
PlatformMetadata::AccessedTime => metadata(temp_dir()).unwrap().accessed(),
|
||||||
|
PlatformMetadata::ModifiedTime => metadata(temp_dir()).unwrap().modified(),
|
||||||
|
PlatformMetadata::CreatedTime => metadata(temp_dir()).unwrap().created(),
|
||||||
|
// We use the Unix API so we know it’s not available elsewhere
|
||||||
|
PlatformMetadata::ChangedTime => {
|
||||||
|
if cfg!(target_family = "unix") {
|
||||||
|
return Ok(())
|
||||||
|
} else {
|
||||||
|
return Err(Misfire::Unsupported(
|
||||||
|
// for consistency, this error message similar to the one Rust
|
||||||
|
// use when created time is not available
|
||||||
|
"status modified time is not available on this platform currently".to_string()));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
match result {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(err) => Err(Misfire::Unsupported(err.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// More readable aliases for the permission bits exposed by libc.
|
/// More readable aliases for the permission bits exposed by libc.
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
mod modes {
|
mod modes {
|
||||||
|
@ -173,13 +173,16 @@ pub enum SortField {
|
|||||||
/// http://unix.stackexchange.com/a/8842
|
/// http://unix.stackexchange.com/a/8842
|
||||||
AccessedDate,
|
AccessedDate,
|
||||||
|
|
||||||
/// The time the file was changed or created (the “ctime”).
|
/// The time the file was changed (the “ctime”).
|
||||||
///
|
///
|
||||||
/// Contrary to the name, this field is used to mark the time when a
|
/// This field is used to mark the time when a file’s metadata
|
||||||
/// file’s metadata changed -- its permissions, owners, or link count.
|
/// changed -- its permissions, owners, or link count.
|
||||||
///
|
///
|
||||||
/// In original Unix, this was, however, meant as creation time.
|
/// In original Unix, this was, however, meant as creation time.
|
||||||
/// https://www.bell-labs.com/usr/dmr/www/cacm.html
|
/// https://www.bell-labs.com/usr/dmr/www/cacm.html
|
||||||
|
ChangedDate,
|
||||||
|
|
||||||
|
/// The time the file was created (the "btime" or "birthtime").
|
||||||
CreatedDate,
|
CreatedDate,
|
||||||
|
|
||||||
/// The type of the file: directories, links, pipes, regular, files, etc.
|
/// The type of the file: directories, links, pipes, regular, files, etc.
|
||||||
@ -247,6 +250,7 @@ impl SortField {
|
|||||||
SortField::FileInode => a.metadata.ino().cmp(&b.metadata.ino()),
|
SortField::FileInode => a.metadata.ino().cmp(&b.metadata.ino()),
|
||||||
SortField::ModifiedDate => a.modified_time().cmp(&b.modified_time()),
|
SortField::ModifiedDate => a.modified_time().cmp(&b.modified_time()),
|
||||||
SortField::AccessedDate => a.accessed_time().cmp(&b.accessed_time()),
|
SortField::AccessedDate => a.accessed_time().cmp(&b.accessed_time()),
|
||||||
|
SortField::ChangedDate => a.changed_time().cmp(&b.changed_time()),
|
||||||
SortField::CreatedDate => a.created_time().cmp(&b.created_time()),
|
SortField::CreatedDate => a.created_time().cmp(&b.created_time()),
|
||||||
SortField::ModifiedAge => b.modified_time().cmp(&a.modified_time()), // flip b and a
|
SortField::ModifiedAge => b.modified_time().cmp(&a.modified_time()), // flip b and a
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ mod dir;
|
|||||||
pub use self::dir::{Dir, DotFilter};
|
pub use self::dir::{Dir, DotFilter};
|
||||||
|
|
||||||
mod file;
|
mod file;
|
||||||
pub use self::file::{File, FileTarget};
|
pub use self::file::{File, FileTarget, PlatformMetadata};
|
||||||
|
|
||||||
pub mod feature;
|
pub mod feature;
|
||||||
pub mod fields;
|
pub mod fields;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Parsing the options for `FileFilter`.
|
//! Parsing the options for `FileFilter`.
|
||||||
|
|
||||||
use fs::DotFilter;
|
use fs::{DotFilter, PlatformMetadata};
|
||||||
use fs::filter::{FileFilter, SortField, SortCase, IgnorePatterns, GitIgnore};
|
use fs::filter::{FileFilter, SortField, SortCase, IgnorePatterns, GitIgnore};
|
||||||
|
|
||||||
use options::{flags, Misfire};
|
use options::{flags, Misfire};
|
||||||
@ -35,62 +35,59 @@ impl SortField {
|
|||||||
None => return Ok(SortField::default()),
|
None => return Ok(SortField::default()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// The field is an OsStr, so can’t be matched.
|
// Get String because we can’t match an OsStr
|
||||||
if word == "name" || word == "filename" {
|
let word = match word.to_str() {
|
||||||
Ok(SortField::Name(SortCase::AaBbCc))
|
Some(ref w) => *w,
|
||||||
}
|
None => return Err(Misfire::BadArgument(&flags::SORT, word.into()))
|
||||||
else if word == "Name" || word == "Filename" {
|
};
|
||||||
Ok(SortField::Name(SortCase::ABCabc))
|
|
||||||
}
|
let field = match word {
|
||||||
else if word == ".name" || word == ".filename" {
|
"name" | "filename" => SortField::Name(SortCase::AaBbCc),
|
||||||
Ok(SortField::NameMixHidden(SortCase::AaBbCc))
|
"Name" | "Filename" => SortField::Name(SortCase::ABCabc),
|
||||||
}
|
".name" | ".filename" => SortField::NameMixHidden(SortCase::AaBbCc),
|
||||||
else if word == ".Name" || word == ".Filename" {
|
".Name" | ".Filename" => SortField::NameMixHidden(SortCase::ABCabc),
|
||||||
Ok(SortField::NameMixHidden(SortCase::ABCabc))
|
"size" | "filesize" => SortField::Size,
|
||||||
}
|
"ext" | "extension" => SortField::Extension(SortCase::AaBbCc),
|
||||||
else if word == "size" || word == "filesize" {
|
"Ext" | "Extension" => SortField::Extension(SortCase::ABCabc),
|
||||||
Ok(SortField::Size)
|
|
||||||
}
|
|
||||||
else if word == "ext" || word == "extension" {
|
|
||||||
Ok(SortField::Extension(SortCase::AaBbCc))
|
|
||||||
}
|
|
||||||
else if word == "Ext" || word == "Extension" {
|
|
||||||
Ok(SortField::Extension(SortCase::ABCabc))
|
|
||||||
}
|
|
||||||
else if word == "date" || word == "time" || word == "mod" || word == "modified" || word == "new" || word == "newest" {
|
|
||||||
// “new” sorts oldest at the top and newest at the bottom; “old”
|
// “new” sorts oldest at the top and newest at the bottom; “old”
|
||||||
// sorts newest at the top and oldest at the bottom. I think this
|
// sorts newest at the top and oldest at the bottom. I think this
|
||||||
// is the right way round to do this: “size” puts the smallest at
|
// is the right way round to do this: “size” puts the smallest at
|
||||||
// the top and the largest at the bottom, doesn’t it?
|
// the top and the largest at the bottom, doesn’t it?
|
||||||
Ok(SortField::ModifiedDate)
|
"date" | "time" | "mod" | "modified" | "new" | "newest" => SortField::ModifiedDate,
|
||||||
}
|
|
||||||
else if word == "age" || word == "old" || word == "oldest" {
|
|
||||||
// Similarly, “age” means that files with the least age (the
|
// Similarly, “age” means that files with the least age (the
|
||||||
// newest files) get sorted at the top, and files with the most
|
// newest files) get sorted at the top, and files with the most
|
||||||
// age (the oldest) at the bottom.
|
// age (the oldest) at the bottom.
|
||||||
Ok(SortField::ModifiedAge)
|
"age" | "old" | "oldest" => SortField::ModifiedAge,
|
||||||
|
"ch" | "changed" => SortField::ChangedDate,
|
||||||
|
"acc" | "accessed" => SortField::AccessedDate,
|
||||||
|
"cr" | "created" => SortField::CreatedDate,
|
||||||
|
"inode" => SortField::FileInode,
|
||||||
|
"type" => SortField::FileType,
|
||||||
|
"none" => SortField::Unsorted,
|
||||||
|
_ => return Err(Misfire::BadArgument(&flags::SORT, word.into()))
|
||||||
|
};
|
||||||
|
|
||||||
|
match SortField::to_platform_metadata(field) {
|
||||||
|
Some(m) => match m.check_supported() {
|
||||||
|
Ok(_) => Ok(field),
|
||||||
|
Err(misfire) => Err(misfire),
|
||||||
|
},
|
||||||
|
None => Ok(field),
|
||||||
}
|
}
|
||||||
else if word == "acc" || word == "accessed" {
|
}
|
||||||
Ok(SortField::AccessedDate)
|
|
||||||
}
|
fn to_platform_metadata(field: Self) -> Option<PlatformMetadata> {
|
||||||
else if word == "cr" || word == "created" {
|
match field {
|
||||||
Ok(SortField::CreatedDate)
|
SortField::ModifiedDate => Some(PlatformMetadata::ModifiedTime),
|
||||||
}
|
SortField::ChangedDate => Some(PlatformMetadata::ChangedTime),
|
||||||
else if word == "inode" {
|
SortField::AccessedDate => Some(PlatformMetadata::AccessedTime),
|
||||||
Ok(SortField::FileInode)
|
SortField::CreatedDate => Some(PlatformMetadata::CreatedTime),
|
||||||
}
|
_ => None
|
||||||
else if word == "type" {
|
|
||||||
Ok(SortField::FileType)
|
|
||||||
}
|
|
||||||
else if word == "none" {
|
|
||||||
Ok(SortField::Unsorted)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Err(Misfire::BadArgument(&flags::SORT, word.into()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// I’ve gone back and forth between whether to sort case-sensitively or
|
// I’ve gone back and forth between whether to sort case-sensitively or
|
||||||
// insensitively by default. The default string sort in most programming
|
// insensitively by default. The default string sort in most programming
|
||||||
// languages takes each character’s ASCII value into account, sorting
|
// languages takes each character’s ASCII value into account, sorting
|
||||||
@ -227,7 +224,7 @@ mod test {
|
|||||||
test!(empty: SortField <- []; Both => Ok(SortField::default()));
|
test!(empty: SortField <- []; Both => Ok(SortField::default()));
|
||||||
|
|
||||||
// Sort field arguments
|
// Sort field arguments
|
||||||
test!(one_arg: SortField <- ["--sort=cr"]; Both => Ok(SortField::CreatedDate));
|
test!(one_arg: SortField <- ["--sort=mod"]; Both => Ok(SortField::ModifiedDate));
|
||||||
test!(one_long: SortField <- ["--sort=size"]; Both => Ok(SortField::Size));
|
test!(one_long: SortField <- ["--sort=size"]; Both => Ok(SortField::Size));
|
||||||
test!(one_short: SortField <- ["-saccessed"]; Both => Ok(SortField::AccessedDate));
|
test!(one_short: SortField <- ["-saccessed"]; Both => Ok(SortField::AccessedDate));
|
||||||
test!(lowercase: SortField <- ["--sort", "name"]; Both => Ok(SortField::Name(SortCase::AaBbCc)));
|
test!(lowercase: SortField <- ["--sort", "name"]; Both => Ok(SortField::Name(SortCase::AaBbCc)));
|
||||||
|
@ -32,7 +32,7 @@ pub static GIT_IGNORE: Arg = Arg { short: None, long: "git-ignore", t
|
|||||||
pub static DIRS_FIRST: Arg = Arg { short: None, long: "group-directories-first", takes_value: TakesValue::Forbidden };
|
pub static DIRS_FIRST: Arg = Arg { short: None, long: "group-directories-first", takes_value: TakesValue::Forbidden };
|
||||||
pub static ONLY_DIRS: Arg = Arg { short: Some(b'D'), long: "only-dirs", takes_value: TakesValue::Forbidden };
|
pub static ONLY_DIRS: Arg = Arg { short: Some(b'D'), long: "only-dirs", takes_value: TakesValue::Forbidden };
|
||||||
const SORTS: Values = &[ "name", "Name", "size", "extension",
|
const SORTS: Values = &[ "name", "Name", "size", "extension",
|
||||||
"Extension", "modified", "accessed",
|
"Extension", "modified", "changed", "accessed",
|
||||||
"created", "inode", "type", "none" ];
|
"created", "inode", "type", "none" ];
|
||||||
|
|
||||||
// display options
|
// display options
|
||||||
@ -43,12 +43,13 @@ pub static HEADER: Arg = Arg { short: Some(b'h'), long: "header", takes_
|
|||||||
pub static INODE: Arg = Arg { short: Some(b'i'), long: "inode", takes_value: TakesValue::Forbidden };
|
pub static INODE: Arg = Arg { short: Some(b'i'), long: "inode", takes_value: TakesValue::Forbidden };
|
||||||
pub static LINKS: Arg = Arg { short: Some(b'H'), long: "links", takes_value: TakesValue::Forbidden };
|
pub static LINKS: Arg = Arg { short: Some(b'H'), long: "links", takes_value: TakesValue::Forbidden };
|
||||||
pub static MODIFIED: Arg = Arg { short: Some(b'm'), long: "modified", takes_value: TakesValue::Forbidden };
|
pub static MODIFIED: Arg = Arg { short: Some(b'm'), long: "modified", takes_value: TakesValue::Forbidden };
|
||||||
|
pub static CHANGED: Arg = Arg { short: None, long: "changed", takes_value: TakesValue::Forbidden };
|
||||||
pub static BLOCKS: Arg = Arg { short: Some(b'S'), long: "blocks", takes_value: TakesValue::Forbidden };
|
pub static BLOCKS: Arg = Arg { short: Some(b'S'), long: "blocks", takes_value: TakesValue::Forbidden };
|
||||||
pub static TIME: Arg = Arg { short: Some(b't'), long: "time", takes_value: TakesValue::Necessary(Some(TIMES)) };
|
pub static TIME: Arg = Arg { short: Some(b't'), long: "time", takes_value: TakesValue::Necessary(Some(TIMES)) };
|
||||||
pub static ACCESSED: Arg = Arg { short: Some(b'u'), long: "accessed", takes_value: TakesValue::Forbidden };
|
pub static ACCESSED: Arg = Arg { short: Some(b'u'), long: "accessed", takes_value: TakesValue::Forbidden };
|
||||||
pub static CREATED: Arg = Arg { short: Some(b'U'), long: "created", takes_value: TakesValue::Forbidden };
|
pub static CREATED: Arg = Arg { short: Some(b'U'), long: "created", takes_value: TakesValue::Forbidden };
|
||||||
pub static TIME_STYLE: Arg = Arg { short: None, long: "time-style", takes_value: TakesValue::Necessary(Some(TIME_STYLES)) };
|
pub static TIME_STYLE: Arg = Arg { short: None, long: "time-style", takes_value: TakesValue::Necessary(Some(TIME_STYLES)) };
|
||||||
const TIMES: Values = &["modified", "accessed", "created"];
|
const TIMES: Values = &["modified", "changed", "accessed", "created"];
|
||||||
const TIME_STYLES: Values = &["default", "long-iso", "full-iso", "iso"];
|
const TIME_STYLES: Values = &["default", "long-iso", "full-iso", "iso"];
|
||||||
|
|
||||||
// optional feature options
|
// optional feature options
|
||||||
@ -65,9 +66,8 @@ pub static ALL_ARGS: Args = Args(&[
|
|||||||
&ALL, &LIST_DIRS, &LEVEL, &REVERSE, &SORT, &DIRS_FIRST,
|
&ALL, &LIST_DIRS, &LEVEL, &REVERSE, &SORT, &DIRS_FIRST,
|
||||||
&IGNORE_GLOB, &GIT_IGNORE, &ONLY_DIRS,
|
&IGNORE_GLOB, &GIT_IGNORE, &ONLY_DIRS,
|
||||||
|
|
||||||
&BINARY, &BYTES, &GROUP, &HEADER, &INODE, &LINKS, &MODIFIED, &BLOCKS,
|
&BINARY, &BYTES, &GROUP, &HEADER, &INODE, &LINKS, &MODIFIED, &CHANGED,
|
||||||
&TIME, &ACCESSED, &CREATED, &TIME_STYLE,
|
&BLOCKS, &TIME, &ACCESSED, &CREATED, &TIME_STYLE,
|
||||||
|
|
||||||
&GIT, &EXTENDED,
|
&GIT, &EXTENDED,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -19,6 +19,9 @@ pub enum Misfire {
|
|||||||
/// The user supplied an illegal choice to an Argument.
|
/// The user supplied an illegal choice to an Argument.
|
||||||
BadArgument(&'static Arg, OsString),
|
BadArgument(&'static Arg, OsString),
|
||||||
|
|
||||||
|
/// The user supplied a set of options
|
||||||
|
Unsupported(String),
|
||||||
|
|
||||||
/// The user asked for help. This isn’t strictly an error, which is why
|
/// The user asked for help. This isn’t strictly an error, which is why
|
||||||
/// this enum isn’t named Error!
|
/// this enum isn’t named Error!
|
||||||
Help(HelpString),
|
Help(HelpString),
|
||||||
@ -83,6 +86,7 @@ impl fmt::Display for Misfire {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
InvalidOptions(ref e) => write!(f, "{}", e),
|
InvalidOptions(ref e) => write!(f, "{}", e),
|
||||||
|
Unsupported(ref e) => write!(f, "{}", e),
|
||||||
Help(ref text) => write!(f, "{}", text),
|
Help(ref text) => write!(f, "{}", text),
|
||||||
Version(ref version) => write!(f, "{}", version),
|
Version(ref version) => write!(f, "{}", version),
|
||||||
Conflict(ref a, ref b) => write!(f, "Option {} conflicts with option {}", a, b),
|
Conflict(ref a, ref b) => write!(f, "Option {} conflicts with option {}", a, b),
|
||||||
|
@ -6,6 +6,7 @@ use output::time::TimeFormat;
|
|||||||
use options::{flags, Misfire, Vars};
|
use options::{flags, Misfire, Vars};
|
||||||
use options::parser::MatchedFlags;
|
use options::parser::MatchedFlags;
|
||||||
|
|
||||||
|
use fs::PlatformMetadata;
|
||||||
use fs::feature::xattr;
|
use fs::feature::xattr;
|
||||||
|
|
||||||
|
|
||||||
@ -296,38 +297,58 @@ impl TimeTypes {
|
|||||||
fn deduce(matches: &MatchedFlags) -> Result<TimeTypes, Misfire> {
|
fn deduce(matches: &MatchedFlags) -> Result<TimeTypes, Misfire> {
|
||||||
let possible_word = matches.get(&flags::TIME)?;
|
let possible_word = matches.get(&flags::TIME)?;
|
||||||
let modified = matches.has(&flags::MODIFIED)?;
|
let modified = matches.has(&flags::MODIFIED)?;
|
||||||
let created = matches.has(&flags::CREATED)?;
|
let changed = matches.has(&flags::CHANGED)?;
|
||||||
let accessed = matches.has(&flags::ACCESSED)?;
|
let accessed = matches.has(&flags::ACCESSED)?;
|
||||||
|
let created = matches.has(&flags::CREATED)?;
|
||||||
|
|
||||||
if let Some(word) = possible_word {
|
let time_types = if let Some(word) = possible_word {
|
||||||
if modified {
|
if modified {
|
||||||
Err(Misfire::Useless(&flags::MODIFIED, true, &flags::TIME))
|
return Err(Misfire::Useless(&flags::MODIFIED, true, &flags::TIME));
|
||||||
}
|
}
|
||||||
else if created {
|
else if changed {
|
||||||
Err(Misfire::Useless(&flags::CREATED, true, &flags::TIME))
|
return Err(Misfire::Useless(&flags::CHANGED, true, &flags::TIME));
|
||||||
}
|
}
|
||||||
else if accessed {
|
else if accessed {
|
||||||
Err(Misfire::Useless(&flags::ACCESSED, true, &flags::TIME))
|
return Err(Misfire::Useless(&flags::ACCESSED, true, &flags::TIME));
|
||||||
|
}
|
||||||
|
else if created {
|
||||||
|
return Err(Misfire::Useless(&flags::CREATED, true, &flags::TIME));
|
||||||
}
|
}
|
||||||
else if word == "mod" || word == "modified" {
|
else if word == "mod" || word == "modified" {
|
||||||
Ok(TimeTypes { accessed: false, modified: true, created: false })
|
TimeTypes { modified: true, changed: false, accessed: false, created: false }
|
||||||
|
}
|
||||||
|
else if word == "ch" || word == "changed" {
|
||||||
|
TimeTypes { modified: false, changed: true, accessed: false, created: false }
|
||||||
}
|
}
|
||||||
else if word == "acc" || word == "accessed" {
|
else if word == "acc" || word == "accessed" {
|
||||||
Ok(TimeTypes { accessed: true, modified: false, created: false })
|
TimeTypes { modified: false, changed: false, accessed: true, created: false }
|
||||||
}
|
}
|
||||||
else if word == "cr" || word == "created" {
|
else if word == "cr" || word == "created" {
|
||||||
Ok(TimeTypes { accessed: false, modified: false, created: true })
|
TimeTypes { modified: false, changed: false, accessed: false, created: true }
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Err(Misfire::BadArgument(&flags::TIME, word.into()))
|
return Err(Misfire::BadArgument(&flags::TIME, word.into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if modified || created || accessed {
|
else if modified || changed || accessed || created {
|
||||||
Ok(TimeTypes { accessed, modified, created })
|
TimeTypes { modified, changed, accessed, created }
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Ok(TimeTypes::default())
|
TimeTypes::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut fields = vec![];
|
||||||
|
if time_types.modified { fields.push(PlatformMetadata::ModifiedTime); }
|
||||||
|
if time_types.changed { fields.push(PlatformMetadata::ChangedTime); }
|
||||||
|
if time_types.accessed { fields.push(PlatformMetadata::AccessedTime); }
|
||||||
|
if time_types.created { fields.push(PlatformMetadata::CreatedTime); }
|
||||||
|
|
||||||
|
for field in fields {
|
||||||
|
if let Err(misfire) = field.check_supported() {
|
||||||
|
return Err(misfire);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Ok(time_types)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,7 +379,8 @@ mod test {
|
|||||||
use options::test::Strictnesses::*;
|
use options::test::Strictnesses::*;
|
||||||
|
|
||||||
static TEST_ARGS: &[&Arg] = &[ &flags::BINARY, &flags::BYTES, &flags::TIME_STYLE,
|
static TEST_ARGS: &[&Arg] = &[ &flags::BINARY, &flags::BYTES, &flags::TIME_STYLE,
|
||||||
&flags::TIME, &flags::MODIFIED, &flags::CREATED, &flags::ACCESSED,
|
&flags::TIME, &flags::MODIFIED, &flags::CHANGED,
|
||||||
|
&flags::CREATED, &flags::ACCESSED,
|
||||||
&flags::HEADER, &flags::GROUP, &flags::INODE, &flags::GIT,
|
&flags::HEADER, &flags::GROUP, &flags::INODE, &flags::GIT,
|
||||||
&flags::LINKS, &flags::BLOCKS, &flags::LONG, &flags::LEVEL,
|
&flags::LINKS, &flags::BLOCKS, &flags::LONG, &flags::LEVEL,
|
||||||
&flags::GRID, &flags::ACROSS, &flags::ONE_LINE ];
|
&flags::GRID, &flags::ACROSS, &flags::ONE_LINE ];
|
||||||
@ -493,32 +515,47 @@ mod test {
|
|||||||
test!(empty: TimeTypes <- []; Both => Ok(TimeTypes::default()));
|
test!(empty: TimeTypes <- []; Both => Ok(TimeTypes::default()));
|
||||||
|
|
||||||
// Modified
|
// Modified
|
||||||
test!(modified: TimeTypes <- ["--modified"]; Both => Ok(TimeTypes { accessed: false, modified: true, created: false }));
|
test!(modified: TimeTypes <- ["--modified"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }));
|
||||||
test!(m: TimeTypes <- ["-m"]; Both => Ok(TimeTypes { accessed: false, modified: true, created: false }));
|
test!(m: TimeTypes <- ["-m"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }));
|
||||||
test!(time_mod: TimeTypes <- ["--time=modified"]; Both => Ok(TimeTypes { accessed: false, modified: true, created: false }));
|
test!(time_mod: TimeTypes <- ["--time=modified"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }));
|
||||||
test!(time_m: TimeTypes <- ["-tmod"]; Both => Ok(TimeTypes { accessed: false, modified: true, created: false }));
|
test!(time_m: TimeTypes <- ["-tmod"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }));
|
||||||
|
|
||||||
|
// Changed
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
test!(changed: TimeTypes <- ["--changed"]; Both => Ok(TimeTypes { modified: false, changed: true, accessed: false, created: false }));
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
test!(time_ch: TimeTypes <- ["--time=changed"]; Both => Ok(TimeTypes { modified: false, changed: true, accessed: false, created: false }));
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
test!(time_c: TimeTypes <- ["-t", "ch"]; Both => Ok(TimeTypes { modified: false, changed: true, accessed: false, created: false }));
|
||||||
|
|
||||||
// Accessed
|
// Accessed
|
||||||
test!(acc: TimeTypes <- ["--accessed"]; Both => Ok(TimeTypes { accessed: true, modified: false, created: false }));
|
test!(acc: TimeTypes <- ["--accessed"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false }));
|
||||||
test!(a: TimeTypes <- ["-u"]; Both => Ok(TimeTypes { accessed: true, modified: false, created: false }));
|
test!(a: TimeTypes <- ["-u"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false }));
|
||||||
test!(time_acc: TimeTypes <- ["--time", "accessed"]; Both => Ok(TimeTypes { accessed: true, modified: false, created: false }));
|
test!(time_acc: TimeTypes <- ["--time", "accessed"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false }));
|
||||||
test!(time_a: TimeTypes <- ["-t", "acc"]; Both => Ok(TimeTypes { accessed: true, modified: false, created: false }));
|
test!(time_a: TimeTypes <- ["-t", "acc"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false }));
|
||||||
|
|
||||||
// Created
|
// Created
|
||||||
test!(cr: TimeTypes <- ["--created"]; Both => Ok(TimeTypes { accessed: false, modified: false, created: true }));
|
#[cfg(not(target_os = "linux"))]
|
||||||
test!(c: TimeTypes <- ["-U"]; Both => Ok(TimeTypes { accessed: false, modified: false, created: true }));
|
test!(cr: TimeTypes <- ["--created"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true }));
|
||||||
test!(time_cr: TimeTypes <- ["--time=created"]; Both => Ok(TimeTypes { accessed: false, modified: false, created: true }));
|
#[cfg(target_os = "linux")]
|
||||||
test!(time_c: TimeTypes <- ["-tcr"]; Both => Ok(TimeTypes { accessed: false, modified: false, created: true }));
|
test!(cr: TimeTypes <- ["--created"]; Both => err Misfire::Unsupported("creation time is not available on this platform currently".to_string()));
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
test!(c: TimeTypes <- ["-U"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true }));
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
test!(time_cr: TimeTypes <- ["--time=created"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true }));
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
test!(time_c: TimeTypes <- ["-tcr"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true }));
|
||||||
|
|
||||||
// Multiples
|
// Multiples
|
||||||
test!(time_uu: TimeTypes <- ["-uU"]; Both => Ok(TimeTypes { accessed: true, modified: false, created: true }));
|
test!(time_uu: TimeTypes <- ["-u", "--modified"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: true, created: false }));
|
||||||
|
|
||||||
|
|
||||||
// Errors
|
// Errors
|
||||||
test!(time_tea: TimeTypes <- ["--time=tea"]; Both => err Misfire::BadArgument(&flags::TIME, OsString::from("tea")));
|
test!(time_tea: TimeTypes <- ["--time=tea"]; Both => err Misfire::BadArgument(&flags::TIME, OsString::from("tea")));
|
||||||
test!(time_ea: TimeTypes <- ["-tea"]; Both => err Misfire::BadArgument(&flags::TIME, OsString::from("ea")));
|
test!(time_ea: TimeTypes <- ["-tea"]; Both => err Misfire::BadArgument(&flags::TIME, OsString::from("ea")));
|
||||||
|
|
||||||
// Overriding
|
// Overriding
|
||||||
test!(overridden: TimeTypes <- ["-tcr", "-tmod"]; Last => Ok(TimeTypes { accessed: false, modified: true, created: false }));
|
test!(overridden: TimeTypes <- ["-tcr", "-tmod"]; Last => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }));
|
||||||
test!(overridden_2: TimeTypes <- ["-tcr", "-tmod"]; Complain => err Misfire::Duplicate(Flag::Short(b't'), Flag::Short(b't')));
|
test!(overridden_2: TimeTypes <- ["-tcr", "-tmod"]; Complain => err Misfire::Duplicate(Flag::Short(b't'), Flag::Short(b't')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ mod size;
|
|||||||
pub use self::size::Colours as SizeColours;
|
pub use self::size::Colours as SizeColours;
|
||||||
|
|
||||||
mod times;
|
mod times;
|
||||||
|
pub use self::times::Render as TimeRender;
|
||||||
// times does too
|
// times does too
|
||||||
|
|
||||||
mod users;
|
mod users;
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
use datetime::TimeZone;
|
use datetime::TimeZone;
|
||||||
use ansi_term::Style;
|
use ansi_term::Style;
|
||||||
|
|
||||||
use fs::fields as f;
|
|
||||||
use output::cell::TextCell;
|
use output::cell::TextCell;
|
||||||
use output::time::TimeFormat;
|
use output::time::TimeFormat;
|
||||||
|
|
||||||
|
|
||||||
impl f::Time {
|
pub trait Render {
|
||||||
pub fn render(self, style: Style,
|
fn render(self, style: Style,
|
||||||
|
tz: &Option<TimeZone>,
|
||||||
|
format: &TimeFormat) -> TextCell;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for std::time::Duration {
|
||||||
|
fn render(self, style: Style,
|
||||||
tz: &Option<TimeZone>,
|
tz: &Option<TimeZone>,
|
||||||
format: &TimeFormat) -> TextCell {
|
format: &TimeFormat) -> TextCell {
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ use users::UsersCache;
|
|||||||
|
|
||||||
use style::Colours;
|
use style::Colours;
|
||||||
use output::cell::TextCell;
|
use output::cell::TextCell;
|
||||||
|
use output::render::TimeRender;
|
||||||
use output::time::TimeFormat;
|
use output::time::TimeFormat;
|
||||||
use fs::{File, fields as f};
|
use fs::{File, fields as f};
|
||||||
use fs::feature::git::GitCache;
|
use fs::feature::git::GitCache;
|
||||||
@ -78,6 +79,10 @@ impl Columns {
|
|||||||
columns.push(Column::Timestamp(TimeType::Modified));
|
columns.push(Column::Timestamp(TimeType::Modified));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.time_types.changed {
|
||||||
|
columns.push(Column::Timestamp(TimeType::Changed));
|
||||||
|
}
|
||||||
|
|
||||||
if self.time_types.created {
|
if self.time_types.created {
|
||||||
columns.push(Column::Timestamp(TimeType::Created));
|
columns.push(Column::Timestamp(TimeType::Created));
|
||||||
}
|
}
|
||||||
@ -175,14 +180,16 @@ impl Default for SizeFormat {
|
|||||||
/// across most (all?) operating systems.
|
/// across most (all?) operating systems.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
||||||
pub enum TimeType {
|
pub enum TimeType {
|
||||||
|
/// The file’s modified time (`st_mtime`).
|
||||||
|
Modified,
|
||||||
|
|
||||||
|
/// The file’s changed time (`st_ctime`)
|
||||||
|
Changed,
|
||||||
|
|
||||||
/// The file’s accessed time (`st_atime`).
|
/// The file’s accessed time (`st_atime`).
|
||||||
Accessed,
|
Accessed,
|
||||||
|
|
||||||
/// The file’s modified time (`st_mtime`).
|
/// The file’s creation time (`btime` or `birthtime`).
|
||||||
Modified,
|
|
||||||
|
|
||||||
/// The file’s creation time (`st_ctime`).
|
|
||||||
Created,
|
Created,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,8 +198,9 @@ impl TimeType {
|
|||||||
/// Returns the text to use for a column’s heading in the columns output.
|
/// Returns the text to use for a column’s heading in the columns output.
|
||||||
pub fn header(self) -> &'static str {
|
pub fn header(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
TimeType::Accessed => "Date Accessed",
|
|
||||||
TimeType::Modified => "Date Modified",
|
TimeType::Modified => "Date Modified",
|
||||||
|
TimeType::Changed => "Date Changed",
|
||||||
|
TimeType::Accessed => "Date Accessed",
|
||||||
TimeType::Created => "Date Created",
|
TimeType::Created => "Date Created",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,8 +214,9 @@ impl TimeType {
|
|||||||
/// the time columns entirely (yet).
|
/// the time columns entirely (yet).
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
||||||
pub struct TimeTypes {
|
pub struct TimeTypes {
|
||||||
pub accessed: bool,
|
|
||||||
pub modified: bool,
|
pub modified: bool,
|
||||||
|
pub changed: bool,
|
||||||
|
pub accessed: bool,
|
||||||
pub created: bool,
|
pub created: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +225,7 @@ impl Default for TimeTypes {
|
|||||||
/// By default, display just the ‘modified’ time. This is the most
|
/// By default, display just the ‘modified’ time. This is the most
|
||||||
/// common option, which is why it has this shorthand.
|
/// common option, which is why it has this shorthand.
|
||||||
fn default() -> TimeTypes {
|
fn default() -> TimeTypes {
|
||||||
TimeTypes { accessed: false, modified: true, created: false }
|
TimeTypes { modified: true, changed: false, accessed: false, created: false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,6 +351,7 @@ impl<'a, 'f> Table<'a> {
|
|||||||
Column::GitStatus => self.git_status(file).render(self.colours),
|
Column::GitStatus => self.git_status(file).render(self.colours),
|
||||||
|
|
||||||
Column::Timestamp(Modified) => file.modified_time().render(self.colours.date, &self.env.tz, &self.time_format),
|
Column::Timestamp(Modified) => file.modified_time().render(self.colours.date, &self.env.tz, &self.time_format),
|
||||||
|
Column::Timestamp(Changed) => file.changed_time() .render(self.colours.date, &self.env.tz, &self.time_format),
|
||||||
Column::Timestamp(Created) => file.created_time() .render(self.colours.date, &self.env.tz, &self.time_format),
|
Column::Timestamp(Created) => file.created_time() .render(self.colours.date, &self.env.tz, &self.time_format),
|
||||||
Column::Timestamp(Accessed) => file.accessed_time().render(self.colours.date, &self.env.tz, &self.time_format),
|
Column::Timestamp(Accessed) => file.accessed_time().render(self.colours.date, &self.env.tz, &self.time_format),
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
//! Timestamp formatting.
|
//! Timestamp formatting.
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use datetime::{LocalDateTime, TimeZone, DatePiece, TimePiece};
|
use datetime::{LocalDateTime, TimeZone, DatePiece, TimePiece};
|
||||||
use datetime::fmt::DateFormat;
|
use datetime::fmt::DateFormat;
|
||||||
use locale;
|
use locale;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
use fs::fields::Time;
|
|
||||||
|
|
||||||
|
|
||||||
/// Every timestamp in exa needs to be rendered by a **time format**.
|
/// Every timestamp in exa needs to be rendered by a **time format**.
|
||||||
/// Formatting times is tricky, because how a timestamp is rendered can
|
/// Formatting times is tricky, because how a timestamp is rendered can
|
||||||
@ -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: Time) -> String {
|
pub fn format_local(&self, time: Duration) -> 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: Time, zone: &TimeZone) -> String {
|
pub fn format_zoned(&self, time: Duration, 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),
|
||||||
@ -128,8 +128,8 @@ impl DefaultFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn format_local(&self, time: Time) -> String {
|
fn format_local(&self, time: Duration) -> String {
|
||||||
let date = LocalDateTime::at(time.seconds as i64);
|
let date = LocalDateTime::at(time.as_secs() as i64);
|
||||||
|
|
||||||
if self.is_recent(date) {
|
if self.is_recent(date) {
|
||||||
self.date_and_time.format(&date, &self.locale)
|
self.date_and_time.format(&date, &self.locale)
|
||||||
@ -140,8 +140,8 @@ impl DefaultFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn format_zoned(&self, time: Time, zone: &TimeZone) -> String {
|
fn format_zoned(&self, time: Duration, zone: &TimeZone) -> String {
|
||||||
let date = zone.to_zoned(LocalDateTime::at(time.seconds as i64));
|
let date = zone.to_zoned(LocalDateTime::at(time.as_secs() as i64));
|
||||||
|
|
||||||
if self.is_recent(date) {
|
if self.is_recent(date) {
|
||||||
self.date_and_time.format(&date, &self.locale)
|
self.date_and_time.format(&date, &self.locale)
|
||||||
@ -154,16 +154,16 @@ impl DefaultFormat {
|
|||||||
|
|
||||||
|
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn long_local(time: Time) -> String {
|
fn long_local(time: Duration) -> String {
|
||||||
let date = LocalDateTime::at(time.seconds as i64);
|
let date = LocalDateTime::at(time.as_secs() as i64);
|
||||||
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: Time, zone: &TimeZone) -> String {
|
fn long_zoned(time: Duration, zone: &TimeZone) -> String {
|
||||||
let date = zone.to_zoned(LocalDateTime::at(time.seconds as i64));
|
let date = zone.to_zoned(LocalDateTime::at(time.as_secs() as i64));
|
||||||
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())
|
||||||
@ -171,23 +171,23 @@ fn long_zoned(time: Time, zone: &TimeZone) -> String {
|
|||||||
|
|
||||||
|
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn full_local(time: Time) -> String {
|
fn full_local(time: Duration) -> String {
|
||||||
let date = LocalDateTime::at(time.seconds as i64);
|
let date = LocalDateTime::at(time.as_secs() as i64);
|
||||||
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.nanoseconds)
|
date.hour(), date.minute(), date.second(), time.subsec_nanos())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn full_zoned(time: Time, zone: &TimeZone) -> String {
|
fn full_zoned(time: Duration, zone: &TimeZone) -> String {
|
||||||
use datetime::Offset;
|
use datetime::Offset;
|
||||||
|
|
||||||
let local = LocalDateTime::at(time.seconds as i64);
|
let local = LocalDateTime::at(time.as_secs() as i64);
|
||||||
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.nanoseconds,
|
date.hour(), date.minute(), date.second(), time.subsec_nanos(),
|
||||||
offset.hours(), offset.minutes().abs())
|
offset.hours(), offset.minutes().abs())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,8 +214,8 @@ impl ISOFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn format_local(&self, time: Time) -> String {
|
fn format_local(&self, time: Duration) -> String {
|
||||||
let date = LocalDateTime::at(time.seconds as i64);
|
let date = LocalDateTime::at(time.as_secs() as i64);
|
||||||
|
|
||||||
if self.is_recent(date) {
|
if self.is_recent(date) {
|
||||||
format!("{:02}-{:02} {:02}:{:02}",
|
format!("{:02}-{:02} {:02}:{:02}",
|
||||||
@ -229,8 +229,8 @@ impl ISOFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn format_zoned(&self, time: Time, zone: &TimeZone) -> String {
|
fn format_zoned(&self, time: Duration, zone: &TimeZone) -> String {
|
||||||
let date = zone.to_zoned(LocalDateTime::at(time.seconds as i64));
|
let date = zone.to_zoned(LocalDateTime::at(time.as_secs() as i64));
|
||||||
|
|
||||||
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