Skip to content

fs: accept multi-unit relative times like 1d12h in Time options - #10130

Merged
ncw merged 2 commits into
rclone:masterfrom
kevin9327:fix-parsetime-duration-sequence
Oct 31, 2026
Merged

ncw merged 2 commits into
rclone:masterfrom
kevin9327:fix-parsetime-duration-sequence

Conversation

@kevin9327

@kevin9327 kevin9327 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

What does this change do?

The time options docs say Time and Duration options take the same relative forms, with "2h45m" and "1d12h" as examples. A relative value passed as a Time is treated as an offset from now.

ParseDuration accepts multi-unit values through parseDurationSequence, but ParseTime only tries time.ParseDuration and the single-suffix parser. So any value that mixes rclone's own units (d, w, M, y) with others is rejected for Time options, such as --s3-version-at, --b2-version-at and --http-no-head-time:

--s3-version-at 36h works, and 1d12h works for Duration options like --max-age.

The fix makes ParseTime also try parseDurationSequence, like parseDurationFromNow already does.

Tests: I added 1d12h and 1w2d to TestParseTime. They fail before the change and pass after. go test ./fs passes and go vet ./fs is clean.

Linked issue

None. This is a small, obvious bug fix I found while reading the code.

Checklist

  • I have read the contribution guidelines.
  • I have added tests for all changes in this PR if appropriate.
  • I have added documentation for the changes if appropriate.
  • All commit messages are in house style.
  • I'm done, this Pull Request is ready for review :-)

Prepared with AI assistance. I reviewed, built and tested the change myself.

The docs say Time options take the same relative forms as Duration
options, with "1d12h" as an example, but ParseTime only tried
time.ParseDuration and single-suffix values, so --max-age style
Time options such as --s3-version-at 1d12h were rejected.
@kevin9327
kevin9327 requested a review from ncw as a code owner October 18, 2026 21:52
@ncw

ncw commented Oct 20, 2026

Copy link
Copy Markdown
Member

Thanks for spotting this. Rather than adding another step to the chain in ParseTime, I think it would be better to have ParseTime call ParseDuration directly. Then there's only one place that defines what a relative time is, and Time and Duration options can't drift apart again, which is what caused this bug.

Something like this:

	// Attempt to parse as a text time
	t, err = parseTimeDates(date)
	if err == nil {
		return t, nil
	}

	// Attempt to parse as a duration offset from now
	d, err := ParseDuration(date)
	if err != nil {
		return t, err
	}
	return now.Add(-d), nil

Please keep the parseTimeDates call first. ParseDuration also accepts dates, but turning one into a duration and back would lose the timezone it was given in, and it would overflow for dates more than ~292 years away.

Your new test cases pass with this change and so does the rest of go test ./fs. Could you update the PR to do it this way?

Have ParseTime call ParseDuration directly instead of repeating its
chain of duration parsers, so there is only one definition of a
relative time and Time and Duration options can't drift apart again.
parseTimeDates is still tried first so dates keep their timezone.
@kevin9327

Copy link
Copy Markdown
Contributor Author

Thanks, that's much cleaner. I've pushed a commit that makes ParseTime call ParseDuration directly after parseTimeDates (kept first, as you suggested), replacing the extra chain step. The new 1d12h-style test cases and go test ./fs pass.

@ncw ncw left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you :-)

@ncw
ncw merged commit dee8b7e into rclone:master Oct 31, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants