Found while implementing the period engine (#1). Not fixed there: the fix belongs on src/objects/duty.object.ts, outside that card's file surface.
What happens now
timezone: Field.text({
label: 'Timezone',
defaultValue: 'UTC',
maxLength: 64,
description: 'IANA name (e.g. Europe/Berlin). Period boundaries and due dates are computed here.',
}),
Nothing checks the value is a zone the host knows. Europe/Munich, CET+1, Asia/Shanghai with a trailing space, or an empty string all save, all pass pnpm validate, and all look correct in the duty form.
The failure surfaces later and somewhere else: periodKeyFor / periodBounds / dueDateFor in src/functions/period.ts throw Unknown IANA timezone: "Europe/Munich" the first time the dispatcher touches that duty. So one bad character on one duty record becomes a run-time throw inside a batch job, attributed to the job rather than to the record — and every other duty in the same run is collateral depending on how #2 handles a partial failure.
Why it is worth a validation rather than tolerance in the engine
The engine deliberately refuses instead of falling back to UTC: a duty silently resolving "the 5th of the month" in the wrong zone is a wrong due date that nobody can see is wrong, which is strictly worse than a loud failure. That is the right call for the consumer — but it means the author never hears about the typo until dispatch night. The check wants to live at author time, where the person who made the typo is still looking at the field.
Options
- A
script validation on duly_duty (and duly_catalog_item, which carries the same field) rejecting a value the host's Intl cannot resolve, in the shape of the two validations already on the object.
- Or a
select of the zones the deployment actually serves, if the product would rather not offer all 418.
Same field, same gap on duly_catalog_item. Filing unassigned for triage — the choice between the two options is a product call, not a mechanical fix.
Found while implementing the period engine (#1). Not fixed there: the fix belongs on
src/objects/duty.object.ts, outside that card's file surface.What happens now
Nothing checks the value is a zone the host knows.
Europe/Munich,CET+1,Asia/Shanghaiwith a trailing space, or an empty string all save, all passpnpm validate, and all look correct in the duty form.The failure surfaces later and somewhere else:
periodKeyFor/periodBounds/dueDateForinsrc/functions/period.tsthrowUnknown IANA timezone: "Europe/Munich"the first time the dispatcher touches that duty. So one bad character on one duty record becomes a run-time throw inside a batch job, attributed to the job rather than to the record — and every other duty in the same run is collateral depending on how #2 handles a partial failure.Why it is worth a validation rather than tolerance in the engine
The engine deliberately refuses instead of falling back to UTC: a duty silently resolving "the 5th of the month" in the wrong zone is a wrong due date that nobody can see is wrong, which is strictly worse than a loud failure. That is the right call for the consumer — but it means the author never hears about the typo until dispatch night. The check wants to live at author time, where the person who made the typo is still looking at the field.
Options
scriptvalidation onduly_duty(andduly_catalog_item, which carries the same field) rejecting a value the host'sIntlcannot resolve, in the shape of the two validations already on the object.selectof the zones the deployment actually serves, if the product would rather not offer all 418.Same field, same gap on
duly_catalog_item. Filing unassigned for triage — the choice between the two options is a product call, not a mechanical fix.