Skip to content

Commit e7ab332

Browse files
committed
perf(client): Remove Luxon from client bundle
Use date-fns timezone support for natural-language dates and the existing Intl helper for API timestamps, reducing the main route chunk by about 22 kB gzip.
1 parent 457d15b commit e7ab332

4 files changed

Lines changed: 30 additions & 32 deletions

File tree

bun.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"check": "bun run lint && bun run test && bun run build"
2121
},
2222
"dependencies": {
23+
"@date-fns/tz": "^1.5.0",
2324
"@fontsource-variable/inter": "^5.3.0",
2425
"@leeoniya/ufuzzy": "^1.0.19",
2526
"@radix-ui/react-accordion": "^1.2.1",
@@ -40,6 +41,7 @@
4041
"chrono-node": "^2.10.1",
4142
"class-variance-authority": "^0.7.1",
4243
"clsx": "^2.1.1",
44+
"date-fns": "^4.4.0",
4345
"hono": "^4.13.3",
4446
"lucide-react": "^0.454.0",
4547
"luxon": "^3.7.2",

src/components/left.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ import { isRoomAvailable, FilterCriteria } from "@/utils/filterUtils";
4444
import { useDateTimeContext } from "@/contexts/DateTimeContext";
4545
import { ThemeToggle } from "@/components/ThemeToggle";
4646
import {
47-
CAMPUS_TIMEZONE,
4847
parseTimeToMinutes,
4948
formatDateForDisplay,
5049
formatTimeForDisplay,
@@ -54,7 +53,6 @@ import {
5453
parseNaturalLanguageSearch,
5554
type NaturalLanguageSearchResult,
5655
} from "@/utils/naturalLanguageSearch";
57-
import { DateTime } from "luxon";
5856

5957
interface LeftSidebarProps {
6058
facilityData: FacilityStatus | null;
@@ -215,14 +213,13 @@ const LeftSidebar: React.FC<LeftSidebarProps> = ({
215213
const facilityDataMatchesSelection = useMemo(() => {
216214
if (!facilityData || isCurrentDateTime) return true;
217215

218-
const responseDateTime = DateTime.fromISO(facilityData.timestamp).setZone(
219-
CAMPUS_TIMEZONE,
220-
);
221-
if (!responseDateTime.isValid) return false;
216+
const responseInstant = new Date(facilityData.timestamp);
217+
if (Number.isNaN(responseInstant.getTime())) return false;
218+
const responseDateTime = getCampusDateTimeParts(responseInstant);
222219

223220
return (
224-
responseDateTime.toFormat("yyyy-MM-dd") === selectedDateTime.date &&
225-
responseDateTime.toFormat("HH:mm") === selectedDateTime.time.slice(0, 5)
221+
responseDateTime.date === selectedDateTime.date &&
222+
responseDateTime.time.slice(0, 5) === selectedDateTime.time.slice(0, 5)
226223
);
227224
}, [facilityData, isCurrentDateTime, selectedDateTime]);
228225

src/utils/naturalLanguageSearch.ts

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as chrono from "chrono-node/en";
2-
import { DateTime } from "luxon";
2+
import { format } from "date-fns";
3+
import { TZDate, tzOffset } from "@date-fns/tz";
34
import {
45
CAMPUS_TIMEZONE,
56
type CampusDateTime,
@@ -48,14 +49,12 @@ export function parseNaturalLanguageSearch(
4849
};
4950
}
5051

51-
const campusReference = DateTime.fromJSDate(referenceInstant).setZone(
52-
CAMPUS_TIMEZONE,
53-
);
52+
const campusReference = TZDate.tz(CAMPUS_TIMEZONE, referenceInstant);
5453
const matches = chrono.parse(
5554
normalizedQuery,
5655
{
5756
instant: referenceInstant,
58-
timezone: campusReference.offset,
57+
timezone: tzOffset(CAMPUS_TIMEZONE, referenceInstant),
5958
},
6059
{ forwardDate: true },
6160
);
@@ -99,29 +98,27 @@ export function parseNaturalLanguageSearch(
9998
};
10099
}
101100

102-
let target: DateTime;
101+
let target: TZDate;
103102
if (match.start.isCertain("timezoneOffset")) {
104103
// Rebuilding relative durations from wall-clock parts loses time at DST boundaries.
105-
target = DateTime.fromJSDate(match.start.date()).setZone(CAMPUS_TIMEZONE);
104+
target = TZDate.tz(CAMPUS_TIMEZONE, match.start.date());
106105
} else {
107-
target = DateTime.fromObject(
108-
{
109-
year: match.start.get("year") ?? campusReference.year,
110-
month: match.start.get("month") ?? campusReference.month,
111-
day: match.start.get("day") ?? campusReference.day,
112-
hour: hasExplicitHour
113-
? (match.start.get("hour") ?? campusReference.hour)
114-
: campusReference.hour,
115-
minute: hasExplicitHour
116-
? (match.start.get("minute") ?? 0)
117-
: campusReference.minute,
118-
second: 0,
119-
},
120-
{ zone: CAMPUS_TIMEZONE },
106+
target = TZDate.tz(
107+
CAMPUS_TIMEZONE,
108+
match.start.get("year") ?? campusReference.getFullYear(),
109+
(match.start.get("month") ?? campusReference.getMonth() + 1) - 1,
110+
match.start.get("day") ?? campusReference.getDate(),
111+
hasExplicitHour
112+
? (match.start.get("hour") ?? campusReference.getHours())
113+
: campusReference.getHours(),
114+
hasExplicitHour
115+
? (match.start.get("minute") ?? 0)
116+
: campusReference.getMinutes(),
117+
0,
121118
);
122119
}
123120

124-
if (!target.isValid) {
121+
if (Number.isNaN(target.getTime())) {
125122
return {
126123
locationQuery,
127124
temporalText,
@@ -134,8 +131,8 @@ export function parseNaturalLanguageSearch(
134131
locationQuery,
135132
temporalText,
136133
dateTime: {
137-
date: target.toFormat("yyyy-MM-dd"),
138-
time: target.startOf("minute").toFormat("HH:mm:ss"),
134+
date: format(target, "yyyy-MM-dd"),
135+
time: format(target, "HH:mm':00'"),
139136
},
140137
error: null,
141138
};

0 commit comments

Comments
 (0)