Fix event time save off-by-one using bundled tz data.

Use moment-timezone in parseEventDatetime so naive America/Asuncion wall-clock times convert correctly regardless of the host Node tz database. Also normalize user registration date filters with toDbDate for Postgres.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Michilis
2026-07-03 01:51:18 +00:00
co-authored by Cursor
parent 78ffd0ae52
commit 8d5fbf18b4
3 changed files with 9 additions and 14 deletions
+1
View File
@@ -25,6 +25,7 @@
"hono": "^4.4.7",
"ioredis": "^5.11.1",
"jose": "^5.4.0",
"moment-timezone": "^0.6.2",
"nanoid": "^5.0.7",
"nodemailer": "^7.0.13",
"pdfkit": "^0.17.2",
+5 -11
View File
@@ -1,5 +1,6 @@
import { nanoid } from 'nanoid';
import { randomUUID } from 'crypto';
import moment from 'moment-timezone';
/**
* Get database type (reads env var each time to handle module loading order)
@@ -59,17 +60,10 @@ export function parseEventDatetime(
return new Date(datetime);
}
// Treat the digits as UTC so we have a stable reference instant.
const fakeUTC = new Date(datetime + 'Z');
// Ask Intl what that UTC instant looks like in both UTC and the target tz.
const utcStr = fakeUTC.toLocaleString('en-US', { timeZone: 'UTC' });
const tzStr = fakeUTC.toLocaleString('en-US', { timeZone: timezone });
// The gap between the two tells us the tz offset at this point in time.
const offsetMs = new Date(utcStr).getTime() - new Date(tzStr).getTime();
return new Date(fakeUTC.getTime() + offsetMs);
// Interpret the wall-clock digits as local time in `timezone` using
// moment-timezone's bundled IANA data. This keeps the conversion correct
// regardless of the host Node runtime's (possibly stale) tz database.
return moment.tz(datetime, timezone).toDate();
}
/**
+3 -3
View File
@@ -4,7 +4,7 @@ import { z } from 'zod';
import { db, dbGet, dbAll, users, tickets, events, payments, magicLinkTokens, userSessions, invoices, auditLogs, emailLogs, paymentOptions, legalPages, siteSettings } from '../db/index.js';
import { eq, desc, sql, and, gte, lte } from 'drizzle-orm';
import { requireAuth } from '../lib/auth.js';
import { getNow } from '../lib/utils.js';
import { getNow, toDbDate } from '../lib/utils.js';
interface UserContext {
id: string;
@@ -39,8 +39,8 @@ usersRouter.get('/', requireAuth(['admin']), async (c) => {
const conditions: any[] = [];
if (role) conditions.push(eq((users as any).role, role));
if (accountStatus) conditions.push(eq((users as any).accountStatus, accountStatus));
if (registeredAfter) conditions.push(gte((users as any).createdAt, registeredAfter));
if (registeredBefore) conditions.push(lte((users as any).createdAt, registeredBefore));
if (registeredAfter) conditions.push(gte((users as any).createdAt, toDbDate(registeredAfter)));
if (registeredBefore) conditions.push(lte((users as any).createdAt, toDbDate(registeredBefore)));
if (search) {
const like = `%${search.toLowerCase()}%`;
conditions.push(sql`(