The operator grant forms use <input type="datetime-local">, which submits a naive wall-clock string with no timezone. The handlers parsed it with time.Parse, whose default for a zone-less value is UTC — so for an operator behind UTC, a near-future pick resolved to a past instant, GrantExpirationWorkflow saw a past valid_until and fired immediately, and the plan reverted to the default at once instead of lasting the chosen window. - Route IssueGrant and ExtendGrant through one parseGrantValidUntil helper. - Interpret the value in the server's local zone (time.ParseInLocation), and when the browser supplies valid_until_offset (grant-valid-until-tz.js, attached on htmx:configRequest) resolve the exact instant regardless of server timezone. - Reject a Valid Until in the past.
20 lines
1.0 KiB
JavaScript
20 lines
1.0 KiB
JavaScript
// grant-valid-until-tz.js
|
|
//
|
|
// The operator grant forms use <input type="datetime-local" name="valid_until">,
|
|
// which submits a naive wall-clock string with no timezone. Without help, the
|
|
// server can only assume its own local zone — wrong when the operator's browser
|
|
// is in a different timezone than the deployment. This attaches the browser's
|
|
// UTC offset (in minutes, for the *picked* date so DST is handled) as
|
|
// valid_until_offset on every HTMX request that carries a valid_until, letting
|
|
// the server resolve the exact instant. Pure progressive enhancement: if this
|
|
// script doesn't run, the server falls back to its local zone.
|
|
(function () {
|
|
document.body.addEventListener("htmx:configRequest", function (evt) {
|
|
var params = evt.detail && evt.detail.parameters;
|
|
if (!params || !params.valid_until) return;
|
|
var picked = new Date(params.valid_until); // datetime-local → local time for that date
|
|
if (isNaN(picked.getTime())) return;
|
|
params.valid_until_offset = String(picked.getTimezoneOffset());
|
|
});
|
|
})();
|