fix(vaccinations): render a dose date through the calendar-date formatter

The noon-UTC rendering trick fixes the day west of UTC and moves it east
of UTC+12. A dose date is a calendar date, so it goes through the
UTC-pinned formatter the date fields already use, which is right in every
zone.
This commit is contained in:
Marc-André Bombeck committed 2026-10-01 10:01:17 +02:00
1 parent 7c5ff89def
commit e37263e8cc
2 files changed
+22 -11

No files matched your search

@@ -22,12 +22,13 @@ import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { toastWrittenOutcome } from "@/components/outcome/outcome-toast";
import { useFormatters, useTranslations } from "@/lib/i18n/context";
import { useDateFormatPreference, useTranslations } from "@/lib/i18n/context";
import {
useLinkDocumentToVaccination,
useVaccinationSuggestion,
} from "./use-vaccinations";
import type { VaccinationSuggestion } from "@/lib/vaccinations/document-suggestion";
import { formatDate as formatCalendarDate } from "@/lib/date-format";
export function VaccinationDocumentSuggestion({
anchor,
@@ -39,8 +40,8 @@ export function VaccinationDocumentSuggestion({
documentId: string;
disabled?: boolean;
}) {
const { t } = useTranslations();
const format = useFormatters();
const { t, locale } = useTranslations();
const dateFormat = useDateFormatPreference();
// Lift the date-only anchor to noon UTC — the rule is a ±7-day window, so the
// time never changes the verdict, and noon keeps a viewer's local midnight
// from rounding the day either way. The bare date would 422 the endpoint.
@@ -60,8 +61,9 @@ export function VaccinationDocumentSuggestion({
const name = candidate.antigenSlug
? t(`vaccinations.catalog.${candidate.antigenSlug}`)
: (candidate.vaccineName ?? t("vaccinations.suggestion.unnamed"));
// A date stored at UTC midnight: render the date, not the instant.
return `${name} · ${format.date(`${candidate.occurredAt.slice(0, 10)}T12:00:00.000Z`)}`;
// A date stored at UTC midnight: render the calendar date, not the
// instant (the previous evening west of UTC).
return `${name} · ${formatCalendarDate(candidate.occurredAt.slice(0, 10), dateFormat, locale)}`;
};
const onPick = (candidate: VaccinationSuggestion) => {
@@ -33,12 +33,17 @@ import { AlarmClock, Syringe } from "lucide-react";
import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { InfoPopover } from "@/components/ui/info-popover";
import { useFormatters, useTranslations } from "@/lib/i18n/context";
import {
useDateFormatPreference,
useFormatters,
useTranslations,
} from "@/lib/i18n/context";
import type { SeriesPosition } from "@/lib/vaccinations/series";
import type { VaccinationRenewalDTO } from "@/lib/vaccinations/renewal";
import { cn } from "@/lib/utils";
import type { Vaccination } from "./use-vaccinations";
import { CatalogInfo, catalogInfoAvailable } from "./catalog-info";
import { formatDate as formatCalendarDate } from "@/lib/date-format";
type Translate = ReturnType<typeof useTranslations>["t"];
@@ -148,8 +153,8 @@ function DoseRow({
row: GroupRow;
onEdit?: (record: Vaccination) => void;
}) {
const { t } = useTranslations();
const format = useFormatters();
const { t, locale } = useTranslations();
const dateFormat = useDateFormatPreference();
const { record, position } = row;
const series = seriesLabel(t, position);
const identity = recordIdentity(t, record);
@@ -199,9 +204,13 @@ function DoseRow({
</div>
<div className="text-muted-foreground flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs">
<span>
{/* A date, stored at UTC midnight: render its date part, not
the instant (the previous evening west of UTC). */}
{format.date(`${record.occurredAt.slice(0, 10)}T12:00:00.000Z`)}
{/* A date, stored at UTC midnight: render the calendar date,
not the instant (the previous evening west of UTC). */}
{formatCalendarDate(
record.occurredAt.slice(0, 10),
dateFormat,
locale,
)}
</span>
{record.lotNumber ? (
<span data-slot="vaccination-lot">