-
-
Notifications
You must be signed in to change notification settings - Fork 318
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
git-date parse draft #498
git-date parse draft #498
Conversation
Hey @Byron can I get early feedback, thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the early feedback request, much appreciated!
The structure of the parse
module can scale to all the sub-parsers you might be writing, nice!
Something that I'd definitely add to the test-suite is a git
baseline for absolute dates. One way I found to trigger git's date parsing is git -c a.d=1979-02-26 config --expiry-date a.d
, which can be used in a fixture script to produce a baseline like here. This file can then be parsed back for quick comparisons like here.
With that you can validate that the date parsed is the same, and if not you could also mark it as such so it's clear we don't expect a perfect match. The reason is that I don't trust git
parses dates correctly at all or handles them correctly, so time
is probably better there.
However, with the hand-implemented parsers, there I would expect a perfect match against git. Ideally, each input tested in git-date
is also run against the git
baseline, and you can setup your test suite to enforce this. Note that it's not viable to call git
each time, the baseline should be cached with a fixture script like in the example.
A lot to digest, I can elaborate if there are questions either here or in the session. Good luck!
git-date/src/parse.rs
Outdated
use crate::Time; | ||
use time::{Date, OffsetDateTime}; | ||
|
||
#[allow(missing_docs)] | ||
pub fn parse(input: &str) -> Option<Time> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should return Result<Time, Error>
with Error
covering typical cases.
git-date/src/parse.rs
Outdated
None | ||
return if let Ok(val) = Date::parse(input, SHORT) { | ||
let val = val.with_hms(0, 0, 0).expect("date is in range").assume_utc(); | ||
Some(Time::new(val.unix_timestamp() as u32, val.offset().whole_seconds())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of casting to u32
this should be try_into()
with conversion into Error
, clearly stating that we can't represent dates past 2038.
git-date/tests/time/parse.rs
Outdated
|
||
#[test] | ||
fn relative() { | ||
let two_weeks_ago = git_date::parse("2 weeks ago").expect("valid time"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am aware that this implementation is not complete, but think every possible input that you know should be working should also be tested here. I can imagine having a loop like for (input, expect) in [(…), (…)] {}
to do that conveniently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Some more notes related to making parse
a pure function)
git-date/src/parse.rs
Outdated
use crate::Time; | ||
use time::{Date, OffsetDateTime}; | ||
|
||
#[allow(missing_docs)] | ||
pub fn parse(input: &str) -> Option<Time> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before I forget, the current time should be passed here. That way tests are deterministic.
git-date/tests/time/parse.rs
Outdated
|
||
#[test] | ||
fn relative() { | ||
let two_weeks_ago = git_date::parse("2 weeks ago").expect("valid time"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests will be easier to write once parse(…)
takes the current time and is 'pure'.
82e3242
to
6c40ac1
Compare
Regrading #498 (comment) I think its local offset. |
Session NotesWays to dig deeper into how date parsing is used:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the updates, I added some notes as well.
); | ||
if *exit_code == 0 { | ||
let actual = res.unwrap().seconds_since_unix_epoch; | ||
let expected = u32::from_str(output.to_str().expect("valid utf")).expect("valid epoch value"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to parse the output while parsing the baseline file, that way it can be used natively here.
git-date/src/parse.rs
Outdated
// TODO: actual implementation, this is just to not constantly fail | ||
if input == "1979-02-26 18:30:00" { | ||
Some(Time::new(42, 1800)) | ||
Ok(Time::new(42, 1800)) | ||
} else { | ||
return if let Ok(val) = Date::parse(input, SHORT) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explicit return
isn't needed here.
Also you can wrap the whole if
expression expression into an Ok
to save on all the Ok
in each branch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to not explicit return, thanks but don't understand "wrap the whole if expression", example may be?
@@ -435,7 +435,7 @@ where | |||
let time = nav | |||
.to_str() | |||
.ok() | |||
.and_then(git_date::parse) | |||
.and_then(|v| git_date::parse(v).ok()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of degenerating the error it should be favorable to put it into .ok_or_else(|| Error::Time { input: nav.into() , source: err})?;
like so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if I understand, and_then
needs to return Option
and git_date::parse
returns Result
@@ -55,8 +58,11 @@ pub fn parse(input: &str) -> Result<Time, Error> { | |||
} else if let Some(val) = parse_raw(input) { | |||
// Format::Raw | |||
Ok(val) | |||
} else if let Some(val) = relative::parse(input) { | |||
Ok(Time::new(val.unix_timestamp() as u32, val.offset().whole_seconds())) | |||
} else if let Some(val) = relative::parse(input, now.ok_or(Error::MissingCurrentTime)?) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would cause unnecessary errors if no time is given but it's not a relative date either. Instead, let the relative::parse()
function return a time offset (it's relative, after all), and if it succeeded and there is no current time, we have to fail. There should be a test for that, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would cause unnecessary errors if no time is given but it's not a relative date either.
See 1eac4de#diff-fe1f4921b662e3ab738c66e74ec7da02aa5ffde9cc94fe9c1a17e90326f54813R74 no error if passing None
Thank you! I hope it's OK to handle the questions during our session, I am so swamped right now. |
I couldn't fix that git-lfs issue, too strange, so I am merging what we have and kindly ask you to move the checkboxes into a new PR :D. Thank you |
This extends `gix_date::parse::relative::span` to recognize times with "months" and "years", as in "3 months ago" and "2 years ago". The actual change here is very simple, because it is directly facilitated by GitoxideLabs#1474. The units "seconds", "minutes", "hours", "days", and "weeks" continue to be recognized (as before). Relative times like "1 year, 2 months ago" remain unrecognized. For background, see 43b6c06 (GitoxideLabs#498), c5c6bf6, GitoxideLabs#1474 (comment), and GitoxideLabs#1696 (comment). Note that this change does not fix issue GitoxideLabs#1696, which continues to apply to the intepretation of "days" and "weeks", and now also applies in the same way to the interpretation of "months" and "years". (It continues not to apply to the interpretation of "seconds", "minutes", and "hours".) The tests are updated to cover "months" and "years", as well as to exercise a wider range of relative times, including showing which units (e.g. "days") are affected by GitoxideLabs#1696. A few sub-cases, of those adjusted or added here, test strings from a related test in Git, to allow comparison. But most are not related to cases there. As before, the tests pass on CI, or when the time zone is otherwise UTC, or with local time zones that never have DST adjustments. The sub-cases are reordered to be monotone increasing in the magnitude of the relative intervals tested (and thus monotone decreasing in the associated absolute timestamps), and the original input is kept zipped into the `actual` and `expected` arrays being compared. This way, if the assertion fails (such as due to GitoxideLabs#1696), it is clear and readable which sub-cases failed and exactly how, as well as what all the sub-cases are and what each sub-case tests. Reordering the sub-cases and preserving the inputs they parse in this way avoids the disadvantages of GitoxideLabs#1697 while keeping, and expanding on, its benefits. Failure, where it occurs, now looks like: --- STDERR: gix-date::date time::parse::relative::various --- thread 'time::parse::relative::various' panicked at gix-date\tests\time\parse.rs:252:9: assertion failed: `(left == right)`: relative times differ Diff < left / right > : [ ( "5 seconds ago", 2024-11-24T23:51:49Z, ), ( "5 minutes ago", 2024-11-24T23:46:54Z, ), ( "5 hours ago", 2024-11-24T18:51:54Z, ), ( "5 days ago", 2024-11-19T23:51:54Z, ), ( "3 weeks ago", 2024-11-03T23:51:54Z, ), ( "21 days ago", 2024-11-03T23:51:54Z, ), ( "504 hours ago", 2024-11-03T23:51:54Z, ), ( "30240 minutes ago", 2024-11-03T23:51:54Z, ), ( "2 months ago", < 2024-09-24T23:51:54Z, > 2024-09-24T22:51:54Z, ), ( "1460 hours ago", 2024-09-25T03:51:54Z, ), ( "87600 minutes ago", 2024-09-25T03:51:54Z, ), ( "14 weeks ago", < 2024-08-18T23:51:54Z, > 2024-08-18T22:51:54Z, ), ( "98 days ago", < 2024-08-18T23:51:54Z, > 2024-08-18T22:51:54Z, ), ( "2352 hours ago", 2024-08-18T23:51:54Z, ), ( "141120 minutes ago", 2024-08-18T23:51:54Z, ), ( "5 months ago", < 2024-06-24T23:51:54Z, > 2024-06-24T22:51:54Z, ), ( "3650 hours ago", 2024-06-25T21:51:54Z, ), ( "219000 minutes ago", 2024-06-25T21:51:54Z, ), ( "26 weeks ago", < 2024-05-26T23:51:54Z, > 2024-05-26T22:51:54Z, ), ( "182 days ago", < 2024-05-26T23:51:54Z, > 2024-05-26T22:51:54Z, ), ( "4368 hours ago", 2024-05-26T23:51:54Z, ), ( "262080 minutes ago", 2024-05-26T23:51:54Z, ), ( "8 months ago", < 2024-03-24T23:51:54Z, > 2024-03-24T22:51:54Z, ), ( "5840 hours ago", 2024-03-26T15:51:54Z, ), ( "350400 minutes ago", 2024-03-26T15:51:54Z, ), ( "38 weeks ago", 2024-03-03T23:51:54Z, ), ( "266 days ago", 2024-03-03T23:51:54Z, ), ( "6384 hours ago", 2024-03-03T23:51:54Z, ), ( "383040 minutes ago", 2024-03-03T23:51:54Z, ), ( "11 months ago", 2023-12-24T23:51:54Z, ), ( "8030 hours ago", 2023-12-26T09:51:54Z, ), ( "481800 minutes ago", 2023-12-26T09:51:54Z, ), ( "14 months ago", < 2023-09-24T23:51:54Z, > 2023-09-24T22:51:54Z, ), ( "21 months ago", 2023-02-24T23:51:54Z, ), ( "2 years ago", 2022-11-24T23:51:54Z, ), ( "20 years ago", 2004-11-24T23:51:54Z, ), ( "630720000 seconds ago", 2004-11-29T23:51:54Z, ), ]
This extends `gix_date::parse::relative::span` to recognize times with "months" and "years", as in "3 months ago" and "2 years ago". Those units are supported by Git. The actual change here to the parsing code is very simple, because it is directly facilitated by GitoxideLabs#1474. The units "seconds", "minutes", "hours", "days", and "weeks" continue to be recognized (as before). Relative times like "1 year, 2 months ago" remain unrecognized. For background, see 43b6c06 (GitoxideLabs#498), c5c6bf6, GitoxideLabs#1474 (comment), and GitoxideLabs#1696 (comment). Note that this specific change does not itself fix issue GitoxideLabs#1696, which applies to the intepretation of "days" and "weeks", and now also applies in the same way to the interpretation of "months" and "years". (It continues not to apply to the interpretation of "seconds", "minutes", and "hours".) The tests are updated to cover "months" and "years", as well as to exercise a wider range of relative times, including showing which units (e.g. "days") are affected by GitoxideLabs#1696. A few sub-cases, of those adjusted or added here, test strings from a related test in Git, to allow comparison. But most are not related to cases there. As before, the tests pass on CI, or when the time zone is otherwise UTC, or with local time zones that never have DST adjustments. The sub-cases are reordered to be monotone increasing in the magnitude of the relative intervals tested (and thus monotone decreasing in the associated absolute timestamps), and the original input is kept zipped into the `actual` and `expected` arrays being compared. This way, if the assertion fails (such as due to GitoxideLabs#1696), it is clear and readable which sub-cases failed and exactly how, as well as what all the sub-cases are and what each sub-case tests. Reordering the sub-cases and preserving the inputs they parse in this way avoids the disadvantages of GitoxideLabs#1697 while keeping, and expanding on, its benefits. Failure, where it occurs, now looks like: --- STDERR: gix-date::date time::parse::relative::various --- thread 'time::parse::relative::various' panicked at gix-date\tests\time\parse.rs:252:9: assertion failed: `(left == right)`: relative times differ Diff < left / right > : [ ( "5 seconds ago", 2024-11-24T23:51:49Z, ), ( "5 minutes ago", 2024-11-24T23:46:54Z, ), ( "5 hours ago", 2024-11-24T18:51:54Z, ), ( "5 days ago", 2024-11-19T23:51:54Z, ), ( "3 weeks ago", 2024-11-03T23:51:54Z, ), ( "21 days ago", 2024-11-03T23:51:54Z, ), ( "504 hours ago", 2024-11-03T23:51:54Z, ), ( "30240 minutes ago", 2024-11-03T23:51:54Z, ), ( "2 months ago", < 2024-09-24T23:51:54Z, > 2024-09-24T22:51:54Z, ), ( "1460 hours ago", 2024-09-25T03:51:54Z, ), ( "87600 minutes ago", 2024-09-25T03:51:54Z, ), ( "14 weeks ago", < 2024-08-18T23:51:54Z, > 2024-08-18T22:51:54Z, ), ( "98 days ago", < 2024-08-18T23:51:54Z, > 2024-08-18T22:51:54Z, ), ( "2352 hours ago", 2024-08-18T23:51:54Z, ), ( "141120 minutes ago", 2024-08-18T23:51:54Z, ), ( "5 months ago", < 2024-06-24T23:51:54Z, > 2024-06-24T22:51:54Z, ), ( "3650 hours ago", 2024-06-25T21:51:54Z, ), ( "219000 minutes ago", 2024-06-25T21:51:54Z, ), ( "26 weeks ago", < 2024-05-26T23:51:54Z, > 2024-05-26T22:51:54Z, ), ( "182 days ago", < 2024-05-26T23:51:54Z, > 2024-05-26T22:51:54Z, ), ( "4368 hours ago", 2024-05-26T23:51:54Z, ), ( "262080 minutes ago", 2024-05-26T23:51:54Z, ), ( "8 months ago", < 2024-03-24T23:51:54Z, > 2024-03-24T22:51:54Z, ), ( "5840 hours ago", 2024-03-26T15:51:54Z, ), ( "350400 minutes ago", 2024-03-26T15:51:54Z, ), ( "38 weeks ago", 2024-03-03T23:51:54Z, ), ( "266 days ago", 2024-03-03T23:51:54Z, ), ( "6384 hours ago", 2024-03-03T23:51:54Z, ), ( "383040 minutes ago", 2024-03-03T23:51:54Z, ), ( "11 months ago", 2023-12-24T23:51:54Z, ), ( "8030 hours ago", 2023-12-26T09:51:54Z, ), ( "481800 minutes ago", 2023-12-26T09:51:54Z, ), ( "14 months ago", < 2023-09-24T23:51:54Z, > 2023-09-24T22:51:54Z, ), ( "21 months ago", 2023-02-24T23:51:54Z, ), ( "2 years ago", 2022-11-24T23:51:54Z, ), ( "20 years ago", 2004-11-24T23:51:54Z, ), ( "630720000 seconds ago", 2004-11-29T23:51:54Z, ), ]
Tasks
parse(input, Option<currenttime>)
pure, allowing the current time to be passed2 seconds ago
gix rev parse '@{1979-02-26 18:30:00}' -e
- should use a nicer format when displaying the parsed time