Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/items/epoch.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

//! Parse a timestamp item.
//!
//! From the GNU docs:
//!
//! > If you precede a number with ‘@’, it represents an internal timestamp as
Expand All @@ -22,7 +24,7 @@ use winnow::{
ModalResult, Parser,
};

use super::primitive::{dec_uint, s};
use super::primitive::{dec_uint, plus_or_minus, s};

/// Represents a timestamp with nanosecond accuracy.
///
Expand All @@ -43,7 +45,7 @@ impl TryFrom<Timestamp> for jiff::Timestamp {
fn try_from(ts: Timestamp) -> Result<Self, Self::Error> {
jiff::Timestamp::new(
ts.second,
i32::try_from(ts.nanosecond).map_err(|_| "nanosecond value exceeds i32::MAX")?,
i32::try_from(ts.nanosecond).map_err(|_| "nanosecond in timestamp exceeds i32::MAX")?,
)
.map_err(|_| "timestamp value is out of valid range")
}
Expand All @@ -52,7 +54,7 @@ impl TryFrom<Timestamp> for jiff::Timestamp {
/// Parse a timestamp in the form of `@1234567890` or `@-1234567890.12345` or
/// `@1234567890,12345`.
pub(super) fn parse(input: &mut &str) -> ModalResult<Timestamp> {
(s("@"), opt(s(one_of(['-', '+']))), sec_and_nsec)
(s("@"), opt(plus_or_minus), s(sec_and_nsec))
.verify_map(|(_, sign, (sec, nsec))| {
let sec = i64::try_from(sec).ok()?;
let (second, nanosecond) = match (sign, nsec) {
Expand All @@ -74,7 +76,7 @@ pub(super) fn parse(input: &mut &str) -> ModalResult<Timestamp> {
/// (padded with zeros on the right if fewer digits are present). If the second
/// part is omitted, it defaults to 0 nanoseconds.
pub(super) fn sec_and_nsec(input: &mut &str) -> ModalResult<(u64, u32)> {
(s(dec_uint), opt(preceded(one_of(['.', ',']), digit1)))
(dec_uint, opt(preceded(one_of(['.', ',']), digit1)))
.verify_map(|(sec, opt_nsec_str)| match opt_nsec_str {
Some(nsec_str) if nsec_str.len() >= 9 => Some((sec, nsec_str[..9].parse().ok()?)),
Some(nsec_str) => {
Expand Down
8 changes: 8 additions & 0 deletions src/items/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ where
s(':').void().parse_next(input)
}

/// Parse a plus or minus character optionally preceeded by whitespace.
pub(super) fn plus_or_minus<'a, E>(input: &mut &'a str) -> winnow::Result<char, E>
where
E: ParserError<&'a str>,
{
s(alt(('+', '-'))).parse_next(input)
}

/// Create a context error with a reason.
pub(super) fn ctx_err(reason: &'static str) -> ContextError {
let mut err = ContextError::new();
Expand Down
2 changes: 1 addition & 1 deletion src/items/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ pub(super) fn minute(input: &mut &str) -> ModalResult<u8> {
/// Parse a number of seconds in `0..60` and an optional number of nanoseconds
/// (default to 0 if not set).
fn second(input: &mut &str) -> ModalResult<(u8, u32)> {
sec_and_nsec
s(sec_and_nsec)
.verify_map(|(s, ns)| if s < 60 { Some((s as u8, ns)) } else { None })
.parse_next(input)
}
Expand Down
Loading