bug: time.Duration fields reject systemd's own duration syntax #17

Closed
opened 2026-09-14 19:21:14 +00:00 by advisor-bot · 0 comments
Owner

Problem

In encoding/systemd/marshal_decode.go, setFieldValue (~lines 243–251), the time.Duration special case calls Go's time.ParseDuration directly. time.ParseDuration only understands Go's own unit spelling and syntax and does not understand systemd's systemd.time(7) grammar: bare integers meaning seconds (e.g. "30"), the min unit, or space-separated compound spans (e.g. "1min 30s") — all of which are standard, commonly-seen systemd unit file values.

Marshal's corresponding encode path (formatValue in marshal_encode.go) has the matching gap: it emits Go's Duration.String() format (e.g. "1m30s"), not systemd's.

Reproduction

type U struct{ TimeoutStartSec time.Duration }
type S struct{ Unit U }

// TimeoutStartSec=30        -> error: time: missing unit in duration "30"
// TimeoutStartSec=5min      -> error: time: unknown unit "min" in duration "5min"
// TimeoutStartSec=1min 30s  -> error: time: unknown unit "min " in duration "1min 30s"

// TimeoutStartSec=30s / 2h / 500ms succeed only because Go and systemd
// happen to share those particular unit spellings, masking the gap in
// casual testing.

Write a small systemd-time-span parser (bare integer → seconds; support us/usec, ms/msec, s/sec/second(s), m/min/minute(s), h/hr/hour(s), d/day(s), w/week(s), space-separated sums) for the decode side, and emit systemd-style unit suffixes on the encode side, rather than delegating straight to time.ParseDuration / Duration.String().

## Problem In `encoding/systemd/marshal_decode.go`, `setFieldValue` (~lines 243–251), the `time.Duration` special case calls Go's `time.ParseDuration` directly. `time.ParseDuration` only understands Go's own unit spelling and syntax and does **not** understand systemd's `systemd.time(7)` grammar: bare integers meaning seconds (e.g. `"30"`), the `min` unit, or space-separated compound spans (e.g. `"1min 30s"`) — all of which are standard, commonly-seen systemd unit file values. `Marshal`'s corresponding encode path (`formatValue` in `marshal_encode.go`) has the matching gap: it emits Go's `Duration.String()` format (e.g. `"1m30s"`), not systemd's. ## Reproduction ```go type U struct{ TimeoutStartSec time.Duration } type S struct{ Unit U } // TimeoutStartSec=30 -> error: time: missing unit in duration "30" // TimeoutStartSec=5min -> error: time: unknown unit "min" in duration "5min" // TimeoutStartSec=1min 30s -> error: time: unknown unit "min " in duration "1min 30s" // TimeoutStartSec=30s / 2h / 500ms succeed only because Go and systemd // happen to share those particular unit spellings, masking the gap in // casual testing. ``` ## Recommended fix Write a small systemd-time-span parser (bare integer → seconds; support `us`/`usec`, `ms`/`msec`, `s`/`sec`/`second(s)`, `m`/`min`/`minute(s)`, `h`/`hr`/`hour(s)`, `d`/`day(s)`, `w`/`week(s)`, space-separated sums) for the decode side, and emit systemd-style unit suffixes on the encode side, rather than delegating straight to `time.ParseDuration` / `Duration.String()`.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
pandora/systemd-file.gopack#17
No description provided.