{"record":{"id":"f9d628cf096b861e","repo":"hashicorp/nomad","slug":"invalid-time-zone-q-v","errorCode":null,"errorMessage":"Invalid time zone %q: %v","messagePattern":"Invalid time zone %q: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":5927,"sourceCode":"}\n\nfunc (p *PeriodicConfig) Validate() error {\n\tif !p.Enabled {\n\t\treturn nil\n\t}\n\n\tvar mErr multierror.Error\n\tif p.Spec != \"\" && len(p.Specs) != 0 {\n\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Only cron or crons may be used\"))\n\t}\n\tif p.Spec == \"\" && len(p.Specs) == 0 {\n\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Must specify a spec\"))\n\t}\n\n\t// Check if we got a valid time zone\n\tif p.TimeZone != \"\" {\n\t\tif _, err := time.LoadLocation(p.TimeZone); err != nil {\n\t\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Invalid time zone %q: %v\", p.TimeZone, err))\n\t\t}\n\t}\n\n\tswitch p.SpecType {\n\tcase PeriodicSpecCron:\n\t\t// Validate the cron spec\n\t\tif p.Spec != \"\" {\n\t\t\tif _, err := cronexpr.Parse(p.Spec); err != nil {\n\t\t\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Invalid cron spec %q: %v\", p.Spec, err))\n\t\t\t}\n\t\t}\n\t\t// Validate the cron specs\n\t\tfor _, spec := range p.Specs {\n\t\t\tif _, err := cronexpr.Parse(spec); err != nil {\n\t\t\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Invalid cron spec %q: %v\", spec, err))\n\t\t\t}\n\t\t}\n","sourceCodeStart":5909,"sourceCodeEnd":5945,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L5909-L5945","documentation":"PeriodicConfig.Validate checks that a periodic job's TimeZone field names a loadable IANA time zone via time.LoadLocation. When the zone string is non-empty but cannot be resolved (bad name or missing tz database), the validation error is appended to the job's multierror. The job spec is rejected until the time zone is corrected.","triggerScenarios":"Submitting/validating a periodic job whose `periodic { time_zone = \"...\" }` is set to a string that time.LoadLocation cannot resolve, e.g. \"EST5EDT\" without tzdata, \"UTC+2\", a typo like \"america/new_yorkk\", or an empty-named location.","commonSituations":"Hand-written HCL job files with guessed zone names; using fixed offsets like \"+02:00\" instead of IANA names; minimal/scratch Docker images lacking /usr/share/zoneinfo so even valid names fail to load.","solutions":["Set TimeZone to a valid IANA zone name, e.g. \"America/New_York\" or \"UTC\".","Verify the name with `time.LoadLocation(\"Zone/Name\")` in a quick Go snippet or `tzdata` lookup before submitting.","Ensure the machine running validation has tzdata installed (/usr/share/zoneinfo); in scratch containers embed the tzdata Go package or install tzdata.","Remove the TimeZone field entirely if the default (UTC) is acceptable."],"exampleFix":"// before\nperiodic {\n  cron = \"*/5 * * * *\"\n  time_zone = \"EST\"\n}\n// after\nperiodic {\n  cron = \"*/5 * * * *\"\n  time_zone = \"America/New_York\"\n}","handlingStrategy":"validation","validationCode":"func validTimeZone(tz string) bool {\n    if tz == \"\" {\n        return true\n    }\n    _, err := time.LoadLocation(tz)\n    return err == nil\n}\n// call before submitting: if !validTimeZone(job.Periodic.TimeZone) { ... }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only use IANA zone names (see `go run` of time.LoadLocation or the tzdata list).","Prefer \"UTC\" when the zone doesn't matter.","In containerized environments, install tzdata or import _ \"time/tzdata\".","Lint job files with a pre-submit validation hook."],"tags":["nomad","validation","timezone","job-spec"],"backgroundTag":"invalid-timezone","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}