Make scheduling and dates timezone-safe

This commit is contained in:
2026-08-17 18:15:39 -04:00
parent 1853eaa963
commit 70c08054fb
63 changed files with 2515 additions and 779 deletions
+21 -4
View File
@@ -20,7 +20,9 @@ import { DateTimeField } from "@/components/ui/DateTimeField";
import {
formatZonedDateTime,
getDefaultScheduledSendAt,
getLocalTimeZone,
DEFAULT_TIME_ZONE,
toLocalDateTimeInputValue,
zonedDateTimeToInstant,
} from "@beenvoice/domain/time-zone";
import { fonts, spacing } from "@/constants/theme";
import { useAppTheme } from "@/contexts/ThemeContext";
@@ -42,7 +44,8 @@ export default function InvoiceSendScreen() {
const [scheduledAt, setScheduledAt] = useState(() =>
getDefaultScheduledSendAt(),
);
const timeZone = useMemo(() => getLocalTimeZone(), []);
const profileQuery = api.settings.getProfile.useQuery();
const timeZone = profileQuery.data?.timeZone ?? DEFAULT_TIME_ZONE;
const invoiceQuery = api.invoices.getById.useQuery(
{ id: id ?? "" },
@@ -139,7 +142,21 @@ export default function InvoiceSendScreen() {
function handleSchedule() {
if (!clientEmail || invoice.items.length === 0) return;
if (scheduledAt.getTime() < Date.now() + 60_000) {
let instant: Date;
try {
instant = zonedDateTimeToInstant(
toLocalDateTimeInputValue(scheduledAt),
timeZone,
"earlier",
);
} catch (error) {
Alert.alert(
"Choose another time",
error instanceof Error ? error.message : "Invalid local time",
);
return;
}
if (instant.getTime() < Date.now() + 60_000) {
Alert.alert(
"Choose a future time",
"The scheduled time must be at least one minute from now.",
@@ -148,7 +165,7 @@ export default function InvoiceSendScreen() {
}
scheduleInvoice.mutate({
invoiceId: invoice.id,
scheduledAt,
scheduledAt: instant,
timeZone,
customMessage: customMessage.trim() || undefined,
});