`FixedOffset` is now the official "fixed offset value" type.
This may sound strange, but the final type for the offset "value" was originally `time::Duration` (returned by `Offset::local_minus_utc`). This caused a lot of problems becaus adding `Duration` fully interacts with leap seconds and `Duration` itself is somewhat deprecated. This commit entirely replaces this role of `Duration` with `FixedOffset`. So if we had `Offset` and `Duration` to represent the "storage" offset type and the offset "value" in the past, we now have `Offset` and `FixedOffset`. Storage-to-value conversion is called to "fix" the offset---an apt term for the type. The list of actual changes: - The time zone offset is now restricted to UTC-23:59:59 through UTC+23:59:59, and no subsecond value is allowed. As described above, `FixedOffset` is now fully used for this purpose. - One can now add and subtract `FixedOffset` to/from timelike values. Replaces a temporary `chrono::offset::add_with_leapsecond` function. Datelike & non-timelike values are never affected by the offset. - UTC and local views to `Date<Tz>` are now identical. We keep relevant methods for the consistency right now. - `chrono::format::format` now receives `FixedOffset` in place of `(Old)Duration`. - `Offset` now has a `fix` method to resolve, or to "fix" the "storage" offset (`Offset`) to the offset "value" (`FixedOffset`). - `FixedOffset::{local_minus_utc, utc_minus_local}` methods are added. They no longer depend on `Duration` as well.
This commit is contained in:
parent
2b5553ee76
commit
7ea1ce5080
67
src/date.rs
67
src/date.rs
|
@ -9,7 +9,7 @@ use std::ops::{Add, Sub};
|
|||
use oldtime::Duration as OldDuration;
|
||||
|
||||
use {Weekday, Datelike};
|
||||
use offset::{TimeZone, Offset};
|
||||
use offset::TimeZone;
|
||||
use offset::utc::UTC;
|
||||
use naive;
|
||||
use naive::date::NaiveDate;
|
||||
|
@ -256,9 +256,13 @@ impl<Tz: TimeZone> Date<Tz> {
|
|||
}
|
||||
|
||||
/// Returns a view to the naive local date.
|
||||
///
|
||||
/// This is technically same to [`naive_utc`](#method.naive_utc)
|
||||
/// because the offset is restricted to never exceed one day,
|
||||
/// but provided for the consistency.
|
||||
#[inline]
|
||||
pub fn naive_local(&self) -> NaiveDate {
|
||||
self.date + self.offset.local_minus_utc()
|
||||
self.date
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -398,62 +402,3 @@ impl<Tz: TimeZone> fmt::Display for Date<Tz> where Tz::Offset: fmt::Display {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fmt;
|
||||
use oldtime::Duration;
|
||||
|
||||
use Datelike;
|
||||
use naive::date::NaiveDate;
|
||||
use naive::datetime::NaiveDateTime;
|
||||
use offset::{TimeZone, Offset, LocalResult};
|
||||
use offset::local::Local;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
struct UTC1y; // same to UTC but with an offset of 365 days
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
struct OneYear;
|
||||
|
||||
impl TimeZone for UTC1y {
|
||||
type Offset = OneYear;
|
||||
|
||||
fn from_offset(_offset: &OneYear) -> UTC1y { UTC1y }
|
||||
|
||||
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<OneYear> {
|
||||
LocalResult::Single(OneYear)
|
||||
}
|
||||
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<OneYear> {
|
||||
LocalResult::Single(OneYear)
|
||||
}
|
||||
|
||||
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> OneYear { OneYear }
|
||||
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> OneYear { OneYear }
|
||||
}
|
||||
|
||||
impl Offset for OneYear {
|
||||
fn local_minus_utc(&self) -> Duration { Duration::days(365) }
|
||||
}
|
||||
|
||||
impl fmt::Debug for OneYear {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "+8760:00") }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_weird_offset() {
|
||||
assert_eq!(format!("{:?}", UTC1y.ymd(2012, 2, 29)),
|
||||
"2012-02-29+8760:00".to_string());
|
||||
assert_eq!(format!("{:?}", UTC1y.ymd(2012, 2, 29).and_hms(5, 6, 7)),
|
||||
"2012-02-29T05:06:07+8760:00".to_string());
|
||||
assert_eq!(format!("{:?}", UTC1y.ymd(2012, 3, 4)),
|
||||
"2012-03-04+8760:00".to_string());
|
||||
assert_eq!(format!("{:?}", UTC1y.ymd(2012, 3, 4).and_hms(5, 6, 7)),
|
||||
"2012-03-04T05:06:07+8760:00".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_date_sanity_check() { // issue #27
|
||||
assert_eq!(Local.ymd(2999, 12, 28).day(), 28);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::ops::{Add, Sub};
|
|||
use oldtime::Duration as OldDuration;
|
||||
|
||||
use {Weekday, Timelike, Datelike};
|
||||
use offset::{TimeZone, Offset, add_with_leapsecond};
|
||||
use offset::{TimeZone, Offset};
|
||||
use offset::utc::UTC;
|
||||
use offset::local::Local;
|
||||
use offset::fixed::FixedOffset;
|
||||
|
@ -59,7 +59,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
|||
/// Unlike `date`, this is not associated to the time zone.
|
||||
#[inline]
|
||||
pub fn time(&self) -> NaiveTime {
|
||||
add_with_leapsecond(&self.datetime.time(), &self.offset.local_minus_utc())
|
||||
self.datetime.time() + self.offset.fix()
|
||||
}
|
||||
|
||||
/// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC
|
||||
|
@ -152,7 +152,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
|||
/// Returns a view to the naive local datetime.
|
||||
#[inline]
|
||||
pub fn naive_local(&self) -> NaiveDateTime {
|
||||
add_with_leapsecond(&self.datetime, &self.offset.local_minus_utc())
|
||||
self.datetime + self.offset.fix()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
|
||||
use std::fmt;
|
||||
use std::error::Error;
|
||||
use oldtime::Duration as OldDuration;
|
||||
|
||||
use {Datelike, Timelike};
|
||||
use div::{div_floor, mod_floor};
|
||||
use offset::{Offset, add_with_leapsecond};
|
||||
use offset::Offset;
|
||||
use offset::fixed::FixedOffset;
|
||||
use naive::date::NaiveDate;
|
||||
use naive::time::NaiveTime;
|
||||
|
||||
|
@ -252,7 +252,7 @@ const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat);
|
|||
/// Tries to format given arguments with given formatting items.
|
||||
/// Internally used by `DelayedFormat`.
|
||||
pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Option<&NaiveTime>,
|
||||
off: Option<&(String, OldDuration)>, items: I) -> fmt::Result
|
||||
off: Option<&(String, FixedOffset)>, items: I) -> fmt::Result
|
||||
where I: Iterator<Item=Item<'a>> {
|
||||
// full and abbreviated month and weekday names
|
||||
static SHORT_MONTHS: [&'static str; 12] =
|
||||
|
@ -302,7 +302,7 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
|
|||
(Some(d), Some(t), None) =>
|
||||
Some(d.and_time(*t).timestamp()),
|
||||
(Some(d), Some(t), Some(&(_, off))) =>
|
||||
Some(add_with_leapsecond(&d.and_time(*t), &-off).timestamp()),
|
||||
Some((d.and_time(*t) - off).timestamp()),
|
||||
(_, _, _) => None
|
||||
}),
|
||||
};
|
||||
|
@ -332,15 +332,15 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
|
|||
|
||||
/// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`.
|
||||
/// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true.
|
||||
fn write_local_minus_utc(w: &mut fmt::Formatter, off: OldDuration,
|
||||
fn write_local_minus_utc(w: &mut fmt::Formatter, off: FixedOffset,
|
||||
allow_zulu: bool, use_colon: bool) -> fmt::Result {
|
||||
let off = off.num_minutes();
|
||||
let off = off.local_minus_utc();
|
||||
if !allow_zulu || off != 0 {
|
||||
let (sign, off) = if off < 0 {('-', -off)} else {('+', off)};
|
||||
if use_colon {
|
||||
write!(w, "{}{:02}:{:02}", sign, off / 60, off % 60)
|
||||
write!(w, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60)
|
||||
} else {
|
||||
write!(w, "{}{:02}{:02}", sign, off / 60, off % 60)
|
||||
write!(w, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60)
|
||||
}
|
||||
} else {
|
||||
write!(w, "Z")
|
||||
|
@ -452,7 +452,7 @@ pub struct DelayedFormat<I> {
|
|||
/// The time view, if any.
|
||||
time: Option<NaiveTime>,
|
||||
/// The name and local-to-UTC difference for the offset (timezone), if any.
|
||||
off: Option<(String, OldDuration)>,
|
||||
off: Option<(String, FixedOffset)>,
|
||||
/// An iterator returning formatting items.
|
||||
items: I,
|
||||
}
|
||||
|
@ -467,7 +467,7 @@ impl<'a, I: Iterator<Item=Item<'a>> + Clone> DelayedFormat<I> {
|
|||
pub fn new_with_offset<Off>(date: Option<NaiveDate>, time: Option<NaiveTime>,
|
||||
offset: &Off, items: I) -> DelayedFormat<I>
|
||||
where Off: Offset + fmt::Display {
|
||||
let name_and_diff = (offset.to_string(), offset.local_minus_utc());
|
||||
let name_and_diff = (offset.to_string(), offset.fix());
|
||||
DelayedFormat { date: date, time: time, off: Some(name_and_diff), items: items }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -604,20 +604,13 @@ impl Parsed {
|
|||
let nanosecond = self.nanosecond.unwrap_or(0);
|
||||
let dt = NaiveDateTime::from_timestamp_opt(timestamp, nanosecond);
|
||||
let dt = try!(dt.ok_or(OUT_OF_RANGE));
|
||||
|
||||
// we cannot handle offsets larger than i32 at all. give up if so.
|
||||
// we can instead make `to_naive_datetime_with_offset` to accept i64, but this makes
|
||||
// the algorithm too complex and tons of edge cases. i32 should be enough for all.
|
||||
let offset = tz.offset_from_utc_datetime(&dt).local_minus_utc().num_seconds();
|
||||
guessed_offset = try!(offset.to_i32().ok_or(OUT_OF_RANGE));
|
||||
guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc();
|
||||
}
|
||||
|
||||
// checks if the given `DateTime` has a consistent `Offset` with given `self.offset`.
|
||||
let check_offset = |dt: &DateTime<Tz>| {
|
||||
if let Some(offset) = self.offset {
|
||||
let delta = dt.offset().local_minus_utc().num_seconds();
|
||||
// if `delta` does not fit in `i32`, it cannot equal to `self.offset` anyway.
|
||||
delta.to_i32() == Some(offset)
|
||||
dt.offset().fix().local_minus_utc() == offset
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
|
|
@ -177,7 +177,7 @@
|
|||
//! assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1
|
||||
//!
|
||||
//! // time zone accessor and manipulation
|
||||
//! assert_eq!(dt.offset().local_minus_utc(), Duration::hours(9));
|
||||
//! assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600);
|
||||
//! assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600));
|
||||
//! assert_eq!(dt.with_timezone(&UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
|
||||
//!
|
||||
|
|
|
@ -3,12 +3,16 @@
|
|||
|
||||
//! The time zone which has a fixed offset from UTC.
|
||||
|
||||
use std::ops::{Add, Sub};
|
||||
use std::fmt;
|
||||
use oldtime::Duration as OldDuration;
|
||||
|
||||
use Timelike;
|
||||
use div::div_mod_floor;
|
||||
use naive::time::NaiveTime;
|
||||
use naive::date::NaiveDate;
|
||||
use naive::datetime::NaiveDateTime;
|
||||
use datetime::DateTime;
|
||||
use super::{TimeZone, Offset, LocalResult};
|
||||
|
||||
/// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59.
|
||||
|
@ -82,6 +86,16 @@ impl FixedOffset {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of seconds to add to convert from UTC to the local time.
|
||||
pub fn local_minus_utc(&self) -> i32 {
|
||||
self.local_minus_utc
|
||||
}
|
||||
|
||||
/// Returns the number of seconds to add to convert from the local time to UTC.
|
||||
pub fn utc_minus_local(&self) -> i32 {
|
||||
-self.local_minus_utc
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeZone for FixedOffset {
|
||||
|
@ -101,7 +115,7 @@ impl TimeZone for FixedOffset {
|
|||
}
|
||||
|
||||
impl Offset for FixedOffset {
|
||||
fn local_minus_utc(&self) -> OldDuration { OldDuration::seconds(self.local_minus_utc as i64) }
|
||||
fn fix(&self) -> FixedOffset { *self }
|
||||
}
|
||||
|
||||
impl fmt::Debug for FixedOffset {
|
||||
|
@ -122,3 +136,91 @@ impl fmt::Display for FixedOffset {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) }
|
||||
}
|
||||
|
||||
// addition or subtraction of FixedOffset to/from Timelike values is same to
|
||||
// adding or subtracting the offset's local_minus_utc value
|
||||
// but keep keeps the leap second information.
|
||||
// this should be implemented more efficiently, but for the time being, this is generic right now.
|
||||
|
||||
fn add_with_leapsecond<T>(lhs: &T, rhs: i32) -> T
|
||||
where T: Timelike + Add<OldDuration, Output=T>
|
||||
{
|
||||
// extract and temporarily remove the fractional part and later recover it
|
||||
let nanos = lhs.nanosecond();
|
||||
let lhs = lhs.with_nanosecond(0).unwrap();
|
||||
(lhs + OldDuration::seconds(rhs as i64)).with_nanosecond(nanos).unwrap()
|
||||
}
|
||||
|
||||
impl Add<FixedOffset> for NaiveTime {
|
||||
type Output = NaiveTime;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: FixedOffset) -> NaiveTime {
|
||||
add_with_leapsecond(&self, rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<FixedOffset> for NaiveTime {
|
||||
type Output = NaiveTime;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: FixedOffset) -> NaiveTime {
|
||||
add_with_leapsecond(&self, -rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<FixedOffset> for NaiveDateTime {
|
||||
type Output = NaiveDateTime;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: FixedOffset) -> NaiveDateTime {
|
||||
add_with_leapsecond(&self, rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<FixedOffset> for NaiveDateTime {
|
||||
type Output = NaiveDateTime;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: FixedOffset) -> NaiveDateTime {
|
||||
add_with_leapsecond(&self, -rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Add<FixedOffset> for DateTime<Tz> {
|
||||
type Output = DateTime<Tz>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: FixedOffset) -> DateTime<Tz> {
|
||||
add_with_leapsecond(&self, rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Sub<FixedOffset> for DateTime<Tz> {
|
||||
type Output = DateTime<Tz>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: FixedOffset) -> DateTime<Tz> {
|
||||
add_with_leapsecond(&self, -rhs.local_minus_utc)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use offset::TimeZone;
|
||||
use super::FixedOffset;
|
||||
|
||||
#[test]
|
||||
fn test_date_extreme_offset() {
|
||||
// starting from 0.3 we don't have an offset exceeding one day.
|
||||
// this makes everything easier!
|
||||
assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29)),
|
||||
"2012-02-29+23:59:59".to_string());
|
||||
assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29).and_hms(5, 6, 7)),
|
||||
"2012-02-29T05:06:07+23:59:59".to_string());
|
||||
assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4)),
|
||||
"2012-03-04-23:59:59".to_string());
|
||||
assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4).and_hms(5, 6, 7)),
|
||||
"2012-03-04T05:06:07-23:59:59".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -137,3 +137,15 @@ impl TimeZone for Local {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use Datelike;
|
||||
use offset::TimeZone;
|
||||
use super::Local;
|
||||
|
||||
#[test]
|
||||
fn test_local_date_sanity_check() { // issue #27
|
||||
assert_eq!(Local.ymd(2999, 12, 28).day(), 28);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,31 +21,15 @@
|
|||
*/
|
||||
|
||||
use std::fmt;
|
||||
use std::ops::Add;
|
||||
use oldtime::Duration as OldDuration;
|
||||
|
||||
use Weekday;
|
||||
use Timelike;
|
||||
use naive::date::NaiveDate;
|
||||
use naive::time::NaiveTime;
|
||||
use naive::datetime::NaiveDateTime;
|
||||
use date::Date;
|
||||
use datetime::DateTime;
|
||||
use format::{parse, Parsed, ParseResult, StrftimeItems};
|
||||
|
||||
/// Same to `*lhs + *rhs`, but keeps the leap second information.
|
||||
/// `rhs` should *not* have a fractional second.
|
||||
// TODO this should be replaced by the addition with FixedOffset in 0.3!
|
||||
pub fn add_with_leapsecond<T>(lhs: &T, rhs: &OldDuration) -> T
|
||||
where T: Timelike + Add<OldDuration, Output=T>
|
||||
{
|
||||
debug_assert!(*rhs == OldDuration::seconds(rhs.num_seconds()));
|
||||
|
||||
// extract and temporarily remove the fractional part and later recover it
|
||||
let nanos = lhs.nanosecond();
|
||||
let lhs = lhs.with_nanosecond(0).unwrap();
|
||||
(lhs + *rhs).with_nanosecond(nanos).unwrap()
|
||||
}
|
||||
use self::fixed::FixedOffset;
|
||||
|
||||
/// The conversion result from the local time to the timezone-aware datetime types.
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
|
@ -175,8 +159,8 @@ impl<T: fmt::Debug> LocalResult<T> {
|
|||
|
||||
/// The offset from the local time to UTC.
|
||||
pub trait Offset: Sized + Clone + fmt::Debug {
|
||||
/// Returns the offset from UTC to the local time stored.
|
||||
fn local_minus_utc(&self) -> OldDuration;
|
||||
/// Returns the fixed offset from UTC to the local time stored.
|
||||
fn fix(&self) -> FixedOffset;
|
||||
}
|
||||
|
||||
/// The time zone.
|
||||
|
@ -358,15 +342,15 @@ pub trait TimeZone: Sized + Clone {
|
|||
/// Converts the local `NaiveDate` to the timezone-aware `Date` if possible.
|
||||
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>> {
|
||||
self.offset_from_local_date(local).map(|offset| {
|
||||
Date::from_utc(*local - offset.local_minus_utc(), offset)
|
||||
// since FixedOffset is within +/- 1 day, the date is never affected
|
||||
Date::from_utc(*local, offset)
|
||||
})
|
||||
}
|
||||
|
||||
/// Converts the local `NaiveDateTime` to the timezone-aware `DateTime` if possible.
|
||||
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Self>> {
|
||||
self.offset_from_local_datetime(local).map(|offset| {
|
||||
let utc = add_with_leapsecond(local, &-offset.local_minus_utc());
|
||||
DateTime::from_utc(utc, offset)
|
||||
DateTime::from_utc(*local - offset.fix(), offset)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
/*!
|
||||
* The UTC (Coordinated Universal Time) time zone.
|
||||
*/
|
||||
//! The UTC (Coordinated Universal Time) time zone.
|
||||
|
||||
use std::fmt;
|
||||
use oldtime;
|
||||
use oldtime::Duration as OldDuration;
|
||||
|
||||
use naive::date::NaiveDate;
|
||||
use naive::datetime::NaiveDateTime;
|
||||
use date::Date;
|
||||
use datetime::DateTime;
|
||||
use super::{TimeZone, Offset, LocalResult};
|
||||
use super::fixed::FixedOffset;
|
||||
|
||||
/// The UTC time zone. This is the most efficient time zone when you don't need the local time.
|
||||
/// It is also used as an offset (which is also a dummy type).
|
||||
|
@ -64,7 +62,7 @@ impl TimeZone for UTC {
|
|||
}
|
||||
|
||||
impl Offset for UTC {
|
||||
fn local_minus_utc(&self) -> OldDuration { OldDuration::zero() }
|
||||
fn fix(&self) -> FixedOffset { FixedOffset::east(0) }
|
||||
}
|
||||
|
||||
impl fmt::Debug for UTC {
|
||||
|
|
Loading…
Reference in New Issue