mirror of
https://github.com/chronotope/chrono.git
synced 2025-10-02 07:21:41 +00:00
GNU coreutils date
-like time zone formatting (#759)
Co-authored-by: Eric Sheppard <key2@eric.sheppard.cloud>
This commit is contained in:
parent
a383abf30e
commit
5f5410163b
@ -49,6 +49,7 @@ Versions with only mechanical changes will be omitted from the following list.
|
||||
* Add compatibility with rfc2822 comments (#733)
|
||||
* Make `js-sys` and `wasm-bindgen` enabled by default when target is `wasm32-unknown-unknown` for ease of API discovery
|
||||
* Add the `Months` struct and associated `Add` and `Sub` impls
|
||||
* Add `GNU` `coreutils` `date`-like time zone formatting
|
||||
|
||||
## 0.4.19
|
||||
|
||||
@ -774,4 +775,3 @@ and replaced by 0.2.25 very shortly. Duh.)
|
||||
## 0.1.0 (2014-11-20)
|
||||
|
||||
The initial version that was available to `crates.io`.
|
||||
|
||||
|
@ -224,6 +224,18 @@ pub enum Fixed {
|
||||
/// The offset is limited from `-24:00` to `+24:00`,
|
||||
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
||||
TimezoneOffsetColon,
|
||||
/// Offset from the local time to UTC with seconds (`+09:00:00` or `-04:00:00` or `+00:00:00`).
|
||||
///
|
||||
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace.
|
||||
/// The offset is limited from `-24:00:00` to `+24:00:00`,
|
||||
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
||||
TimezoneOffsetDoubleColon,
|
||||
/// Offset from the local time to UTC without minutes (`+09` or `-04` or `+00`).
|
||||
///
|
||||
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace.
|
||||
/// The offset is limited from `-24` to `+24`,
|
||||
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
||||
TimezoneOffsetTripleColon,
|
||||
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`).
|
||||
///
|
||||
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace,
|
||||
@ -274,6 +286,15 @@ enum InternalInternal {
|
||||
Nanosecond9NoDot,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
enum Colons {
|
||||
None,
|
||||
Single,
|
||||
Double,
|
||||
Triple,
|
||||
}
|
||||
|
||||
/// A single formatting item. This is used for both formatting and parsing.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Item<'a> {
|
||||
@ -557,16 +578,33 @@ fn format_inner<'a>(
|
||||
result: &mut String,
|
||||
off: FixedOffset,
|
||||
allow_zulu: bool,
|
||||
use_colon: bool,
|
||||
colon_type: Colons,
|
||||
) -> fmt::Result {
|
||||
let off = off.local_minus_utc();
|
||||
if !allow_zulu || off != 0 {
|
||||
let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) };
|
||||
if use_colon {
|
||||
write!(result, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60)
|
||||
} else {
|
||||
|
||||
match colon_type {
|
||||
Colons::None => {
|
||||
write!(result, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60)
|
||||
}
|
||||
Colons::Single => {
|
||||
write!(result, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60)
|
||||
}
|
||||
Colons::Double => {
|
||||
write!(
|
||||
result,
|
||||
"{}{:02}:{:02}:{:02}",
|
||||
sign,
|
||||
off / 3600,
|
||||
off / 60 % 60,
|
||||
off % 60
|
||||
)
|
||||
}
|
||||
Colons::Triple => {
|
||||
write!(result, "{}{:02}", sign, off / 3600)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result.push('Z');
|
||||
Ok(())
|
||||
@ -650,17 +688,19 @@ fn format_inner<'a>(
|
||||
result.push_str(name);
|
||||
Ok(())
|
||||
}),
|
||||
TimezoneOffsetColon => {
|
||||
off.map(|&(_, off)| write_local_minus_utc(result, off, false, true))
|
||||
}
|
||||
TimezoneOffsetColonZ => {
|
||||
off.map(|&(_, off)| write_local_minus_utc(result, off, true, true))
|
||||
}
|
||||
TimezoneOffsetColon => off
|
||||
.map(|&(_, off)| write_local_minus_utc(result, off, false, Colons::Single)),
|
||||
TimezoneOffsetDoubleColon => off
|
||||
.map(|&(_, off)| write_local_minus_utc(result, off, false, Colons::Double)),
|
||||
TimezoneOffsetTripleColon => off
|
||||
.map(|&(_, off)| write_local_minus_utc(result, off, false, Colons::Triple)),
|
||||
TimezoneOffsetColonZ => off
|
||||
.map(|&(_, off)| write_local_minus_utc(result, off, true, Colons::Single)),
|
||||
TimezoneOffset => {
|
||||
off.map(|&(_, off)| write_local_minus_utc(result, off, false, false))
|
||||
off.map(|&(_, off)| write_local_minus_utc(result, off, false, Colons::None))
|
||||
}
|
||||
TimezoneOffsetZ => {
|
||||
off.map(|&(_, off)| write_local_minus_utc(result, off, true, false))
|
||||
off.map(|&(_, off)| write_local_minus_utc(result, off, true, Colons::None))
|
||||
}
|
||||
Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) => {
|
||||
panic!("Do not try to write %#z it is undefined")
|
||||
@ -681,7 +721,7 @@ fn format_inner<'a>(
|
||||
t.minute(),
|
||||
sec
|
||||
)?;
|
||||
Some(write_local_minus_utc(result, off, false, false))
|
||||
Some(write_local_minus_utc(result, off, false, Colons::None))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -693,7 +733,7 @@ fn format_inner<'a>(
|
||||
// reuse `Debug` impls which already print ISO 8601 format.
|
||||
// this is faster in this way.
|
||||
write!(result, "{:?}T{:?}", d, t)?;
|
||||
Some(write_local_minus_utc(result, off, false, true))
|
||||
Some(write_local_minus_utc(result, off, false, Colons::Single))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -420,7 +420,10 @@ where
|
||||
try_consume!(scan::timezone_name_skip(s));
|
||||
}
|
||||
|
||||
&TimezoneOffsetColon | &TimezoneOffset => {
|
||||
&TimezoneOffsetColon
|
||||
| &TimezoneOffsetDoubleColon
|
||||
| &TimezoneOffsetTripleColon
|
||||
| &TimezoneOffset => {
|
||||
let offset = try_consume!(scan::timezone_offset(
|
||||
s.trim_left(),
|
||||
scan::colon_or_space
|
||||
|
@ -71,6 +71,8 @@ The following specifiers are available both to formatting and parsing.
|
||||
| `%Z` | `ACST` | Local time zone name. Skips all non-whitespace characters during parsing. [^8] |
|
||||
| `%z` | `+0930` | Offset from the local time to UTC (with UTC being `+0000`). |
|
||||
| `%:z` | `+09:30` | Same as `%z` but with a colon. |
|
||||
|`%::z`|`+09:30:00`| Offset from the local time to UTC with seconds. |
|
||||
|`%:::z`| `+09` | Offset from the local time to UTC without minutes. |
|
||||
| `%#z` | `+09` | *Parsing only:* Same as `%z` but allows minutes to be missing or present. |
|
||||
| | | |
|
||||
| | | **DATE & TIME SPECIFIERS:** |
|
||||
@ -404,10 +406,20 @@ impl<'a> Iterator for StrftimeItems<'a> {
|
||||
}
|
||||
}
|
||||
'+' => fix!(RFC3339),
|
||||
':' => match next!() {
|
||||
'z' => fix!(TimezoneOffsetColon),
|
||||
_ => Item::Error,
|
||||
},
|
||||
':' => {
|
||||
if self.remainder.starts_with("::z") {
|
||||
self.remainder = &self.remainder[3..];
|
||||
fix!(TimezoneOffsetTripleColon)
|
||||
} else if self.remainder.starts_with(":z") {
|
||||
self.remainder = &self.remainder[2..];
|
||||
fix!(TimezoneOffsetDoubleColon)
|
||||
} else if self.remainder.starts_with('z') {
|
||||
self.remainder = &self.remainder[1..];
|
||||
fix!(TimezoneOffsetColon)
|
||||
} else {
|
||||
Item::Error
|
||||
}
|
||||
}
|
||||
'.' => match next!() {
|
||||
'3' => match next!() {
|
||||
'f' => fix!(Nanosecond3),
|
||||
@ -596,6 +608,8 @@ fn test_strftime_docs() {
|
||||
//assert_eq!(dt.format("%Z").to_string(), "ACST");
|
||||
assert_eq!(dt.format("%z").to_string(), "+0930");
|
||||
assert_eq!(dt.format("%:z").to_string(), "+09:30");
|
||||
assert_eq!(dt.format("%::z").to_string(), "+09:30:00");
|
||||
assert_eq!(dt.format("%:::z").to_string(), "+09");
|
||||
|
||||
// date & time specifiers
|
||||
assert_eq!(dt.format("%c").to_string(), "Sun Jul 8 00:34:60 2001");
|
||||
|
Loading…
x
Reference in New Issue
Block a user