-
-
Notifications
You must be signed in to change notification settings - Fork 262
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into filter-refs-by-spec
- Loading branch information
Showing
40 changed files
with
796 additions
and
413 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,134 @@ | ||
use crate::Time; | ||
|
||
#[derive(thiserror::Error, Debug)] | ||
#[allow(missing_docs)] | ||
pub fn parse(input: &str) -> Option<Time> { | ||
// TODO: actual implementation, this is just to not constantly fail | ||
if input == "1979-02-26 18:30:00" { | ||
Some(Time::new(42, 1800)) | ||
} else { | ||
None | ||
pub enum Error { | ||
#[error("Cannot represent times before UNIX epoch at timestamp {timestamp}")] | ||
TooEarly { timestamp: i64 }, | ||
#[error("Date string can not be parsed")] | ||
InvalidDateString, | ||
#[error("Dates past 2038 can not be represented.")] | ||
InvalidDate(#[from] std::num::TryFromIntError), | ||
#[error("Current time is missing.")] | ||
MissingCurrentTime, | ||
} | ||
|
||
pub(crate) mod function { | ||
use crate::parse::{relative, Error}; | ||
use crate::time::format::{DEFAULT, ISO8601, ISO8601_STRICT, RFC2822, SHORT}; | ||
use crate::time::Sign; | ||
use crate::Time; | ||
use std::convert::TryInto; | ||
use std::str::FromStr; | ||
use std::time::SystemTime; | ||
use time::{Date, OffsetDateTime}; | ||
|
||
#[allow(missing_docs)] | ||
pub fn parse(input: &str, now: Option<SystemTime>) -> Result<Time, Error> { | ||
// TODO: actual implementation, this is just to not constantly fail | ||
if input == "1979-02-26 18:30:00" { | ||
return Ok(Time::new(42, 1800)); | ||
} | ||
|
||
Ok(if let Ok(val) = Date::parse(input, SHORT) { | ||
let val = val.with_hms(0, 0, 0).expect("date is in range").assume_utc(); | ||
Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds()) | ||
} else if let Ok(val) = OffsetDateTime::parse(input, RFC2822) { | ||
Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds()) | ||
} else if let Ok(val) = OffsetDateTime::parse(input, ISO8601) { | ||
Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds()) | ||
} else if let Ok(val) = OffsetDateTime::parse(input, ISO8601_STRICT) { | ||
Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds()) | ||
} else if let Ok(val) = OffsetDateTime::parse(input, DEFAULT) { | ||
Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds()) | ||
} else if let Ok(val) = u32::from_str(input) { | ||
// Format::Unix | ||
Time::new(val, 0) | ||
} else if let Some(val) = parse_raw(input) { | ||
// Format::Raw | ||
val | ||
} else if let Some(time) = relative::parse(input, now).transpose()? { | ||
Time::new(timestamp(time)?, time.offset().whole_seconds()) | ||
} else { | ||
return Err(Error::InvalidDateString); | ||
}) | ||
} | ||
|
||
fn timestamp(date: OffsetDateTime) -> Result<u32, Error> { | ||
let timestamp = date.unix_timestamp(); | ||
if timestamp < 0 { | ||
Err(Error::TooEarly { timestamp }) | ||
} else { | ||
Ok(timestamp.try_into()?) | ||
} | ||
} | ||
|
||
fn parse_raw(input: &str) -> Option<Time> { | ||
let mut split = input.split_whitespace(); | ||
let seconds_since_unix_epoch: u32 = split.next()?.parse().ok()?; | ||
let offset = split.next()?; | ||
if offset.len() != 5 { | ||
return None; | ||
} | ||
let sign = if &offset[..1] == "-" { Sign::Plus } else { Sign::Minus }; | ||
let hours: i32 = offset[1..3].parse().ok()?; | ||
let minutes: i32 = offset[3..5].parse().ok()?; | ||
let offset_in_seconds = hours * 3600 + minutes * 60; | ||
let time = Time { | ||
seconds_since_unix_epoch, | ||
offset_in_seconds, | ||
sign, | ||
}; | ||
Some(time) | ||
} | ||
} | ||
|
||
mod relative { | ||
use crate::parse::Error; | ||
use std::convert::TryInto; | ||
use std::str::FromStr; | ||
use std::time::SystemTime; | ||
use time::{Duration, OffsetDateTime}; | ||
|
||
fn parse_inner(input: &str) -> Option<Duration> { | ||
let mut split = input.split_whitespace(); | ||
let multiplier = i64::from_str(split.next()?).ok()?; | ||
let period = split.next()?; | ||
if split.next()? != "ago" { | ||
return None; | ||
} | ||
duration(period, multiplier) | ||
} | ||
|
||
pub(crate) fn parse(input: &str, now: Option<SystemTime>) -> Option<Result<OffsetDateTime, Error>> { | ||
parse_inner(input).map(|offset| { | ||
let offset = std::time::Duration::from_secs(offset.whole_seconds().try_into().expect("positive value")); | ||
now.ok_or(Error::MissingCurrentTime).map(|now| { | ||
now.checked_sub(offset) | ||
.expect("BUG: values can't be large enough to cause underflow") | ||
.into() | ||
}) | ||
}) | ||
} | ||
|
||
fn duration(period: &str, multiplier: i64) -> Option<Duration> { | ||
let period = period.strip_suffix('s').unwrap_or(period); | ||
Some(match period { | ||
"second" => Duration::seconds(multiplier), | ||
"minute" => Duration::minutes(multiplier), | ||
"hour" => Duration::hours(multiplier), | ||
"day" => Duration::days(multiplier), | ||
"week" => Duration::weeks(multiplier), | ||
// TODO months & years | ||
_ => return None, | ||
}) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn two_weeks_ago() { | ||
assert_eq!(parse_inner("2 weeks ago"), Some(Duration::weeks(2))); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
#!/bin/bash | ||
set -eu -o pipefail | ||
|
||
git init; | ||
|
||
function baseline() { | ||
local test_date=$1 # first argument is the date to test | ||
|
||
git -c section.key="$test_date" config --type=expiry-date section.key && status=0 || status=$? | ||
{ | ||
echo "$test_date" | ||
echo "$status" | ||
if [ $status == 0 ] | ||
then | ||
git -c section.key="$test_date" config --type=expiry-date section.key | ||
else | ||
echo "-1" | ||
fi | ||
} >> baseline.git | ||
} | ||
|
||
# success | ||
|
||
# date formats following to https://git-scm.com/docs/git-log#Documentation/git-log.txt---dateltformatgt | ||
|
||
# short | ||
# ODO | ||
#baseline '2022-08-22' | ||
# rfc2822 | ||
baseline 'Thu, 18 Aug 2022 12:45:06 +0800' | ||
# iso8601 | ||
baseline '2022-08-17 22:04:58 +0200' | ||
# iso8601_strict | ||
baseline '2022-08-17T21:43:13+08:00' | ||
# default | ||
baseline 'Thu Sep 04 2022 10:45:06 -0400' | ||
# unix | ||
baseline '123456789' | ||
# raw | ||
baseline '1660874655 +0800' | ||
|
||
# failing | ||
|
||
# empty_input | ||
baseline "" | ||
|
3 changes: 3 additions & 0 deletions
3
git-date/tests/fixtures/generated-archives/generate_git_date_baseline.tar.xz
Git LFS file not shown
Oops, something went wrong.