fix(activities): stamp local tz offset on event datetimes before submit #66

Merged
padreug merged 1 commit from fix/activities-event-time-tz-offset into dev 2026-05-23 13:29:48 +00:00

View file

@ -87,6 +87,28 @@ function foldDateTime(date: string, time: string): string {
return time ? `${date}T${time}` : date
}
// Stamp the form's wall-clock datetime with the user's local UTC offset
// before sending it to the LNbits events backend. Without this, the
// backend's `_to_unix` (nostr_publisher.py) treats a naive ISO string
// as UTC, so e.g. "08:00" entered in CEST gets stored as 08:00 UTC and
// the NIP-52 `start` tag is off by the user's offset on the relay
// the detail page then renders it +offset (08:00 10:00 in CEST).
// Preserving the user's intended wall-clock means stamping it here.
// Date-only values (no "T") pass through unchanged.
function withLocalTzOffset(value: string): string {
if (!value || !value.includes('T')) return value
// The form's "YYYY-MM-DDTHH:MM" is parsed by JS Date as local time;
// getTimezoneOffset() returns minutes west of UTC, so negate it.
const offMin = -new Date(value).getTimezoneOffset()
const sign = offMin >= 0 ? '+' : '-'
const abs = Math.abs(offMin)
const hh = String(Math.floor(abs / 60)).padStart(2, '0')
const mm = String(abs % 60).padStart(2, '0')
// Include `:00` seconds for compatibility with older Python
// `datetime.fromisoformat` (pre-3.11 won't accept "HH:MM+HH:MM").
return `${value}:00${sign}${hh}:${mm}`
}
const formSchema = toTypedSchema(
z
.object({
@ -138,8 +160,11 @@ interface BannerImage extends UploadedImage {
}
const bannerImages = ref<BannerImage[]>([])
// Inverse of foldDateTime: split a stored "YYYY-MM-DD[THH:MM]" back
// into separate date + time pieces for the form inputs.
// Inverse of foldDateTime: split a stored "YYYY-MM-DD[THH:MM[:SS][±HH:MM]]"
// back into separate date + time pieces for the form inputs. The
// time slice trims to "HH:MM" so any seconds + offset suffix added by
// withLocalTzOffset on submit drops cleanly the user sees the same
// wall-clock they originally entered when re-editing.
function splitDateTime(value: string | null | undefined): { date: string; time: string } {
if (!value) return { date: '', time: '' }
const [date, time = ''] = value.split('T')
@ -267,9 +292,8 @@ const onSubmit = form.handleSubmit(async (formValues) => {
try {
const eventData: CreateEventRequest = {
name: formValues.name,
event_start_date: foldDateTime(
formValues.event_start_date,
formValues.event_start_time
event_start_date: withLocalTzOffset(
foldDateTime(formValues.event_start_date, formValues.event_start_time)
),
}
if (!isEditMode.value) {
@ -281,9 +305,8 @@ const onSubmit = form.handleSubmit(async (formValues) => {
// Optional fields only include if provided
if (formValues.info) eventData.info = formValues.info
if (formValues.event_end_date) {
eventData.event_end_date = foldDateTime(
formValues.event_end_date,
formValues.event_end_time
eventData.event_end_date = withLocalTzOffset(
foldDateTime(formValues.event_end_date, formValues.event_end_time)
)
}
if (formValues.location) eventData.location = formValues.location