mirror of
https://github.com/starship/starship.git
synced 2026-06-19 02:02:00 +07:00
feat(time): improve timezone handling by switching to jiff (#7222)
This commit is contained in:
Generated
+1
-44
@@ -393,19 +393,6 @@ dependencies = [
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.6.1"
|
||||
@@ -538,12 +525,6 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
@@ -1892,30 +1873,6 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.3.0"
|
||||
@@ -3380,7 +3337,6 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
name = "starship"
|
||||
version = "1.25.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"clap_complete_nushell",
|
||||
@@ -3391,6 +3347,7 @@ dependencies = [
|
||||
"guess_host_triple",
|
||||
"home",
|
||||
"indexmap",
|
||||
"jiff",
|
||||
"jsonc-parser",
|
||||
"log",
|
||||
"mockall",
|
||||
|
||||
+1
-1
@@ -35,7 +35,6 @@ config-schema = ["schemars"]
|
||||
notify = ["notify-rust"]
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.44", default-features = false, features = ["clock", "std", "wasmbind"] }
|
||||
clap = { version = "4.6.1", features = ["derive", "cargo", "unicode"] }
|
||||
clap_complete = "4.6.5"
|
||||
clap_complete_nushell = "4.6.0"
|
||||
@@ -44,6 +43,7 @@ dunce = "1.0.5"
|
||||
# default feature restriction addresses https://github.com/starship/starship/issues/4251
|
||||
gix = { version = "0.84.0", default-features = false, features = ["max-performance-safe", "revision", "zlib-rs", "status", "sha1"] }
|
||||
indexmap = { version = "2.14.0", features = ["serde"] }
|
||||
jiff = { version = "0.2.24", features = ["serde"] }
|
||||
jsonc-parser = { version = "0.32.4", features = ["serde"] }
|
||||
log = { version = "0.4.30", features = ["std"] }
|
||||
# notify-rust is optional (on by default) because the crate doesn't currently build for darwin with nix
|
||||
|
||||
+23
-10
@@ -4814,7 +4814,7 @@ format = 'via [$symbol$workspace]($style) '
|
||||
## Time
|
||||
|
||||
The `time` module shows the current **local** time.
|
||||
The `format` configuration value is used by the [`chrono`](https://crates.io/crates/chrono) crate to control how the time is displayed. Take a look [at the chrono strftime docs](https://docs.rs/chrono/0.4.7/chrono/format/strftime/index.html) to see what options are available.
|
||||
The `format` configuration value is used by the [`jiff`](https://crates.io/crates/jiff) crate to control how the time is displayed. Take a look [at the jiff strftime docs](https://docs.rs/jiff/latest/jiff/fmt/strtime/index.html) to see what options are available.
|
||||
|
||||
> [!TIP]
|
||||
> This module is disabled by default.
|
||||
@@ -4822,15 +4822,15 @@ The `format` configuration value is used by the [`chrono`](https://crates.io/cra
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Default | Description |
|
||||
| ----------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
||||
| `format` | `'at [$time]($style) '` | The format string for the module. |
|
||||
| `use_12hr` | `false` | Enables 12 hour formatting |
|
||||
| `time_format` | see below | The [chrono format string](https://docs.rs/chrono/0.4.7/chrono/format/strftime/index.html) used to format the time. |
|
||||
| `style` | `'bold yellow'` | The style for the module time |
|
||||
| `utc_time_offset` | `'local'` | Sets the UTC offset to use. Range from -24 < x < 24. Allows floats to accommodate 30/45 minute timezone offsets. |
|
||||
| `disabled` | `true` | Disables the `time` module. |
|
||||
| `time_range` | `'-'` | Sets the time range during which the module will be shown. Times must be specified in 24-hours format |
|
||||
| Option | Default | Description |
|
||||
| ----------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `format` | `'at [$time]($style) '` | The format string for the module. |
|
||||
| `use_12hr` | `false` | Enables 12 hour formatting |
|
||||
| `time_format` | see below | The [jiff format string](https://docs.rs/jiff/latest/jiff/fmt/strtime/index.html) used to format the time. |
|
||||
| `style` | `'bold yellow'` | The style for the module time |
|
||||
| `utc_time_offset` | `'local'` | Sets the UTC offset to use. Either an IANA time zone name or a range from -24 < x < 24. Allows floats to accommodate 30/45 minute timezone offsets. |
|
||||
| `disabled` | `true` | Disables the `time` module. |
|
||||
| `time_range` | `'-'` | Sets the time range during which the module will be shown. Times must be specified in 24-hours format |
|
||||
|
||||
If `use_12hr` is `true`, then `time_format` defaults to `'%r'`. Otherwise, it defaults to `'%T'`.
|
||||
Manually setting `time_format` will override the `use_12hr` setting.
|
||||
@@ -4846,6 +4846,8 @@ Manually setting `time_format` will override the `use_12hr` setting.
|
||||
|
||||
### Example
|
||||
|
||||
#### With UTC offset
|
||||
|
||||
```toml
|
||||
# ~/.config/starship.toml
|
||||
|
||||
@@ -4857,6 +4859,17 @@ utc_time_offset = '-5'
|
||||
time_range = '10:00:00-14:00:00'
|
||||
```
|
||||
|
||||
#### With Timezone name
|
||||
|
||||
```toml
|
||||
# ~/.config/starship.toml
|
||||
|
||||
[time]
|
||||
disabled = false
|
||||
time_format = '%T'
|
||||
utc_time_offset = 'Europe/Berlin'
|
||||
```
|
||||
|
||||
## Typst
|
||||
|
||||
The `typst` module shows the current installed version of Typst used in a project.
|
||||
|
||||
+11
-2
@@ -1,5 +1,13 @@
|
||||
use crate::config::Either;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// Wrapper struct to enable serde serialization/deserialization for jiff::tz::TimeZone
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct TimezoneWrapper(
|
||||
#[serde(with = "jiff::fmt::serde::tz::required")] pub jiff::tz::TimeZone,
|
||||
);
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
#[cfg_attr(
|
||||
feature = "config-schema",
|
||||
@@ -14,7 +22,8 @@ pub struct TimeConfig<'a> {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub time_format: Option<&'a str>,
|
||||
pub disabled: bool,
|
||||
pub utc_time_offset: &'a str,
|
||||
#[cfg_attr(feature = "config-schema", schemars(with = "String"))]
|
||||
pub utc_time_offset: Either<TimezoneWrapper, &'a str>,
|
||||
pub time_range: &'a str,
|
||||
}
|
||||
|
||||
@@ -26,7 +35,7 @@ impl Default for TimeConfig<'_> {
|
||||
use_12hr: false,
|
||||
time_format: None,
|
||||
disabled: true,
|
||||
utc_time_offset: "local",
|
||||
utc_time_offset: Either::Second("local"),
|
||||
time_range: "-",
|
||||
}
|
||||
}
|
||||
|
||||
+20
-48
@@ -3,8 +3,8 @@ use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use chrono::DateTime;
|
||||
use ini::Ini;
|
||||
use jiff::{Timestamp, Zoned};
|
||||
use serde_json as json;
|
||||
use sha1::{Digest, Sha1};
|
||||
|
||||
@@ -149,7 +149,7 @@ fn get_credentials_duration(
|
||||
.find_map(|env_var| context.get_env(env_var))
|
||||
{
|
||||
// get expiration from environment variables
|
||||
chrono::DateTime::parse_from_rfc3339(&expiration_date).ok()
|
||||
expiration_date.parse::<Timestamp>().ok()
|
||||
} else if let Some(section) =
|
||||
get_creds(context, aws_creds).and_then(|creds| get_profile_creds(creds, aws_profile))
|
||||
{
|
||||
@@ -158,7 +158,7 @@ fn get_credentials_duration(
|
||||
expiration_keys
|
||||
.iter()
|
||||
.find_map(|expiration_key| section.get(expiration_key))
|
||||
.and_then(|expiration| DateTime::parse_from_rfc3339(expiration).ok())
|
||||
.and_then(|expiration| expiration.parse::<Timestamp>().ok())
|
||||
} else {
|
||||
// get expiration from cached SSO credentials
|
||||
let config = get_config(context, aws_config)?;
|
||||
@@ -172,10 +172,10 @@ fn get_credentials_duration(
|
||||
let sso_cred_json: json::Value =
|
||||
json::from_str(&crate::utils::read_file(&sso_cred_path).ok()?).ok()?;
|
||||
let expires_at = sso_cred_json.get("expiresAt")?.as_str();
|
||||
DateTime::parse_from_rfc3339(expires_at?).ok()
|
||||
expires_at?.parse::<Timestamp>().ok()
|
||||
}?;
|
||||
|
||||
Some(expiration_date.timestamp() - chrono::Local::now().timestamp())
|
||||
Some(expiration_date.as_second() - Zoned::now().timestamp().as_second())
|
||||
}
|
||||
|
||||
fn alias_name(name: Option<String>, aliases: &HashMap<String, &str>) -> Option<String> {
|
||||
@@ -332,6 +332,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test::ModuleRenderer;
|
||||
use nu_ansi_term::Color;
|
||||
use std::fs::{File, create_dir};
|
||||
@@ -736,21 +737,16 @@ credential_process = /opt/bin/awscreds-retriever
|
||||
|
||||
#[test]
|
||||
fn expiration_date_set() {
|
||||
use chrono::{DateTime, SecondsFormat, Utc};
|
||||
|
||||
let expiration_env_vars = ["AWS_SESSION_EXPIRATION", "AWS_CREDENTIAL_EXPIRATION"];
|
||||
for env_var in expiration_env_vars {
|
||||
let now_plus_half_hour: DateTime<Utc> =
|
||||
DateTime::from_timestamp(chrono::Local::now().timestamp() + 1800, 0).unwrap();
|
||||
let now_plus_half_hour =
|
||||
Timestamp::from_second(Zoned::now().timestamp().as_second() + 1800).unwrap();
|
||||
|
||||
let actual = ModuleRenderer::new("aws")
|
||||
.env("AWS_PROFILE", "astronauts")
|
||||
.env("AWS_REGION", "ap-northeast-2")
|
||||
.env("AWS_ACCESS_KEY_ID", "dummy")
|
||||
.env(
|
||||
env_var,
|
||||
now_plus_half_hour.to_rfc3339_opts(SecondsFormat::Secs, true),
|
||||
)
|
||||
.env(env_var, now_plus_half_hour.to_string())
|
||||
.collect();
|
||||
|
||||
let possible_values = [
|
||||
@@ -772,16 +768,14 @@ credential_process = /opt/bin/awscreds-retriever
|
||||
|
||||
#[test]
|
||||
fn expiration_date_set_from_file() -> io::Result<()> {
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
let dir = tempfile::tempdir()?;
|
||||
let credentials_path = dir.path().join("credentials");
|
||||
let mut file = File::create(&credentials_path)?;
|
||||
|
||||
let now_plus_half_hour: DateTime<Utc> =
|
||||
DateTime::from_timestamp(chrono::Local::now().timestamp() + 1800, 0).unwrap();
|
||||
let now_plus_half_hour =
|
||||
Timestamp::from_second(Zoned::now().timestamp().as_second() + 1800).unwrap();
|
||||
|
||||
let expiration_date = now_plus_half_hour.to_rfc3339_opts(chrono::SecondsFormat::Secs, true);
|
||||
let expiration_date = now_plus_half_hour.to_string();
|
||||
|
||||
let expiration_keys = ["expiration", "x_security_token_expires"];
|
||||
for key in expiration_keys {
|
||||
@@ -847,10 +841,7 @@ aws_secret_access_key=dummy
|
||||
|
||||
#[test]
|
||||
fn expiration_date_set_expired() {
|
||||
use chrono::{DateTime, SecondsFormat, Utc};
|
||||
|
||||
let now: DateTime<Utc> =
|
||||
DateTime::from_timestamp(chrono::Local::now().timestamp() - 1800, 0).unwrap();
|
||||
let now = Timestamp::from_second(Zoned::now().timestamp().as_second() - 1800).unwrap();
|
||||
|
||||
let symbol = "!!!";
|
||||
|
||||
@@ -862,10 +853,7 @@ aws_secret_access_key=dummy
|
||||
.env("AWS_PROFILE", "astronauts")
|
||||
.env("AWS_REGION", "ap-northeast-2")
|
||||
.env("AWS_ACCESS_KEY_ID", "dummy")
|
||||
.env(
|
||||
"AWS_SESSION_EXPIRATION",
|
||||
now.to_rfc3339_opts(SecondsFormat::Secs, true),
|
||||
)
|
||||
.env("AWS_SESSION_EXPIRATION", now.to_string())
|
||||
.collect();
|
||||
let expected = Some(format!(
|
||||
"on {}",
|
||||
@@ -1056,8 +1044,6 @@ credential_process = /opt/bin/awscreds-for-tests
|
||||
|
||||
#[test]
|
||||
fn sso_legacy_set() -> io::Result<()> {
|
||||
use chrono::{DateTime, SecondsFormat, Utc};
|
||||
|
||||
let (module_renderer, dir) = ModuleRenderer::new_with_home("aws")?;
|
||||
std::fs::create_dir_all(dir.path().join(".aws/sso/cache"))?;
|
||||
|
||||
@@ -1080,16 +1066,10 @@ sso_role_name = <AWS-ROLE-NAME>
|
||||
.join(".aws/sso/cache/a47a4e57aecc96b31b4f083543924bd6f828e65a.json"),
|
||||
)?;
|
||||
|
||||
let one_second_ago: DateTime<Utc> =
|
||||
DateTime::from_timestamp(chrono::Local::now().timestamp() - 1, 0).unwrap();
|
||||
let one_second_ago =
|
||||
Timestamp::from_second(Zoned::now().timestamp().as_second() - 1).unwrap();
|
||||
|
||||
file.write_all(
|
||||
format!(
|
||||
r#"{{"expiresAt": "{}"}}"#,
|
||||
one_second_ago.to_rfc3339_opts(SecondsFormat::Secs, true)
|
||||
)
|
||||
.as_bytes(),
|
||||
)?;
|
||||
file.write_all(format!(r#"{{"expiresAt": "{one_second_ago}"}}"#).as_bytes())?;
|
||||
file.sync_all()?;
|
||||
|
||||
let actual = module_renderer.collect();
|
||||
@@ -1104,8 +1084,6 @@ sso_role_name = <AWS-ROLE-NAME>
|
||||
|
||||
#[test]
|
||||
fn sso_set() -> io::Result<()> {
|
||||
use chrono::{DateTime, SecondsFormat, Utc};
|
||||
|
||||
let (module_renderer, dir) = ModuleRenderer::new_with_home("aws")?;
|
||||
std::fs::create_dir_all(dir.path().join(".aws/sso/cache"))?;
|
||||
|
||||
@@ -1134,16 +1112,10 @@ sso_registration_scopes = sso:account:access
|
||||
.join(".aws/sso/cache/7505d64a54e061b7acd54ccd58b49dc43500b635.json"),
|
||||
)?;
|
||||
|
||||
let one_second_ago: DateTime<Utc> =
|
||||
DateTime::from_timestamp(chrono::Local::now().timestamp() - 1, 0).unwrap();
|
||||
let one_second_ago =
|
||||
Timestamp::from_second(Zoned::now().timestamp().as_second() - 1).unwrap();
|
||||
|
||||
cache_file.write_all(
|
||||
format!(
|
||||
r#"{{"expiresAt": "{}"}}"#,
|
||||
one_second_ago.to_rfc3339_opts(SecondsFormat::Secs, true)
|
||||
)
|
||||
.as_bytes(),
|
||||
)?;
|
||||
cache_file.write_all(format!(r#"{{"expiresAt": "{one_second_ago}"}}"#).as_bytes())?;
|
||||
cache_file.sync_all()?;
|
||||
|
||||
let actual = module_renderer
|
||||
|
||||
+258
-111
@@ -1,7 +1,12 @@
|
||||
use chrono::{DateTime, FixedOffset, Local, NaiveTime, Utc};
|
||||
use jiff::{
|
||||
Timestamp, Zoned,
|
||||
civil::Time,
|
||||
tz::{Offset, TimeZone},
|
||||
};
|
||||
|
||||
use super::{Context, Module, ModuleConfig};
|
||||
use crate::configs::time::TimeConfig;
|
||||
use crate::config::Either;
|
||||
use crate::configs::time::{TimeConfig, TimezoneWrapper};
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Outputs the current time
|
||||
@@ -17,7 +22,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
|
||||
// Hide prompt if current time is not inside time_range
|
||||
let (display_start, display_end) = parse_time_range(config.time_range);
|
||||
let time_now = Local::now().time();
|
||||
let time_now = Zoned::now().time();
|
||||
if !is_inside_time_range(time_now, display_start, display_end) {
|
||||
return None;
|
||||
}
|
||||
@@ -27,17 +32,26 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
|
||||
log::trace!("Timer module is enabled with format string: {time_format}");
|
||||
|
||||
let formatted_time_string = if config.utc_time_offset != "local" {
|
||||
create_offset_time_string(Utc::now(), config.utc_time_offset, time_format).unwrap_or_else(
|
||||
|_| {
|
||||
log::warn!(
|
||||
"Invalid utc_time_offset configuration provided! Falling back to \"local\"."
|
||||
);
|
||||
format_time(time_format, Local::now())
|
||||
},
|
||||
)
|
||||
} else {
|
||||
format_time(time_format, Local::now())
|
||||
let formatted_time_string = match &config.utc_time_offset {
|
||||
Either::First(TimezoneWrapper(tz)) => {
|
||||
// Use IANA timezone name
|
||||
let target_time = Timestamp::now().to_zoned(tz.clone());
|
||||
format_time_fixed_offset(time_format, target_time)
|
||||
}
|
||||
Either::Second("local") => {
|
||||
// Use local timezone
|
||||
format_time(time_format, Zoned::now())
|
||||
}
|
||||
Either::Second(utc_time_offset) => {
|
||||
// Use numeric offset
|
||||
create_offset_time_string(Timestamp::now(), utc_time_offset, time_format)
|
||||
.unwrap_or_else(|_| {
|
||||
log::warn!(
|
||||
"Invalid utc_time_offset configuration provided! Falling back to \"local\"."
|
||||
);
|
||||
format_time(time_format, Zoned::now())
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
@@ -65,10 +79,10 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
}
|
||||
|
||||
fn create_offset_time_string(
|
||||
utc_time: DateTime<Utc>,
|
||||
utc_time: Timestamp,
|
||||
utc_time_offset_str: &str,
|
||||
time_format: &str,
|
||||
) -> Result<String, &'static str> {
|
||||
) -> Result<String, String> {
|
||||
// Using floats to allow 30/45 minute offsets: https://www.timeanddate.com/time/time-zones-interesting.html
|
||||
let utc_time_offset_in_hours = utc_time_offset_str.parse::<f32>().unwrap_or(
|
||||
// Passing out of range value to force falling back to "local"
|
||||
@@ -76,38 +90,34 @@ fn create_offset_time_string(
|
||||
);
|
||||
if utc_time_offset_in_hours < 24_f32 && utc_time_offset_in_hours > -24_f32 {
|
||||
let utc_offset_in_seconds: i32 = (utc_time_offset_in_hours * 3600_f32) as i32;
|
||||
let Some(timezone_offset) = FixedOffset::east_opt(utc_offset_in_seconds) else {
|
||||
return Err("Invalid offset");
|
||||
};
|
||||
let timezone_offset = Offset::from_seconds(utc_offset_in_seconds)
|
||||
.map_err(|err| format!("Invalid timezone offset: {err:?}"))?;
|
||||
let tz = TimeZone::fixed(timezone_offset);
|
||||
log::trace!("Target timezone offset is {timezone_offset}");
|
||||
|
||||
let target_time = utc_time.with_timezone(&timezone_offset);
|
||||
let target_time = utc_time.to_zoned(tz);
|
||||
log::trace!("Time in target timezone now is {target_time}");
|
||||
|
||||
Ok(format_time_fixed_offset(time_format, target_time))
|
||||
} else {
|
||||
Err("Invalid timezone offset.")
|
||||
Err("Invalid timezone offset.".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// Format a given time into the given string. This function should be referentially
|
||||
/// transparent, which makes it easy to test (unlike anything involving the actual time)
|
||||
fn format_time(time_format: &str, local_time: DateTime<Local>) -> String {
|
||||
local_time.format(time_format).to_string()
|
||||
fn format_time(time_format: &str, local_time: Zoned) -> String {
|
||||
local_time.strftime(time_format).to_string()
|
||||
}
|
||||
|
||||
fn format_time_fixed_offset(time_format: &str, utc_time: DateTime<FixedOffset>) -> String {
|
||||
utc_time.format(time_format).to_string()
|
||||
fn format_time_fixed_offset(time_format: &str, zoned_time: Zoned) -> String {
|
||||
zoned_time.strftime(time_format).to_string()
|
||||
}
|
||||
|
||||
/// Returns true if `time_now` is between `time_start` and `time_end`.
|
||||
/// If one of these values is not given, then it is ignored.
|
||||
/// It also handles cases where `time_start` and `time_end` have a midnight in between
|
||||
fn is_inside_time_range(
|
||||
time_now: NaiveTime,
|
||||
time_start: Option<NaiveTime>,
|
||||
time_end: Option<NaiveTime>,
|
||||
) -> bool {
|
||||
fn is_inside_time_range(time_now: Time, time_start: Option<Time>, time_end: Option<Time>) -> bool {
|
||||
match (time_start, time_end) {
|
||||
(None, None) => true,
|
||||
(Some(i), None) => time_now > i,
|
||||
@@ -127,7 +137,7 @@ fn is_inside_time_range(
|
||||
///
|
||||
/// If one of the ranges is invalid or not provided, then the corresponding field in the output
|
||||
/// tuple is None
|
||||
fn parse_time_range(time_range: &str) -> (Option<NaiveTime>, Option<NaiveTime>) {
|
||||
fn parse_time_range(time_range: &str) -> (Option<Time>, Option<Time>) {
|
||||
let value = String::from(time_range);
|
||||
|
||||
// Check if there is exactly one hyphen, and fail otherwise
|
||||
@@ -140,8 +150,8 @@ fn parse_time_range(time_range: &str) -> (Option<NaiveTime>, Option<NaiveTime>)
|
||||
let end = &end[1..];
|
||||
|
||||
// Parse the ranges
|
||||
let start_time = NaiveTime::parse_from_str(start, "%H:%M:%S").ok();
|
||||
let end_time = NaiveTime::parse_from_str(end, "%H:%M:%S").ok();
|
||||
let start_time = start.parse::<Time>().ok();
|
||||
let end_time = end.parse::<Time>().ok();
|
||||
|
||||
(start_time, end_time)
|
||||
}
|
||||
@@ -152,140 +162,151 @@ tests become extra important */
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test::ModuleRenderer;
|
||||
use chrono::offset::TimeZone;
|
||||
use jiff::civil::date;
|
||||
|
||||
const FMT_12: &str = "%r";
|
||||
const FMT_24: &str = "%T";
|
||||
|
||||
#[test]
|
||||
fn test_midnight_12hr() {
|
||||
let time = Local.with_ymd_and_hms(2014, 7, 8, 0, 0, 0).unwrap();
|
||||
let time = date(2014, 7, 8)
|
||||
.at(0, 0, 0, 0)
|
||||
.to_zoned(TimeZone::system())
|
||||
.unwrap();
|
||||
let formatted = format_time(FMT_12, time);
|
||||
assert_eq!(formatted, "12:00:00 AM");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_midnight_24hr() {
|
||||
let time = Local.with_ymd_and_hms(2014, 7, 8, 0, 0, 0).unwrap();
|
||||
let time = date(2014, 7, 8)
|
||||
.at(0, 0, 0, 0)
|
||||
.to_zoned(TimeZone::system())
|
||||
.unwrap();
|
||||
let formatted = format_time(FMT_24, time);
|
||||
assert_eq!(formatted, "00:00:00");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_noon_12hr() {
|
||||
let time = Local.with_ymd_and_hms(2014, 7, 8, 12, 0, 0).unwrap();
|
||||
let time = date(2014, 7, 8)
|
||||
.at(12, 0, 0, 0)
|
||||
.to_zoned(TimeZone::system())
|
||||
.unwrap();
|
||||
let formatted = format_time(FMT_12, time);
|
||||
assert_eq!(formatted, "12:00:00 PM");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_noon_24hr() {
|
||||
let time = Local.with_ymd_and_hms(2014, 7, 8, 12, 0, 0).unwrap();
|
||||
let time = date(2014, 7, 8)
|
||||
.at(12, 0, 0, 0)
|
||||
.to_zoned(TimeZone::system())
|
||||
.unwrap();
|
||||
let formatted = format_time(FMT_24, time);
|
||||
assert_eq!(formatted, "12:00:00");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arbtime_12hr() {
|
||||
let time = Local.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
||||
let time = date(2014, 7, 8)
|
||||
.at(15, 36, 47, 0)
|
||||
.to_zoned(TimeZone::system())
|
||||
.unwrap();
|
||||
let formatted = format_time(FMT_12, time);
|
||||
assert_eq!(formatted, "03:36:47 PM");
|
||||
assert_eq!(formatted, "3:36:47 PM");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arbtime_24hr() {
|
||||
let time = Local.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
||||
let time = date(2014, 7, 8)
|
||||
.at(15, 36, 47, 0)
|
||||
.to_zoned(TimeZone::system())
|
||||
.unwrap();
|
||||
let formatted = format_time(FMT_24, time);
|
||||
assert_eq!(formatted, "15:36:47");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_with_paren() {
|
||||
let time = Local.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
||||
let time = date(2014, 7, 8)
|
||||
.at(15, 36, 47, 0)
|
||||
.to_zoned(TimeZone::system())
|
||||
.unwrap();
|
||||
let formatted = format_time("[%T]", time);
|
||||
assert_eq!(formatted, "[15:36:47]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_midnight_12hr_fixed_offset() {
|
||||
let timezone_offset = FixedOffset::east_opt(0).unwrap();
|
||||
let time = Utc
|
||||
.with_ymd_and_hms(2014, 7, 8, 0, 0, 0)
|
||||
.unwrap()
|
||||
.with_timezone(&timezone_offset);
|
||||
let timezone_offset = Offset::from_seconds(0).unwrap();
|
||||
let tz = TimeZone::fixed(timezone_offset);
|
||||
let time = date(2014, 7, 8).at(0, 0, 0, 0).to_zoned(tz).unwrap();
|
||||
let formatted = format_time_fixed_offset(FMT_12, time);
|
||||
assert_eq!(formatted, "12:00:00 AM");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_midnight_24hr_fixed_offset() {
|
||||
let timezone_offset = FixedOffset::east_opt(0).unwrap();
|
||||
let time = Utc
|
||||
.with_ymd_and_hms(2014, 7, 8, 0, 0, 0)
|
||||
.unwrap()
|
||||
.with_timezone(&timezone_offset);
|
||||
let timezone_offset = Offset::from_seconds(0).unwrap();
|
||||
let tz = TimeZone::fixed(timezone_offset);
|
||||
let time = date(2014, 7, 8).at(0, 0, 0, 0).to_zoned(tz).unwrap();
|
||||
let formatted = format_time_fixed_offset(FMT_24, time);
|
||||
assert_eq!(formatted, "00:00:00");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_noon_12hr_fixed_offset() {
|
||||
let timezone_offset = FixedOffset::east_opt(0).unwrap();
|
||||
let time = Utc
|
||||
.with_ymd_and_hms(2014, 7, 8, 12, 0, 0)
|
||||
.unwrap()
|
||||
.with_timezone(&timezone_offset);
|
||||
let timezone_offset = Offset::from_seconds(0).unwrap();
|
||||
let tz = TimeZone::fixed(timezone_offset);
|
||||
let time = date(2014, 7, 8).at(12, 0, 0, 0).to_zoned(tz).unwrap();
|
||||
let formatted = format_time_fixed_offset(FMT_12, time);
|
||||
assert_eq!(formatted, "12:00:00 PM");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_noon_24hr_fixed_offset() {
|
||||
let timezone_offset = FixedOffset::east_opt(0).unwrap();
|
||||
let time = Utc
|
||||
.with_ymd_and_hms(2014, 7, 8, 12, 0, 0)
|
||||
.unwrap()
|
||||
.with_timezone(&timezone_offset);
|
||||
let timezone_offset = Offset::from_seconds(0).unwrap();
|
||||
let tz = TimeZone::fixed(timezone_offset);
|
||||
let time = date(2014, 7, 8).at(12, 0, 0, 0).to_zoned(tz).unwrap();
|
||||
let formatted = format_time_fixed_offset(FMT_24, time);
|
||||
assert_eq!(formatted, "12:00:00");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arbtime_12hr_fixed_offset() {
|
||||
let timezone_offset = FixedOffset::east_opt(0).unwrap();
|
||||
let time = Utc
|
||||
.with_ymd_and_hms(2014, 7, 8, 15, 36, 47)
|
||||
.unwrap()
|
||||
.with_timezone(&timezone_offset);
|
||||
let timezone_offset = Offset::from_seconds(0).unwrap();
|
||||
let tz = TimeZone::fixed(timezone_offset);
|
||||
let time = date(2014, 7, 8).at(15, 36, 47, 0).to_zoned(tz).unwrap();
|
||||
let formatted = format_time_fixed_offset(FMT_12, time);
|
||||
assert_eq!(formatted, "03:36:47 PM");
|
||||
assert_eq!(formatted, "3:36:47 PM");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arbtime_24hr_fixed_offset() {
|
||||
let timezone_offset = FixedOffset::east_opt(0).unwrap();
|
||||
let time = Utc
|
||||
.with_ymd_and_hms(2014, 7, 8, 15, 36, 47)
|
||||
.unwrap()
|
||||
.with_timezone(&timezone_offset);
|
||||
let timezone_offset = Offset::from_seconds(0).unwrap();
|
||||
let tz = TimeZone::fixed(timezone_offset);
|
||||
let time = date(2014, 7, 8).at(15, 36, 47, 0).to_zoned(tz).unwrap();
|
||||
let formatted = format_time_fixed_offset(FMT_24, time);
|
||||
assert_eq!(formatted, "15:36:47");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_with_paren_fixed_offset() {
|
||||
let timezone_offset = FixedOffset::east_opt(0).unwrap();
|
||||
let time = Utc
|
||||
.with_ymd_and_hms(2014, 7, 8, 15, 36, 47)
|
||||
.unwrap()
|
||||
.with_timezone(&timezone_offset);
|
||||
let timezone_offset = Offset::from_seconds(0).unwrap();
|
||||
let tz = TimeZone::fixed(timezone_offset);
|
||||
let time = date(2014, 7, 8).at(15, 36, 47, 0).to_zoned(tz).unwrap();
|
||||
let formatted = format_time_fixed_offset("[%T]", time);
|
||||
assert_eq!(formatted, "[15:36:47]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_formatted_time_string_with_minus_3() {
|
||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
||||
let utc_time = date(2014, 7, 8)
|
||||
.at(15, 36, 47, 0)
|
||||
.to_zoned(TimeZone::UTC)
|
||||
.unwrap()
|
||||
.timestamp();
|
||||
let utc_time_offset_str = "-3";
|
||||
|
||||
let actual = create_offset_time_string(utc_time, utc_time_offset_str, FMT_12).unwrap();
|
||||
@@ -294,34 +315,50 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_create_formatted_time_string_with_plus_5() {
|
||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
||||
let utc_time = date(2014, 7, 8)
|
||||
.at(15, 36, 47, 0)
|
||||
.to_zoned(TimeZone::UTC)
|
||||
.unwrap()
|
||||
.timestamp();
|
||||
let utc_time_offset_str = "+5";
|
||||
|
||||
let actual = create_offset_time_string(utc_time, utc_time_offset_str, FMT_12).unwrap();
|
||||
assert_eq!(actual, "08:36:47 PM");
|
||||
assert_eq!(actual, "8:36:47 PM");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_formatted_time_string_with_plus_9_30() {
|
||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
||||
let utc_time = date(2014, 7, 8)
|
||||
.at(15, 36, 47, 0)
|
||||
.to_zoned(TimeZone::UTC)
|
||||
.unwrap()
|
||||
.timestamp();
|
||||
let utc_time_offset_str = "+9.5";
|
||||
|
||||
let actual = create_offset_time_string(utc_time, utc_time_offset_str, FMT_12).unwrap();
|
||||
assert_eq!(actual, "01:06:47 AM");
|
||||
assert_eq!(actual, "1:06:47 AM");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_formatted_time_string_with_plus_5_45() {
|
||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
||||
let utc_time = date(2014, 7, 8)
|
||||
.at(15, 36, 47, 0)
|
||||
.to_zoned(TimeZone::UTC)
|
||||
.unwrap()
|
||||
.timestamp();
|
||||
let utc_time_offset_str = "+5.75";
|
||||
|
||||
let actual = create_offset_time_string(utc_time, utc_time_offset_str, FMT_12).unwrap();
|
||||
assert_eq!(actual, "09:21:47 PM");
|
||||
assert_eq!(actual, "9:21:47 PM");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_formatted_time_string_with_plus_24() {
|
||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
||||
let utc_time = date(2014, 7, 8)
|
||||
.at(15, 36, 47, 0)
|
||||
.to_zoned(TimeZone::UTC)
|
||||
.unwrap()
|
||||
.timestamp();
|
||||
let utc_time_offset_str = "+24";
|
||||
|
||||
create_offset_time_string(utc_time, utc_time_offset_str, FMT_12)
|
||||
@@ -330,7 +367,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_create_formatted_time_string_with_minus_24() {
|
||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
||||
let utc_time = date(2014, 7, 8)
|
||||
.at(15, 36, 47, 0)
|
||||
.to_zoned(TimeZone::UTC)
|
||||
.unwrap()
|
||||
.timestamp();
|
||||
let utc_time_offset_str = "-24";
|
||||
|
||||
create_offset_time_string(utc_time, utc_time_offset_str, FMT_12)
|
||||
@@ -339,7 +380,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_create_formatted_time_string_with_plus_9001() {
|
||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
||||
let utc_time = date(2014, 7, 8)
|
||||
.at(15, 36, 47, 0)
|
||||
.to_zoned(TimeZone::UTC)
|
||||
.unwrap()
|
||||
.timestamp();
|
||||
let utc_time_offset_str = "+9001";
|
||||
|
||||
create_offset_time_string(utc_time, utc_time_offset_str, FMT_12)
|
||||
@@ -348,7 +393,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_create_formatted_time_string_with_minus_4242() {
|
||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
||||
let utc_time = date(2014, 7, 8)
|
||||
.at(15, 36, 47, 0)
|
||||
.to_zoned(TimeZone::UTC)
|
||||
.unwrap()
|
||||
.timestamp();
|
||||
let utc_time_offset_str = "-4242";
|
||||
|
||||
create_offset_time_string(utc_time, utc_time_offset_str, FMT_12)
|
||||
@@ -357,7 +406,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_create_formatted_time_string_with_invalid_string() {
|
||||
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2014, 7, 8, 15, 36, 47).unwrap();
|
||||
let utc_time = date(2014, 7, 8)
|
||||
.at(15, 36, 47, 0)
|
||||
.to_zoned(TimeZone::UTC)
|
||||
.unwrap()
|
||||
.timestamp();
|
||||
let utc_time_offset_str = "completely wrong config";
|
||||
|
||||
create_offset_time_string(utc_time, utc_time_offset_str, FMT_12)
|
||||
@@ -379,7 +432,7 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
parse_time_range(time_range),
|
||||
(Some(NaiveTime::from_hms_opt(10, 00, 00).unwrap()), None)
|
||||
(Some(Time::new(10, 0, 0, 0).unwrap()), None)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -389,7 +442,7 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
parse_time_range(time_range),
|
||||
(None, Some(NaiveTime::from_hms_opt(22, 00, 00).unwrap()))
|
||||
(None, Some(Time::new(22, 0, 0, 0).unwrap()))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -400,8 +453,8 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse_time_range(time_range),
|
||||
(
|
||||
Some(NaiveTime::from_hms_opt(10, 00, 00).unwrap()),
|
||||
Some(NaiveTime::from_hms_opt(16, 00, 00).unwrap())
|
||||
Some(Time::new(10, 0, 0, 0).unwrap()),
|
||||
Some(Time::new(16, 0, 0, 0).unwrap())
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -410,16 +463,16 @@ mod tests {
|
||||
fn test_is_inside_time_range_with_no_range() {
|
||||
let time_start = None;
|
||||
let time_end = None;
|
||||
let time_now = NaiveTime::from_hms_opt(10, 00, 00).unwrap();
|
||||
let time_now = Time::new(10, 0, 0, 0).unwrap();
|
||||
|
||||
assert!(is_inside_time_range(time_now, time_start, time_end));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_inside_time_range_with_start_range() {
|
||||
let time_start = Some(NaiveTime::from_hms_opt(10, 00, 00).unwrap());
|
||||
let time_now = NaiveTime::from_hms_opt(12, 00, 00).unwrap();
|
||||
let time_now2 = NaiveTime::from_hms_opt(8, 00, 00).unwrap();
|
||||
let time_start = Some(Time::new(10, 0, 0, 0).unwrap());
|
||||
let time_now = Time::new(12, 0, 0, 0).unwrap();
|
||||
let time_now2 = Time::new(8, 0, 0, 0).unwrap();
|
||||
|
||||
assert!(is_inside_time_range(time_now, time_start, None));
|
||||
assert!(!is_inside_time_range(time_now2, time_start, None));
|
||||
@@ -427,9 +480,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_is_inside_time_range_with_end_range() {
|
||||
let time_end = Some(NaiveTime::from_hms_opt(16, 00, 00).unwrap());
|
||||
let time_now = NaiveTime::from_hms_opt(15, 00, 00).unwrap();
|
||||
let time_now2 = NaiveTime::from_hms_opt(19, 00, 00).unwrap();
|
||||
let time_end = Some(Time::new(16, 0, 0, 0).unwrap());
|
||||
let time_now = Time::new(15, 0, 0, 0).unwrap();
|
||||
let time_now2 = Time::new(19, 0, 0, 0).unwrap();
|
||||
|
||||
assert!(is_inside_time_range(time_now, None, time_end));
|
||||
assert!(!is_inside_time_range(time_now2, None, time_end));
|
||||
@@ -437,11 +490,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_is_inside_time_range_with_complete_range() {
|
||||
let time_start = Some(NaiveTime::from_hms_opt(9, 00, 00).unwrap());
|
||||
let time_end = Some(NaiveTime::from_hms_opt(18, 00, 00).unwrap());
|
||||
let time_now = NaiveTime::from_hms_opt(3, 00, 00).unwrap();
|
||||
let time_now2 = NaiveTime::from_hms_opt(13, 00, 00).unwrap();
|
||||
let time_now3 = NaiveTime::from_hms_opt(20, 00, 00).unwrap();
|
||||
let time_start = Some(Time::new(9, 0, 0, 0).unwrap());
|
||||
let time_end = Some(Time::new(18, 0, 0, 0).unwrap());
|
||||
let time_now = Time::new(3, 0, 0, 0).unwrap();
|
||||
let time_now2 = Time::new(13, 0, 0, 0).unwrap();
|
||||
let time_now3 = Time::new(20, 0, 0, 0).unwrap();
|
||||
|
||||
assert!(!is_inside_time_range(time_now, time_start, time_end));
|
||||
assert!(is_inside_time_range(time_now2, time_start, time_end));
|
||||
@@ -450,11 +503,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_is_inside_time_range_with_complete_range_passing_midnight() {
|
||||
let time_start = Some(NaiveTime::from_hms_opt(19, 00, 00).unwrap());
|
||||
let time_end = Some(NaiveTime::from_hms_opt(12, 00, 00).unwrap());
|
||||
let time_now = NaiveTime::from_hms_opt(3, 00, 00).unwrap();
|
||||
let time_now2 = NaiveTime::from_hms_opt(13, 00, 00).unwrap();
|
||||
let time_now3 = NaiveTime::from_hms_opt(20, 00, 00).unwrap();
|
||||
let time_start = Some(Time::new(19, 0, 0, 0).unwrap());
|
||||
let time_end = Some(Time::new(12, 0, 0, 0).unwrap());
|
||||
let time_now = Time::new(3, 0, 0, 0).unwrap();
|
||||
let time_now2 = Time::new(13, 0, 0, 0).unwrap();
|
||||
let time_now3 = Time::new(20, 0, 0, 0).unwrap();
|
||||
|
||||
assert!(is_inside_time_range(time_now, time_start, time_end));
|
||||
assert!(!is_inside_time_range(time_now2, time_start, time_end));
|
||||
@@ -503,4 +556,98 @@ mod tests {
|
||||
assert!(actual.starts_with(&col_prefix));
|
||||
assert!(actual.ends_with(&col_suffix));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_check_invalid_tz() {
|
||||
let actual = ModuleRenderer::new("time")
|
||||
.config(toml::toml! {
|
||||
[time]
|
||||
disabled = false
|
||||
time_format = "%T"
|
||||
utc_time_offset = "invalid"
|
||||
})
|
||||
.collect();
|
||||
|
||||
assert!(actual.is_some(), "Falls back to local time");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn module_tz() {
|
||||
use nu_ansi_term::Color;
|
||||
|
||||
let actual = ModuleRenderer::new("time")
|
||||
.config(toml::toml! {
|
||||
[time]
|
||||
disabled = false
|
||||
time_format = "%:z"
|
||||
utc_time_offset = "Asia/Kolkata"
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expected = Some(format!("at {} ", Color::Yellow.bold().paint("+05:30")));
|
||||
|
||||
assert_eq!(actual, expected, "Uses timezone");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn module_offset() {
|
||||
use nu_ansi_term::Color;
|
||||
|
||||
let actual = ModuleRenderer::new("time")
|
||||
.config(toml::toml! {
|
||||
[time]
|
||||
disabled = false
|
||||
time_format = "%:z"
|
||||
utc_time_offset = "-1.75"
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expected = Some(format!("at {} ", Color::Yellow.bold().paint("-01:45")));
|
||||
|
||||
assert_eq!(actual, expected, "Uses timezone offset");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn module_tz_abbreviation() {
|
||||
let actual = ModuleRenderer::new("time")
|
||||
.config(toml::toml! {
|
||||
[time]
|
||||
disabled = false
|
||||
time_format = "%Z"
|
||||
utc_time_offset = "America/New_York"
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Should output a timezone abbreviation like "EST" or "EDT"
|
||||
assert!(
|
||||
actual.is_some(),
|
||||
"Timezone abbreviation should be displayed"
|
||||
);
|
||||
let output = actual.unwrap();
|
||||
assert!(
|
||||
output.contains("EST") || output.contains("EDT"),
|
||||
"Should contain timezone abbreviation"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn module_tz_identifier() {
|
||||
use nu_ansi_term::Color;
|
||||
|
||||
let actual = ModuleRenderer::new("time")
|
||||
.config(toml::toml! {
|
||||
[time]
|
||||
disabled = false
|
||||
time_format = "%Q"
|
||||
utc_time_offset = "America/New_York"
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expected = Some(format!(
|
||||
"at {} ",
|
||||
Color::Yellow.bold().paint("America/New_York")
|
||||
));
|
||||
|
||||
assert_eq!(actual, expected, "Uses IANA timezone identifier");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user