mirror of
https://github.com/uutils/coreutils.git
synced 2025-12-23 08:47:37 +00:00
Merge pull request #8390 from drinkcat/ls-bigtime
Some checks are pending
CICD / Style/cargo-deny (push) Waiting to run
CICD / Test all features separately (push) Blocked by required conditions
CICD / Style/deps (push) Waiting to run
CICD / Documentation/warnings (push) Waiting to run
CICD / MinRustV (push) Waiting to run
CICD / Build (push) Blocked by required conditions
CICD / Dependencies (push) Waiting to run
CICD / Build/Makefile (push) Blocked by required conditions
CICD / Build/stable (push) Blocked by required conditions
CICD / Build/nightly (push) Blocked by required conditions
CICD / Binary sizes (push) Blocked by required conditions
CICD / Tests/BusyBox test suite (push) Blocked by required conditions
CICD / Tests/Toybox test suite (push) Blocked by required conditions
CICD / Code Coverage (push) Waiting to run
CICD / Separate Builds (push) Waiting to run
CICD / Build/SELinux (push) Blocked by required conditions
GnuTests / Run GNU tests (push) Waiting to run
Android / Test builds (push) Waiting to run
Code Quality / Style/format (push) Waiting to run
Code Quality / Style/lint (push) Waiting to run
Code Quality / Style/spelling (push) Waiting to run
Code Quality / Style/toml (push) Waiting to run
Code Quality / Style/Python (push) Waiting to run
Code Quality / Pre-commit hooks (push) Waiting to run
FreeBSD / Style and Lint (push) Waiting to run
FreeBSD / Tests (push) Waiting to run
Some checks are pending
CICD / Style/cargo-deny (push) Waiting to run
CICD / Test all features separately (push) Blocked by required conditions
CICD / Style/deps (push) Waiting to run
CICD / Documentation/warnings (push) Waiting to run
CICD / MinRustV (push) Waiting to run
CICD / Build (push) Blocked by required conditions
CICD / Dependencies (push) Waiting to run
CICD / Build/Makefile (push) Blocked by required conditions
CICD / Build/stable (push) Blocked by required conditions
CICD / Build/nightly (push) Blocked by required conditions
CICD / Binary sizes (push) Blocked by required conditions
CICD / Tests/BusyBox test suite (push) Blocked by required conditions
CICD / Tests/Toybox test suite (push) Blocked by required conditions
CICD / Code Coverage (push) Waiting to run
CICD / Separate Builds (push) Waiting to run
CICD / Build/SELinux (push) Blocked by required conditions
GnuTests / Run GNU tests (push) Waiting to run
Android / Test builds (push) Waiting to run
Code Quality / Style/format (push) Waiting to run
Code Quality / Style/lint (push) Waiting to run
Code Quality / Style/spelling (push) Waiting to run
Code Quality / Style/toml (push) Waiting to run
Code Quality / Style/Python (push) Waiting to run
Code Quality / Pre-commit hooks (push) Waiting to run
FreeBSD / Style and Lint (push) Waiting to run
FreeBSD / Tests (push) Waiting to run
du/ls: Merge date formatting code, and handle timestamps far in the future/past by just printing them
This commit is contained in:
commit
ded761d334
9 changed files with 170 additions and 103 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
|
@ -3164,7 +3164,6 @@ dependencies = [
|
|||
name = "uu_du"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"glob",
|
||||
"thiserror 2.0.12",
|
||||
|
|
@ -3381,7 +3380,6 @@ dependencies = [
|
|||
"clap",
|
||||
"glob",
|
||||
"hostname",
|
||||
"jiff",
|
||||
"lscolors",
|
||||
"selinux",
|
||||
"terminal_size",
|
||||
|
|
@ -3995,6 +3993,7 @@ dependencies = [
|
|||
"icu_locale",
|
||||
"icu_provider",
|
||||
"itertools 0.14.0",
|
||||
"jiff",
|
||||
"libc",
|
||||
"md-5",
|
||||
"memchr",
|
||||
|
|
|
|||
|
|
@ -18,11 +18,10 @@ workspace = true
|
|||
path = "src/du.rs"
|
||||
|
||||
[dependencies]
|
||||
chrono = { workspace = true }
|
||||
# For the --exclude & --exclude-from options
|
||||
glob = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
uucore = { workspace = true, features = ["format", "parser"] }
|
||||
uucore = { workspace = true, features = ["format", "parser", "time"] }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use chrono::{DateTime, Local};
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command, builder::PossibleValue};
|
||||
use glob::Pattern;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
|
@ -11,7 +10,7 @@ use std::env;
|
|||
#[cfg(not(windows))]
|
||||
use std::fs::Metadata;
|
||||
use std::fs::{self, DirEntry, File};
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::io::{BufRead, BufReader, stdout};
|
||||
#[cfg(not(windows))]
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
#[cfg(windows)]
|
||||
|
|
@ -576,13 +575,13 @@ impl StatPrinter {
|
|||
}
|
||||
|
||||
fn print_stat(&self, stat: &Stat, size: u64) -> UResult<()> {
|
||||
print!("{}\t", self.convert_size(size));
|
||||
|
||||
if let Some(time) = self.time {
|
||||
let secs = get_time_secs(time, stat)?;
|
||||
let tm = DateTime::<Local>::from(UNIX_EPOCH + Duration::from_secs(secs));
|
||||
let time_str = tm.format(&self.time_format).to_string();
|
||||
print!("{}\t{time_str}\t", self.convert_size(size));
|
||||
} else {
|
||||
print!("{}\t", self.convert_size(size));
|
||||
let time = UNIX_EPOCH + Duration::from_secs(secs);
|
||||
uucore::time::format_system_time(&mut stdout(), time, &self.time_format, true)?;
|
||||
print!("\t");
|
||||
}
|
||||
|
||||
print_verbatim(&stat.path).unwrap();
|
||||
|
|
|
|||
|
|
@ -24,11 +24,6 @@ ansi-width = { workspace = true }
|
|||
clap = { workspace = true, features = ["env"] }
|
||||
glob = { workspace = true }
|
||||
hostname = { workspace = true }
|
||||
jiff = { workspace = true, features = [
|
||||
"tzdb-bundle-platform",
|
||||
"tzdb-zoneinfo",
|
||||
"tzdb-concatenated",
|
||||
] }
|
||||
lscolors = { workspace = true }
|
||||
selinux = { workspace = true, optional = true }
|
||||
terminal_size = { workspace = true }
|
||||
|
|
@ -41,6 +36,7 @@ uucore = { workspace = true, features = [
|
|||
"fsxattr",
|
||||
"parser",
|
||||
"quoting-style",
|
||||
"time",
|
||||
"version-cmp",
|
||||
] }
|
||||
uutils_term_grid = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -31,9 +31,6 @@ use clap::{
|
|||
builder::{NonEmptyStringValueParser, PossibleValue, ValueParser},
|
||||
};
|
||||
use glob::{MatchOptions, Pattern};
|
||||
use jiff::fmt::StdIoWrite;
|
||||
use jiff::fmt::strtime::BrokenDownTime;
|
||||
use jiff::{Timestamp, Zoned};
|
||||
use lscolors::LsColors;
|
||||
use term_grid::{DEFAULT_SEPARATOR_SIZE, Direction, Filling, Grid, GridOptions, SPACES_IN_TAB};
|
||||
use thiserror::Error;
|
||||
|
|
@ -258,56 +255,30 @@ enum Time {
|
|||
Birth,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum TimeStyle {
|
||||
FullIso,
|
||||
LongIso,
|
||||
Iso,
|
||||
Locale,
|
||||
Format(String),
|
||||
}
|
||||
|
||||
/// Whether the given date is considered recent (i.e., in the last 6 months).
|
||||
fn is_recent(time: Timestamp, state: &mut ListState) -> bool {
|
||||
time > state.recent_time_threshold
|
||||
}
|
||||
|
||||
impl TimeStyle {
|
||||
/// Format the given time according to this time format style.
|
||||
fn format(
|
||||
&self,
|
||||
date: Zoned,
|
||||
out: &mut Vec<u8>,
|
||||
state: &mut ListState,
|
||||
) -> Result<(), jiff::Error> {
|
||||
let recent = is_recent(date.timestamp(), state);
|
||||
let tm = BrokenDownTime::from(&date);
|
||||
let mut out = StdIoWrite(out);
|
||||
let config = jiff::fmt::strtime::Config::new().lenient(true);
|
||||
|
||||
let fmt = match (self, recent) {
|
||||
(Self::FullIso, _) => "%Y-%m-%d %H:%M:%S.%f %z",
|
||||
(Self::LongIso, _) => "%Y-%m-%d %H:%M",
|
||||
(Self::Iso, true) => "%m-%d %H:%M",
|
||||
(Self::Iso, false) => "%Y-%m-%d ",
|
||||
// TODO: Using correct locale string is not implemented.
|
||||
(Self::Locale, true) => "%b %e %H:%M",
|
||||
(Self::Locale, false) => "%b %e %Y",
|
||||
(Self::Format(fmt), _) => fmt,
|
||||
};
|
||||
|
||||
tm.format_with_config(&config, fmt, &mut out)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_time_style(options: &clap::ArgMatches) -> Result<TimeStyle, LsError> {
|
||||
let possible_time_styles = vec![
|
||||
"full-iso".to_string(),
|
||||
"long-iso".to_string(),
|
||||
"iso".to_string(),
|
||||
"locale".to_string(),
|
||||
"+FORMAT (e.g., +%H:%M) for a 'date'-style format".to_string(),
|
||||
fn parse_time_style(options: &clap::ArgMatches) -> Result<(String, Option<String>), LsError> {
|
||||
const TIME_STYLES: [(&str, (&str, Option<&str>)); 4] = [
|
||||
("full-iso", ("%Y-%m-%d %H:%M:%S.%f %z", None)),
|
||||
("long-iso", ("%Y-%m-%d %H:%M", None)),
|
||||
("iso", ("%m-%d %H:%M", Some("%Y-%m-%d "))),
|
||||
// TODO: Using correct locale string is not implemented.
|
||||
("locale", ("%b %e %H:%M", Some("%b %e %Y"))),
|
||||
];
|
||||
// A map from a time-style parameter to a length-2 tuple of formats:
|
||||
// the first one is used for recent dates, the second one for older ones (optional).
|
||||
let time_styles = HashMap::from(TIME_STYLES);
|
||||
let possible_time_styles = TIME_STYLES
|
||||
.iter()
|
||||
.map(|(x, _)| *x)
|
||||
.chain(iter::once(
|
||||
"+FORMAT (e.g., +%H:%M) for a 'date'-style format",
|
||||
))
|
||||
.map(|s| s.to_string());
|
||||
|
||||
// Convert time_styles references to owned String/option.
|
||||
fn ok((recent, older): (&str, Option<&str>)) -> Result<(String, Option<String>), LsError> {
|
||||
Ok((recent.to_string(), older.map(String::from)))
|
||||
}
|
||||
|
||||
if let Some(field) = options.get_one::<String>(options::TIME_STYLE) {
|
||||
//If both FULL_TIME and TIME_STYLE are present
|
||||
//The one added last is dominant
|
||||
|
|
@ -315,26 +286,23 @@ fn parse_time_style(options: &clap::ArgMatches) -> Result<TimeStyle, LsError> {
|
|||
&& options.indices_of(options::FULL_TIME).unwrap().next_back()
|
||||
> options.indices_of(options::TIME_STYLE).unwrap().next_back()
|
||||
{
|
||||
Ok(TimeStyle::FullIso)
|
||||
ok(time_styles["full-iso"])
|
||||
} else {
|
||||
match field.as_str() {
|
||||
"full-iso" => Ok(TimeStyle::FullIso),
|
||||
"long-iso" => Ok(TimeStyle::LongIso),
|
||||
"iso" => Ok(TimeStyle::Iso),
|
||||
"locale" => Ok(TimeStyle::Locale),
|
||||
_ => match field.chars().next().unwrap() {
|
||||
'+' => Ok(TimeStyle::Format(String::from(&field[1..]))),
|
||||
match time_styles.get(field.as_str()) {
|
||||
Some(formats) => ok(*formats),
|
||||
None => match field.chars().next().unwrap() {
|
||||
'+' => Ok((field[1..].to_string(), None)),
|
||||
_ => Err(LsError::TimeStyleParseError(
|
||||
String::from(field),
|
||||
possible_time_styles,
|
||||
possible_time_styles.collect(),
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
} else if options.get_flag(options::FULL_TIME) {
|
||||
Ok(TimeStyle::FullIso)
|
||||
ok(time_styles["full-iso"])
|
||||
} else {
|
||||
Ok(TimeStyle::Locale)
|
||||
ok(time_styles["locale"])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -377,7 +345,8 @@ pub struct Config {
|
|||
// Dir and vdir needs access to this field
|
||||
pub quoting_style: QuotingStyle,
|
||||
indicator_style: IndicatorStyle,
|
||||
time_style: TimeStyle,
|
||||
time_format_recent: String, // Time format for recent dates
|
||||
time_format_older: Option<String>, // Time format for older dates (optional, if not present, time_format_recent is used)
|
||||
context: bool,
|
||||
selinux_supported: bool,
|
||||
group_directories_first: bool,
|
||||
|
|
@ -925,10 +894,10 @@ impl Config {
|
|||
let indicator_style = extract_indicator_style(options);
|
||||
// Only parse the value to "--time-style" if it will become relevant.
|
||||
let dired = options.get_flag(options::DIRED);
|
||||
let time_style = if format == Format::Long || dired {
|
||||
let (time_format_recent, time_format_older) = if format == Format::Long || dired {
|
||||
parse_time_style(options)?
|
||||
} else {
|
||||
TimeStyle::Iso
|
||||
Default::default()
|
||||
};
|
||||
|
||||
let mut ignore_patterns: Vec<Pattern> = Vec::new();
|
||||
|
|
@ -1114,7 +1083,8 @@ impl Config {
|
|||
width,
|
||||
quoting_style,
|
||||
indicator_style,
|
||||
time_style,
|
||||
time_format_recent,
|
||||
time_format_older,
|
||||
context,
|
||||
selinux_supported: {
|
||||
#[cfg(feature = "selinux")]
|
||||
|
|
@ -2002,7 +1972,7 @@ struct ListState<'a> {
|
|||
uid_cache: HashMap<u32, String>,
|
||||
#[cfg(unix)]
|
||||
gid_cache: HashMap<u32, String>,
|
||||
recent_time_threshold: Timestamp,
|
||||
recent_time_threshold: SystemTime,
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
|
|
@ -2020,7 +1990,7 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
|
|||
#[cfg(unix)]
|
||||
gid_cache: HashMap::new(),
|
||||
// According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average.
|
||||
recent_time_threshold: Timestamp::now() - Duration::new(31_556_952 / 2, 0),
|
||||
recent_time_threshold: SystemTime::now() - Duration::new(31_556_952 / 2, 0),
|
||||
};
|
||||
|
||||
for loc in locs {
|
||||
|
|
@ -2993,7 +2963,7 @@ fn display_group(_metadata: &Metadata, _config: &Config, _state: &mut ListState)
|
|||
"somegroup"
|
||||
}
|
||||
|
||||
// The implementations for get_time are separated because some options, such
|
||||
// The implementations for get_system_time are separated because some options, such
|
||||
// as ctime will not be available
|
||||
#[cfg(unix)]
|
||||
fn get_system_time(md: &Metadata, config: &Config) -> Option<SystemTime> {
|
||||
|
|
@ -3015,28 +2985,25 @@ fn get_system_time(md: &Metadata, config: &Config) -> Option<SystemTime> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_time(md: &Metadata, config: &Config) -> Option<Zoned> {
|
||||
let time = get_system_time(md, config)?;
|
||||
time.try_into().ok()
|
||||
}
|
||||
|
||||
fn display_date(
|
||||
metadata: &Metadata,
|
||||
config: &Config,
|
||||
state: &mut ListState,
|
||||
out: &mut Vec<u8>,
|
||||
) -> UResult<()> {
|
||||
match get_time(metadata, config) {
|
||||
// TODO: Some fancier error conversion might be nice.
|
||||
Some(time) => config
|
||||
.time_style
|
||||
.format(time, out, state)
|
||||
.map_err(|x| USimpleError::new(1, x.to_string())),
|
||||
None => {
|
||||
out.extend(b"???");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
let Some(time) = get_system_time(metadata, config) else {
|
||||
out.extend(b"???");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// Use "recent" format if the given date is considered recent (i.e., in the last 6 months),
|
||||
// or if no "older" format is available.
|
||||
let fmt = match &config.time_format_older {
|
||||
Some(time_format_older) if time <= state.recent_time_threshold => time_format_older,
|
||||
_ => &config.time_format_recent,
|
||||
};
|
||||
|
||||
uucore::time::format_system_time(out, time, fmt, false)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# spell-checker:ignore (features) bigdecimal zerocopy extendedbigdecimal
|
||||
# spell-checker:ignore (features) bigdecimal zerocopy extendedbigdecimal tzdb zoneinfo
|
||||
|
||||
[package]
|
||||
name = "uucore"
|
||||
|
|
@ -29,6 +29,11 @@ dunce = { version = "1.0.4", optional = true }
|
|||
wild = "2.2.1"
|
||||
glob = { workspace = true, optional = true }
|
||||
itertools = { workspace = true, optional = true }
|
||||
jiff = { workspace = true, optional = true, features = [
|
||||
"tzdb-bundle-platform",
|
||||
"tzdb-zoneinfo",
|
||||
"tzdb-concatenated",
|
||||
] }
|
||||
time = { workspace = true, optional = true, features = [
|
||||
"formatting",
|
||||
"local-offset",
|
||||
|
|
@ -150,4 +155,5 @@ utmpx = ["time", "time/macros", "libc", "dns-lookup"]
|
|||
version-cmp = []
|
||||
wide = []
|
||||
tty = []
|
||||
time = ["jiff"]
|
||||
uptime = ["chrono", "libc", "windows-sys", "utmpx", "utmp-classic"]
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ pub mod ranges;
|
|||
pub mod ringbuffer;
|
||||
#[cfg(feature = "sum")]
|
||||
pub mod sum;
|
||||
#[cfg(feature = "time")]
|
||||
pub mod time;
|
||||
#[cfg(feature = "update-control")]
|
||||
pub mod update_control;
|
||||
#[cfg(feature = "uptime")]
|
||||
|
|
|
|||
97
src/uucore/src/lib/features/time.rs
Normal file
97
src/uucore/src/lib/features/time.rs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// This file is part of the uutils coreutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore (ToDO) strtime
|
||||
|
||||
//! Set of functions related to time handling
|
||||
|
||||
use jiff::Zoned;
|
||||
use jiff::fmt::StdIoWrite;
|
||||
use jiff::fmt::strtime::{BrokenDownTime, Config};
|
||||
use std::io::Write;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use crate::error::{UResult, USimpleError};
|
||||
use crate::show_error;
|
||||
|
||||
/// Format the given date according to this time format style.
|
||||
fn format_zoned<W: Write>(out: &mut W, zoned: Zoned, fmt: &str) -> UResult<()> {
|
||||
let tm = BrokenDownTime::from(&zoned);
|
||||
let mut out = StdIoWrite(out);
|
||||
let config = Config::new().lenient(true);
|
||||
tm.format_with_config(&config, fmt, &mut out)
|
||||
.map_err(|x| USimpleError::new(1, x.to_string()))
|
||||
}
|
||||
|
||||
/// Format a `SystemTime` according to given fmt, and append to vector out.
|
||||
pub fn format_system_time<W: Write>(
|
||||
out: &mut W,
|
||||
time: SystemTime,
|
||||
fmt: &str,
|
||||
show_error: bool,
|
||||
) -> UResult<()> {
|
||||
let zoned: Result<Zoned, _> = time.try_into();
|
||||
match zoned {
|
||||
Ok(zoned) => format_zoned(out, zoned, fmt),
|
||||
Err(_) => {
|
||||
// Assume that if we cannot build a Zoned element, the timestamp is
|
||||
// out of reasonable range, just print it then.
|
||||
// TODO: The range allowed by jiff is different from what GNU accepts,
|
||||
// but it still far enough in the future/past to be unlikely to matter:
|
||||
// jiff: Year between -9999 to 9999 (UTC) [-377705023201..=253402207200]
|
||||
// GNU: Year fits in signed 32 bits (timezone dependent)
|
||||
let ts: i64 = if time > UNIX_EPOCH {
|
||||
time.duration_since(UNIX_EPOCH).unwrap().as_secs() as i64
|
||||
} else {
|
||||
-(UNIX_EPOCH.duration_since(time).unwrap().as_secs() as i64)
|
||||
};
|
||||
let str = ts.to_string();
|
||||
if show_error {
|
||||
show_error!("time '{str}' is out of range");
|
||||
}
|
||||
out.write_all(str.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::time::format_system_time;
|
||||
use std::time::{Duration, UNIX_EPOCH};
|
||||
|
||||
// Test epoch SystemTime get printed correctly at UTC0, with 2 simple formats.
|
||||
#[test]
|
||||
fn test_simple_system_time() {
|
||||
unsafe { std::env::set_var("TZ", "UTC0") };
|
||||
|
||||
let time = UNIX_EPOCH;
|
||||
let mut out = Vec::new();
|
||||
format_system_time(&mut out, time, "%Y-%m-%d %H:%M", false).expect("Formatting error.");
|
||||
assert_eq!(String::from_utf8(out).unwrap(), "1970-01-01 00:00");
|
||||
|
||||
let mut out = Vec::new();
|
||||
format_system_time(&mut out, time, "%Y-%m-%d %H:%M:%S.%N %z", false)
|
||||
.expect("Formatting error.");
|
||||
assert_eq!(
|
||||
String::from_utf8(out).unwrap(),
|
||||
"1970-01-01 00:00:00.000000000 +0000"
|
||||
);
|
||||
}
|
||||
|
||||
// Test that very large (positive or negative) lead to just the timestamp being printed.
|
||||
#[test]
|
||||
fn test_large_system_time() {
|
||||
let time = UNIX_EPOCH + Duration::from_secs(67_768_036_191_763_200);
|
||||
let mut out = Vec::new();
|
||||
format_system_time(&mut out, time, "%Y-%m-%d %H:%M", false).expect("Formatting error.");
|
||||
assert_eq!(String::from_utf8(out).unwrap(), "67768036191763200");
|
||||
|
||||
let time = UNIX_EPOCH - Duration::from_secs(67_768_040_922_076_800);
|
||||
let mut out = Vec::new();
|
||||
format_system_time(&mut out, time, "%Y-%m-%d %H:%M", false).expect("Formatting error.");
|
||||
assert_eq!(String::from_utf8(out).unwrap(), "-67768040922076800");
|
||||
}
|
||||
}
|
||||
|
|
@ -65,6 +65,8 @@ pub use crate::features::ranges;
|
|||
pub use crate::features::ringbuffer;
|
||||
#[cfg(feature = "sum")]
|
||||
pub use crate::features::sum;
|
||||
#[cfg(feature = "time")]
|
||||
pub use crate::features::time;
|
||||
#[cfg(feature = "update-control")]
|
||||
pub use crate::features::update_control;
|
||||
#[cfg(feature = "uptime")]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue