Skip to content

Commit

Permalink
Implement [unix_timestamp] component
Browse files Browse the repository at this point in the history
  • Loading branch information
jhpratt committed Feb 24, 2023
1 parent d07b394 commit 8dca98c
Showing 15 changed files with 406 additions and 40 deletions.
29 changes: 29 additions & 0 deletions tests/formatting.rs
Original file line number Diff line number Diff line change
@@ -738,3 +738,32 @@ fn ignore() -> time::Result<()> {

Ok(())
}

#[test]
fn unix_timestamp() -> time::Result<()> {
let dt = datetime!(2009-02-13 23:31:30.123456789 UTC);

assert_eq!(dt.format(&fd!("[unix_timestamp]"))?, "1234567890");
assert_eq!(
dt.format(&fd!("[unix_timestamp sign:mandatory]"))?,
"+1234567890"
);
assert_eq!(
dt.format(&fd!("[unix_timestamp precision:millisecond]"))?,
"1234567890123"
);
assert_eq!(
dt.format(&fd!("[unix_timestamp precision:microsecond]"))?,
"1234567890123456"
);
assert_eq!(
dt.format(&fd!("[unix_timestamp precision:nanosecond]"))?,
"1234567890123456789"
);
assert_eq!(
datetime!(1969-12-31 23:59:59 UTC).format(&fd!("[unix_timestamp]"))?,
"-1"
);

Ok(())
}
21 changes: 18 additions & 3 deletions tests/macros.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use time::format_description::modifier::{
MonthRepr, Padding, WeekNumberRepr, WeekdayRepr, YearRepr,
};
use core::num::NonZeroU16;

use time::format_description::modifier::*;
use time::format_description::{Component, FormatItem};
use time::macros::{date, format_description, time};
use time::{Date, Time};
@@ -294,6 +294,21 @@ fn format_description_coverage() {
format_description!("[[ "),
&[FormatItem::Literal(b"["), FormatItem::Literal(b" ")]
);
assert_eq!(
format_description!("[ignore count:2]"),
&[FormatItem::Component(Component::Ignore(Ignore::count(
NonZeroU16::new(2).unwrap()
)))]
);
assert_eq!(
format_description!("[unix_timestamp precision:nanosecond sign:mandatory]"),
&[FormatItem::Component(Component::UnixTimestamp(modifier!(
UnixTimestamp {
precision: UnixTimestampPrecision::Nanosecond,
sign_is_mandatory: true,
}
)))]
);
}

#[test]
4 changes: 2 additions & 2 deletions tests/meta.rs
Original file line number Diff line number Diff line change
@@ -70,7 +70,7 @@ fn alignment() {
assert_alignment!(iso8601::FormattedComponents, 1);
assert_alignment!(iso8601::OffsetPrecision, 1);
assert_alignment!(iso8601::TimePrecision, 1);
assert_alignment!(Parsed, 4);
assert_alignment!(Parsed, 8);
assert_alignment!(Month, 1);
assert_alignment!(Weekday, 1);
assert_alignment!(Error, 8);
@@ -147,7 +147,7 @@ fn size() {
assert_size!(iso8601::FormattedComponents, 1, 1);
assert_size!(iso8601::OffsetPrecision, 1, 1);
assert_size!(iso8601::TimePrecision, 2, 2);
assert_size!(Parsed, 32, 32);
assert_size!(Parsed, 56, 56);
assert_size!(Month, 1, 1);
assert_size!(Weekday, 1, 1);
// assert_size!(Error, 56, 56);
52 changes: 42 additions & 10 deletions tests/parse_format_description.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use core::num::NonZeroU16;

mod iterator {
use time::format_description::modifier::{
MonthRepr, Padding, SubsecondDigits, WeekNumberRepr, WeekdayRepr, YearRepr,
};
use time::error::InvalidFormatDescription;
use time::format_description::modifier::*;
use time::format_description::{self, Component, FormatItem, OwnedFormatItem};

mod iterator {
use super::*;

pub(super) fn padding() -> impl Iterator<Item = (Padding, &'static str)> {
@@ -128,13 +128,18 @@ mod iterator {
)
})
}
}

use time::error::InvalidFormatDescription;
use time::format_description::modifier::{
Ignore, MonthRepr, Padding, SubsecondDigits, WeekNumberRepr, WeekdayRepr, YearRepr,
};
use time::format_description::{self, Component, FormatItem, OwnedFormatItem};
pub(super) fn unix_timestamp_precision()
-> impl Iterator<Item = (UnixTimestampPrecision, &'static str)> {
[
(UnixTimestampPrecision::Second, "precision:second"),
(UnixTimestampPrecision::Millisecond, "precision:millisecond"),
(UnixTimestampPrecision::Microsecond, "precision:microsecond"),
(UnixTimestampPrecision::Nanosecond, "precision:nanosecond"),
]
.into_iter()
}
}

#[test]
fn empty() {
@@ -268,6 +273,15 @@ fn simple_component() {
})
))])
);
assert_eq!(
format_description::parse("[unix_timestamp]"),
Ok(vec![FormatItem::Component(Component::UnixTimestamp(
modifier!(UnixTimestamp {
precision: UnixTimestampPrecision::Second,
sign_is_mandatory: false,
})
))])
);
assert_eq!(
format_description::parse("[weekday]"),
Ok(vec![FormatItem::Component(Component::Weekday(modifier!(
@@ -469,6 +483,24 @@ fn component_with_modifiers() {
}
}

for (sign_is_mandatory, sign_is_mandatory_str) in iterator::sign_is_mandatory() {
for (unix_timestamp_precision, unix_timestamp_precision_str) in
iterator::unix_timestamp_precision()
{
assert_eq!(
format_description::parse(&format!(
"[unix_timestamp {sign_is_mandatory_str} {unix_timestamp_precision_str}]"
)),
Ok(vec![FormatItem::Component(Component::UnixTimestamp(
modifier!(UnixTimestamp {
sign_is_mandatory,
precision: unix_timestamp_precision
})
))])
);
}
}

for (digits, digits_str) in iterator::subsecond_digits() {
assert_eq!(
format_description::parse(&format!("[subsecond {digits_str}]")),
107 changes: 107 additions & 0 deletions tests/parsing.rs
Original file line number Diff line number Diff line change
@@ -1302,6 +1302,46 @@ fn parse_components() -> time::Result<()> {
result,
Err(error::ParseFromDescription::InvalidComponent("ignore"))
));
parse_component!(
Component::UnixTimestamp(modifier!(UnixTimestamp {
precision: modifier::UnixTimestampPrecision::Second,
sign_is_mandatory: false,
})),
b"1234567890",
_.unix_timestamp_nanos() == Some(1_234_567_890_000_000_000)
);
parse_component!(
Component::UnixTimestamp(modifier!(UnixTimestamp {
precision: modifier::UnixTimestampPrecision::Millisecond,
sign_is_mandatory: false,
})),
b"1234567890123",
_.unix_timestamp_nanos() == Some(1_234_567_890_123_000_000)
);
parse_component!(
Component::UnixTimestamp(modifier!(UnixTimestamp {
precision: modifier::UnixTimestampPrecision::Microsecond,
sign_is_mandatory: false,
})),
b"1234567890123456",
_.unix_timestamp_nanos() == Some(1_234_567_890_123_456_000)
);
parse_component!(
Component::UnixTimestamp(modifier!(UnixTimestamp {
precision: modifier::UnixTimestampPrecision::Nanosecond,
sign_is_mandatory: false,
})),
b"1234567890123456789",
_.unix_timestamp_nanos() == Some(1_234_567_890_123_456_789)
);
parse_component!(
Component::UnixTimestamp(modifier!(UnixTimestamp {
precision: modifier::UnixTimestampPrecision::Nanosecond,
sign_is_mandatory: false,
})),
b"-1234567890123456789",
_.unix_timestamp_nanos() == Some(-1_234_567_890_123_456_789)
);

Ok(())
}
@@ -1454,3 +1494,70 @@ fn parse_first() -> time::Result<()> {

Ok(())
}

#[test]
fn parse_unix_timestamp() -> time::Result<()> {
assert_eq!(
OffsetDateTime::parse("1234567890", &fd::parse("[unix_timestamp]")?)?,
datetime!(2009-02-13 23:31:30 UTC)
);
assert_eq!(
OffsetDateTime::parse(
"1234567890123",
&fd::parse("[unix_timestamp precision:millisecond]")?
)?,
datetime!(2009-02-13 23:31:30.123 UTC)
);
assert_eq!(
OffsetDateTime::parse(
"1234567890123456",
&fd::parse("[unix_timestamp precision:microsecond]")?
)?,
datetime!(2009-02-13 23:31:30.123456 UTC)
);
assert_eq!(
OffsetDateTime::parse(
"1234567890123456789",
&fd::parse("[unix_timestamp precision:nanosecond]")?
)?,
datetime!(2009-02-13 23:31:30.123456789 UTC)
);

Ok(())
}

#[test]
fn parse_unix_timestamp_err() -> time::Result<()> {
assert_eq!(
OffsetDateTime::parse("1234567890", &fd::parse("[unix_timestamp sign:mandatory]")?),
Err(error::Parse::ParseFromDescription(
error::ParseFromDescription::InvalidComponent("unix_timestamp")
))
);
assert_eq!(
OffsetDateTime::parse("a", &fd::parse("[unix_timestamp precision:second]")?),
Err(error::Parse::ParseFromDescription(
error::ParseFromDescription::InvalidComponent("unix_timestamp")
))
);
assert_eq!(
OffsetDateTime::parse("a", &fd::parse("[unix_timestamp precision:millisecond]")?),
Err(error::Parse::ParseFromDescription(
error::ParseFromDescription::InvalidComponent("unix_timestamp")
))
);
assert_eq!(
OffsetDateTime::parse("a", &fd::parse("[unix_timestamp precision:microsecond]")?),
Err(error::Parse::ParseFromDescription(
error::ParseFromDescription::InvalidComponent("unix_timestamp")
))
);
assert_eq!(
OffsetDateTime::parse("a", &fd::parse("[unix_timestamp precision:nanosecond]")?),
Err(error::Parse::ParseFromDescription(
error::ParseFromDescription::InvalidComponent("unix_timestamp")
))
);

Ok(())
}
12 changes: 12 additions & 0 deletions time-macros/src/format_description/format_item.rs
Original file line number Diff line number Diff line change
@@ -251,6 +251,10 @@ component_definition! {
Subsecond = "subsecond" {
digits = "digits": Option<SubsecondDigits> => digits,
},
UnixTimestamp = "unix_timestamp" {
precision = "precision": Option<UnixTimestampPrecision> => precision,
sign_behavior = "sign": Option<SignBehavior> => sign_is_mandatory,
},
Weekday = "weekday" {
repr = "repr": Option<WeekdayRepr> => repr,
one_indexed = "one_indexed": Option<WeekdayOneIndexed> => one_indexed,
@@ -381,6 +385,14 @@ modifier! {
OneOrMore = b"1+",
}

enum UnixTimestampPrecision {
#[default]
Second = b"second",
Millisecond = b"millisecond",
Microsecond = b"microsecond",
Nanosecond = b"nanosecond",
}

enum WeekNumberRepr {
#[default]
Iso = b"iso",
1 change: 1 addition & 0 deletions time-macros/src/format_description/public/component.rs
Original file line number Diff line number Diff line change
@@ -45,4 +45,5 @@ declare_component! {
OffsetMinute
OffsetSecond
Ignore
UnixTimestamp
}
16 changes: 16 additions & 0 deletions time-macros/src/format_description/public/modifier.rs
Original file line number Diff line number Diff line change
@@ -229,3 +229,19 @@ impl ToTokenTree for Ignore {
}}
}
}

to_tokens! {
pub(crate) enum UnixTimestampPrecision {
Second,
Millisecond,
Microsecond,
Nanosecond,
}
}

to_tokens! {
pub(crate) struct UnixTimestamp {
pub(crate) precision: UnixTimestampPrecision,
pub(crate) sign_is_mandatory: bool,
}
}
2 changes: 2 additions & 0 deletions time/src/format_description/component.rs
Original file line number Diff line number Diff line change
@@ -36,4 +36,6 @@ pub enum Component {
OffsetSecond(modifier::OffsetSecond),
/// A number of bytes to ignore when parsing. This has no effect on formatting.
Ignore(modifier::Ignore),
/// A Unix timestamp.
UnixTimestamp(modifier::UnixTimestamp),
}
33 changes: 33 additions & 0 deletions time/src/format_description/modifier.rs
Original file line number Diff line number Diff line change
@@ -255,6 +255,30 @@ impl Ignore {
}
}

/// The precision of a Unix timestamp.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnixTimestampPrecision {
/// Seconds since the Unix epoch.
Second,
/// Milliseconds since the Unix epoch.
Millisecond,
/// Microseconds since the Unix epoch.
Microsecond,
/// Nanoseconds since the Unix epoch.
Nanosecond,
}

/// A Unix timestamp.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UnixTimestamp {
/// The precision of the timestamp.
pub precision: UnixTimestampPrecision,
/// Whether the `+` sign must be present for a non-negative timestamp.
pub sign_is_mandatory: bool,
}

/// Generate the provided code if and only if `pub` is present.
macro_rules! if_pub {
(pub $(#[$attr:meta])*; $($x:tt)*) => {
@@ -373,4 +397,13 @@ impl_const_default! {
@pub OffsetSecond => Self { padding: Padding::Zero };
/// Creates a modifier that indicates the value is [padded with zeroes](Self::Zero).
Padding => Self::Zero;
/// Creates a modifier that indicates the value represents the [number of seconds](Self::Second)
/// since the Unix epoch.
UnixTimestampPrecision => Self::Second;
/// Creates a modifier that indicates the value represents the [number of
/// seconds](UnixTimestampPrecision::Second) since the Unix epoch. The sign is not mandatory.
@pub UnixTimestamp => Self {
precision: UnixTimestampPrecision::Second,
sign_is_mandatory: false,
};
}
Loading

0 comments on commit 8dca98c

Please # to comment.