puppetlabs/puppet · error · ArgumentError
Using a Timezone designator in format specification is mutua
Error message
Using a Timezone designator in format specification is mutually exclusive to providing an explicit timezone argument
What it means
After a successful parse, Timestamp.parse checks whether an explicit timezone argument was given AND the parsed result contains a :zone key (i.e. the format contained a timezone designator like %Z or %z and the string satisfied it). Because both would determine the zone, the combination is rejected with ArgumentError - the two are mutually exclusive by design.
Source
Thrown at lib/puppet/pops/time/timestamp.rb:98
if parsed.include?(:leftover) || (has_timezone && parsed.include?(:zone))
parsed = nil
next
end
break
end
if parsed.nil?
raise ArgumentError, _(
"Unable to parse '%{str}' using any of the formats %{formats}"
) % { str: str, formats: format.join(', ') }
end
else
parsed = DateTime._strptime(str, format)
if parsed.nil? || parsed.include?(:leftover)
raise ArgumentError, _("Unable to parse '%{str}' using format '%{format}'") % { str: str, format: format }
end
if has_timezone && parsed.include?(:zone)
raise ArgumentError, _(
'Using a Timezone designator in format specification is mutually exclusive to providing an explicit timezone argument'
)
end
end
unless has_timezone
timezone = parsed[:zone]
has_timezone = !timezone.nil?
end
fraction = parsed[:sec_fraction]
# Convert msec rational found in _strptime hash to usec
fraction *= 1_000_000 unless fraction.nil?
# Create the Time instance and adjust for timezone
parsed_time = ::Time.utc(parsed[:year], parsed[:mon], parsed[:mday], parsed[:hour], parsed[:min], parsed[:sec], fraction)
parsed_time -= utc_offset(timezone) if has_timezone
# Convert to TimestampView on GitHub (pinned to e227c27540)
Solutions
- Pick one mechanism: either give the timezone argument and use zone-less formats ('%F %T'), or embed %Z/%z in the format and pass no timezone
- Strip the zone from the string when a timezone argument must win: raw.sub(/\s*(UTC|[+-]\d{2}:?\d{2}|Z)\z/, '')
- When the string's own zone is authoritative, drop the timezone argument entirely
- Audit helper functions that inject a default timezone arg on every call
Example fix
// before
ts = Timestamp.parse('2023-01-31 12:00:00 UTC', '%F %T %Z', '+01:00') # raise
// after (string zone wins)
ts = Timestamp.parse('2023-01-31 12:00:00 UTC', '%F %T %Z')
// after (argument wins)
ts = Timestamp.parse('2023-01-31 12:00:00', '%F %T', '+01:00') Defensive patterns
Strategy: validation
Validate before calling
has_zone_directive = fmt.match?(/%[-_0-9^#]*[zZ]/) raise ConfigError, 'format already encodes a zone - drop the timezone argument' if has_zone_directive && timezone ts = Timestamp.parse(str, fmt)
Try / catch
begin
ts = Timestamp.parse(str, fmt, timezone)
rescue ArgumentError => e
timezone = nil # let the embedded %Z/%z win and retry once
retry if e.message.include?('mutually exclusive')
raise
end Prevention
- Choose one zone mechanism per call: format designator OR explicit argument, never both
- Note :default auto-selects zone-less formats when a timezone argument is present - rely on that
- Strip trailing zone tokens from strings when a timezone argument must be authoritative
When it happens
Trigger: Timestamp.parse('2023-01-31 12:00:00 UTC', '%F %T %Z', '+00:00'), or Puppet DSL Timestamp('... %Z format...', timezone => '+02:00'). Also with the default format set: passing a timezone argument selects DEFAULT_FORMATS_WO_TZ precisely to avoid this, so it fires mainly with user-supplied formats.
Common situations: Layered defaults: a helper always passes a timezone argument while the caller's format string already encodes the zone; migrating configs that grew both mechanisms; ISO strings ending in 'Z' or '+02:00' paired with an explicit zone parameter.
Related errors
- Illegal timezone '%{timezone}'
- Unable to parse '%{str}' using any of the formats %{formats}
- Unable to parse '%{str}' using format '%{format}'
- %{klass} cannot be added to a Timestamp
- %{klass} cannot be subtracted from a Timestamp
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/83c000625bba4e81.
Report an issue: GitHub.