Skip to content

Fix a year-less timestamp layout losing January 1 - #2427

Merged
dearchap merged 1 commit into
urfave:mainfrom
youdie006:timestamp-yearless-january-first
Sep 13, 2026
Merged

dearchap merged 1 commit into
urfave:mainfrom
youdie006:timestamp-yearless-january-first

Conversation

@youdie006

Copy link
Copy Markdown
Contributor

What type of PR is this?

  • bug

What this PR does / why we need it:

A TimestampFlag whose layout carries a month and day but no year returns today's date instead of the parsed date, but only when the value is January 1.

Config: TimestampConfig{Layouts: []string{"01-02"}, Timezone: time.UTC}

--time 01-01   before: 2026-09-08T00:00:00Z   after: 2026-01-01T00:00:00Z
--time 01-02   2026-01-02T00:00:00Z  (both)
--time 03-04   2026-03-04T00:00:00Z  (both)
--time 12-31   2026-12-31T00:00:00Z  (both)

timestampValue.Set decides "this layout has no date" by comparing the parsed instant against defaultTS, a probe parsed from time.TimeOnly, which lands on year-0 January 1 (flag_timestamp.go:90-93). A layout that does carry a date parses 01-01 onto that exact same day, so the two cases are indistinguishable and the date-less branch fires, overwriting month and day with time.Now().

The fix asks the layout rather than the parsed value. layoutHasDate round-trips a reference date that is deliberately not January 1 (0002-03-04) through Format/Parse: a date-less layout drops it and comes back as January 1, a layout with a date returns March 4.

  • flag_timestamp.go — capture the layout that matched in the parse loop, add the layoutHasDate guard to the first branch, add the helper.
  • flag_test.go — TestTimestampFlagApply_YearlessLayoutJanuaryFirst, three rows.

The one-sentence version: the very next branch (flag_timestamp.go:104, else if timestamp.Year() == 0) already handles "layout has a date but no year" correctly — January 1 is the single input that never reaches it.

matchedLayout is load-bearing: the loop otherwise discards the layout and the check cannot be made without it. The helper is unexported, so make generate produces no change to godoc-current.txt.

Which issue(s) this PR fixes:

None — found by reading flag_timestamp.go. I did not open one first since #2400 and #2401 landed the same way, but happy to file one if you would prefer.

Special notes for your reviewer:

time.Stamp-style layouts ("Jan _2 15:04:05", i.e. syslog and journal timestamps) are the canonical reason to use a year-less layout, and they are affected too — that row is in the test.

One honest limitation: flag_timestamp.go:92 calls time.Now() directly, so on January 1 itself the buggy code returns the right answer by coincidence and the two January rows would pass against the bug. That day is unobservable by construction, not a gap in the test. The March 4 control row and the mutation checks below still discriminate on every other day.

I also considered detecting date components by scanning the layout string for 1/01/Jan/2/02/_2 tokens, and rejected it — the round-trip probe is shorter and does not have to enumerate Go's layout vocabulary. If a layout is lossy enough that Format/Parse does not round-trip, the err == nil guard makes layoutHasDate return false, which is exactly today's behaviour, so an exotic layout degrades to the status quo rather than regressing.

Testing

TestTimestampFlagApply_YearlessLayoutJanuaryFirst drives the production chain — cmd.Run → Action → cmd.Timestamp("time") — with two January 1 rows and a March 4 control.

  • On main the two January rows fail (expected: 2026-01-01, actual: 2026-09-08) and the control passes.
  • Mutation-checked both directions, failing disjoint tests: forcing layoutHasDate to false (restoring current behaviour) fails only the new test, and forcing it to true fails only the pre-existing TestTimestampFlagApply_ShortenedLayouts. Both sides of the boundary are pinned.
  • make test passes (-race, coverage 99.3%). make vet, make lint, make ensure-goimports, make generate + make diffcheck, make v3diff, make check-binary-size (1.8MB) all clean, and golangci-lint run ./... reports 0 issues.
  • Existing timestamp coverage still passes, including time.Kitchen, time.Stamp, StampMilli/Micro/Nano, TimeOnly, "15:04" and time.RFC3339.

Release Notes

Fixed `TimestampFlag` returning the current date instead of the parsed date when the layout has no year and the value is January 1.

AI assistance disclosure: this patch was found and written with Claude Code. Every number above is verbatim from running it against main and against this branch.

timestampValue.Set decides "this layout has no date" by comparing the parsed
instant against defaultTS, a probe parsed from time.TimeOnly that lands on
year-0 January 1. A layout that does carry a date parses "01-01" onto that
same day, so the two cases are indistinguishable and the date-less branch
overwrites the month and day with time.Now().

    Layouts: []string{"01-02"}
    --time 01-01   before: 2026-09-08   after: 2026-01-01
    --time 01-02   2026-01-02 both
    --time 03-04   2026-03-04 both

Ask the layout instead of the parsed value. The next branch already handles
"has a date, no year" correctly for every other day of the year.
@youdie006
youdie006 requested a review from a team as a code owner September 8, 2026 13:50

@dearchap dearchap left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving. Verified on the PR branch: the new test fails on main (bug reproduced) and passes with the fix; full go test -race ., go vet, gofmt, and make v3diff are clean. The round-trip probe (layoutHasDate) is exact and only changes behavior in the formerly buggy case — edge probing (year-only, year-month, day-of-year, time-only, multi-layout layouts) shows no regressions. Minor non-blocking notes: Hoist layoutHasDate out of the per-Set path if you want to avoid the extra Format/Parse on each parse; the test's time.Now().UTC().Year() has a narrow NYE UTC rollover race, consistent with existing tests.

@dearchap
dearchap merged commit 72ca6b8 into urfave:main Sep 13, 2026
9 checks passed
Maks1mS pushed a commit to stplr-dev/stplr that referenced this pull request Oct 24, 2026
This PR contains the following updates:

| Package | Type | Update | Change | OpenSSF |
|---|---|---|---|---|
| [github.com/urfave/cli/v3](https://github.com/urfave/cli) | require | minor | `v3.10.1` → `v3.14.0` | [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/urfave/cli/badge)](https://securityscorecards.dev/viewer/?uri=github.com/urfave/cli) |

---

> ⚠️ **Warning**
>
> Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/23) for more information.

---

### Release Notes

<details>
<summary>urfave/cli (github.com/urfave/cli/v3)</summary>

### [`v3.14.0`](https://github.com/urfave/cli/releases/tag/v3.14.0)

[Compare Source](urfave/cli@v3.13.0...v3.14.0)

#### What's Changed

- chore(deps): bump mkdocs-git-revision-date-localized-plugin from 1.5.4 to 1.6.0 in the python-packages group by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;2436](urfave/cli#2436)
- fix: set BoolWithInverseFlag in a short option group by [@&#8203;lazerg](https://github.com/lazerg) in [#&#8203;2440](urfave/cli#2440)
- Keep arguments after a bare dash by [@&#8203;official-burak](https://github.com/official-burak) in [#&#8203;2419](urfave/cli#2419)
- fix: handle a multi-byte flag at the end of a short option group by [@&#8203;SulimanAbdulrazzaq](https://github.com/SulimanAbdulrazzaq) in [#&#8203;2438](urfave/cli#2438)
- fix: complete persistent flags in shell completion by [@&#8203;sb123sb123](https://github.com/sb123sb123) in [#&#8203;2437](urfave/cli#2437)
- fix: restore Unwrap on the error returned by Exit by [@&#8203;fadiroot](https://github.com/fadiroot) in [#&#8203;2444](urfave/cli#2444)
- fix: reject a parent's Local flag inside a short option group by [@&#8203;fadiroot](https://github.com/fadiroot) in [#&#8203;2445](urfave/cli#2445)
- fix: report a flag that needs an argument but is not last in a short group by [@&#8203;yu2971512385-ui](https://github.com/yu2971512385-ui) in [#&#8203;2443](urfave/cli#2443)
- Use prefixFor so flag suggestions agree with help on rune count by [@&#8203;januththedev](https://github.com/januththedev) in [#&#8203;2448](urfave/cli#2448)
- fix: reject duplicate flag names by [@&#8203;deepakganesh78](https://github.com/deepakganesh78) in [#&#8203;2396](urfave/cli#2396)
- fix: date a time-only timestamp today when Timezone is not UTC by [@&#8203;breken-ai](https://github.com/breken-ai) in [#&#8203;2450](urfave/cli#2450)
- fix: let a subcommand's command-line value replace a persistent flag's env/file value by [@&#8203;breken-ai](https://github.com/breken-ai) in [#&#8203;2449](urfave/cli#2449)
- Add urfave_cli_no_template build tag by [@&#8203;kolyshkin](https://github.com/kolyshkin) in [#&#8203;2441](urfave/cli#2441)
- fix: skip empty source values for flags that cannot parse them by [@&#8203;abo3losh1](https://github.com/abo3losh1) in [#&#8203;2451](urfave/cli#2451)

#### New Contributors

- [@&#8203;lazerg](https://github.com/lazerg) made their first contribution in [#&#8203;2440](urfave/cli#2440)
- [@&#8203;official-burak](https://github.com/official-burak) made their first contribution in [#&#8203;2419](urfave/cli#2419)
- [@&#8203;SulimanAbdulrazzaq](https://github.com/SulimanAbdulrazzaq) made their first contribution in [#&#8203;2438](urfave/cli#2438)
- [@&#8203;sb123sb123](https://github.com/sb123sb123) made their first contribution in [#&#8203;2437](urfave/cli#2437)
- [@&#8203;fadiroot](https://github.com/fadiroot) made their first contribution in [#&#8203;2444](urfave/cli#2444)
- [@&#8203;yu2971512385-ui](https://github.com/yu2971512385-ui) made their first contribution in [#&#8203;2443](urfave/cli#2443)
- [@&#8203;januththedev](https://github.com/januththedev) made their first contribution in [#&#8203;2448](urfave/cli#2448)
- [@&#8203;deepakganesh78](https://github.com/deepakganesh78) made their first contribution in [#&#8203;2396](urfave/cli#2396)
- [@&#8203;breken-ai](https://github.com/breken-ai) made their first contribution in [#&#8203;2450](urfave/cli#2450)
- [@&#8203;abo3losh1](https://github.com/abo3losh1) made their first contribution in [#&#8203;2451](urfave/cli#2451)

**Full Changelog**: <urfave/cli@v3.13.0...v3.14.0>

### [`v3.13.0`](https://github.com/urfave/cli/releases/tag/v3.13.0)

[Compare Source](urfave/cli@v3.12.0...v3.13.0)

#### What's Changed

- Trim the key of a map flag when TrimSpace is set by [@&#8203;youdie006](https://github.com/youdie006) in [#&#8203;2431](urfave/cli#2431)
- Fix inverse bool flag counting with an external counter by [@&#8203;jakezwang](https://github.com/jakezwang) in [#&#8203;2432](urfave/cli#2432)
- feat: add Deprecated field to flags and commands by [@&#8203;anandghegde](https://github.com/anandghegde) in [#&#8203;2429](urfave/cli#2429)
- fix: parse single-rune Unicode flags by [@&#8203;znnnnnnn-wil](https://github.com/znnnnnnn-wil) in [#&#8203;2434](urfave/cli#2434)

#### New Contributors

- [@&#8203;jakezwang](https://github.com/jakezwang) made their first contribution in [#&#8203;2432](urfave/cli#2432)
- [@&#8203;anandghegde](https://github.com/anandghegde) made their first contribution in [#&#8203;2429](urfave/cli#2429)
- [@&#8203;znnnnnnn-wil](https://github.com/znnnnnnn-wil) made their first contribution in [#&#8203;2434](urfave/cli#2434)

**Full Changelog**: <urfave/cli@v3.12.0...v3.13.0>

### [`v3.12.0`](https://github.com/urfave/cli/releases/tag/v3.12.0)

[Compare Source](urfave/cli@v3.11.0...v3.12.0)

#### What's Changed

- feat: add Command.ArgValidator for tree-wide argument validation by [@&#8203;dearchap](https://github.com/dearchap) in [#&#8203;2377](urfave/cli#2377)
- Single-value arguments can be marked as required by [@&#8203;idelchi](https://github.com/idelchi) in [#&#8203;2393](urfave/cli#2393)
- docs: clarify that named Arguments are excluded from cmd.Args() by [@&#8203;dearchap](https://github.com/dearchap) in [#&#8203;2411](urfave/cli#2411)
- fix: never run command action on shell completion past a double dash by [@&#8203;dearchap](https://github.com/dearchap) in [#&#8203;2412](urfave/cli#2412)
- chore(deps): bump github.com/stretchr/testify from 1.11.1 to 1.12.0 by [@&#8203;app/dependabot](https://github.com/app/dependabot) in [#&#8203;2413](urfave/cli#2413)
- Support custom FlagStringer for MutuallyExclusiveFlags by [@&#8203;SHIVANSHGARG07](https://github.com/SHIVANSHGARG07) in [#&#8203;2414](urfave/cli#2414)
- Add TextFlag for encoding.TextMarshaler/TextUnmarshaler values by [@&#8203;ChrisJr404](https://github.com/ChrisJr404) in [#&#8203;2415](urfave/cli#2415)
- chore(deps): bump github.com/stretchr/testify from 1.12.0 to 1.12.1 by [@&#8203;app/dependabot](https://github.com/app/dependabot) in [#&#8203;2416](urfave/cli#2416)
- chore(deps): bump the python-packages group with 2 updates by [@&#8203;app/dependabot](https://github.com/app/dependabot) in [#&#8203;2417](urfave/cli#2417)
- Show inherited persistent flags in child help by [@&#8203;justadityaraj](https://github.com/justadityaraj) in [#&#8203;2421](urfave/cli#2421)
- fix: preserve whitespace in positional arguments by [@&#8203;lenamonj](https://github.com/lenamonj) in [#&#8203;2423](urfave/cli#2423)
- fix: preserve whitespace in equals-form flag values by [@&#8203;BenYang12](https://github.com/BenYang12) in [#&#8203;2425](urfave/cli#2425)
- fix: complete subcommand flags after positional arguments by [@&#8203;fzlzjerry](https://github.com/fzlzjerry) in [#&#8203;2426](urfave/cli#2426)
- Fix a year-less timestamp layout losing January 1 by [@&#8203;youdie006](https://github.com/youdie006) in [#&#8203;2427](urfave/cli#2427)
- docs: add go-size-analyzer, module version, and reflection by [@&#8203;imsumedhaa](https://github.com/imsumedhaa) in [#&#8203;2428](urfave/cli#2428)

#### New Contributors

- [@&#8203;idelchi](https://github.com/idelchi) made their first contribution in [#&#8203;2393](urfave/cli#2393)
- [@&#8203;SHIVANSHGARG07](https://github.com/SHIVANSHGARG07) made their first contribution in [#&#8203;2414](urfave/cli#2414)
- [@&#8203;ChrisJr404](https://github.com/ChrisJr404) made their first contribution in [#&#8203;2415](urfave/cli#2415)
- [@&#8203;justadityaraj](https://github.com/justadityaraj) made their first contribution in [#&#8203;2421](urfave/cli#2421)
- [@&#8203;lenamonj](https://github.com/lenamonj) made their first contribution in [#&#8203;2423](urfave/cli#2423)
- [@&#8203;BenYang12](https://github.com/BenYang12) made their first contribution in [#&#8203;2425](urfave/cli#2425)
- [@&#8203;youdie006](https://github.com/youdie006) made their first contribution in [#&#8203;2427](urfave/cli#2427)
- [@&#8203;imsumedhaa](https://github.com/imsumedhaa) made their first contribution in [#&#8203;2428](urfave/cli#2428)

**Full Changelog**: <urfave/cli@v3.11.0...v3.12.0>

### [`v3.11.0`](https://github.com/urfave/cli/releases/tag/v3.11.0)

[Compare Source](urfave/cli@v3.10.1...v3.11.0)

#### What's Changed

- fix: correct a typo in the help message for the powershell autocompletion by [@&#8203;suzuki-shunsuke](https://github.com/suzuki-shunsuke) in [#&#8203;2383](urfave/cli#2383)
- Escape backslashes in fish completion descriptions by [@&#8203;mahirhir](https://github.com/mahirhir) in [#&#8203;2378](urfave/cli#2378)
- chore(deps): bump actions/setup-go from 6 to 7 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;2389](urfave/cli#2389)
- chore(deps): bump mkdocs-material from 9.7.6 to 9.7.7 in the python-packages group by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;2391](urfave/cli#2391)
- chore(deps): bump actions/setup-python from 6 to 7 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;2390](urfave/cli#2390)
- fix: skip all hidden flags in shell completion, not just bool flags by [@&#8203;vidigoat](https://github.com/vidigoat) in [#&#8203;2388](urfave/cli#2388)
- Fix nil pointer dereference for interface-typed flag values by [@&#8203;mrueg](https://github.com/mrueg) in [#&#8203;2400](urfave/cli#2400)
- Fix index out of range panic when Run is given no arguments by [@&#8203;mrueg](https://github.com/mrueg) in [#&#8203;2401](urfave/cli#2401)
- fix: honor BoolWithInverseFlag DefaultText by [@&#8203;fzlzjerry](https://github.com/fzlzjerry) in [#&#8203;2403](urfave/cli#2403)
- Fix Issue [#&#8203;2385](urfave/cli#2385) by [@&#8203;BrandonIrizarry](https://github.com/BrandonIrizarry) in [#&#8203;2386](urfave/cli#2386)
- fix: inherit HideHelpCommand in subcommands by [@&#8203;utkarshalpha](https://github.com/utkarshalpha) in [#&#8203;2395](urfave/cli#2395)
- test: cover Run-with-no-arguments paths ([#&#8203;2406](urfave/cli#2406)) by [@&#8203;dearchap](https://github.com/dearchap) in [#&#8203;2407](urfave/cli#2407)
- test: add shell-completion case for hidden BoolWithInverseFlag by [@&#8203;dearchap](https://github.com/dearchap) in [#&#8203;2408](urfave/cli#2408)
- test: fix ineffective mutex error-string assertion and add mirror cases by [@&#8203;dearchap](https://github.com/dearchap) in [#&#8203;2410](urfave/cli#2410)

#### New Contributors

- [@&#8203;mahirhir](https://github.com/mahirhir) made their first contribution in [#&#8203;2378](urfave/cli#2378)
- [@&#8203;vidigoat](https://github.com/vidigoat) made their first contribution in [#&#8203;2388](urfave/cli#2388)
- [@&#8203;fzlzjerry](https://github.com/fzlzjerry) made their first contribution in [#&#8203;2403](urfave/cli#2403)
- [@&#8203;BrandonIrizarry](https://github.com/BrandonIrizarry) made their first contribution in [#&#8203;2386](urfave/cli#2386)
- [@&#8203;utkarshalpha](https://github.com/utkarshalpha) made their first contribution in [#&#8203;2395](urfave/cli#2395)

**Full Changelog**: <urfave/cli@v3.10.1...v3.11.0>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At 12:00 AM through 04:59 AM and 10:00 PM through 11:59 PM, Monday through Friday (`* 0-4,22-23 * * 1-5`)
  - Only on Sunday and Saturday (`* * * * 0,6`)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xNTEuMiIsInVwZGF0ZWRJblZlciI6IjQ0LjE1MS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJLaW5kL0RlcGVuZGVuY2llcyJdfQ==-->

Reviewed-on: https://altlinux.space/stapler/stplr/pulls/503
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