Skip to content

Commit 69d0393

Browse files
authored
Merge pull request #42 from programmerbar/timezone-fix
maybe fix for timezone
2 parents 11f137d + dd710ed commit 69d0393

4 files changed

Lines changed: 36 additions & 142 deletions

File tree

‎pnpm-lock.yaml‎

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

‎programmerbar-web/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"class-variance-authority": "0.7.1",
7373
"clsx": "2.1.1",
7474
"date-fns": "4.1.0",
75+
"date-fns-tz": "^3.2.0",
7576
"drizzle-orm": "0.44.4",
7677
"groq": "4.5.0",
7778
"ky": "^1.9.0",

‎programmerbar-web/src/lib/date.ts‎

Lines changed: 23 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,162 +1,45 @@
1+
import { toZonedTime } from 'date-fns-tz';
2+
import { format } from 'date-fns';
3+
14
export type Dateish = string | Date | number;
25
export const OSLO_TIME_ZONE = 'Europe/Oslo';
36

4-
const dateFormatter = new Intl.DateTimeFormat('nb-NO', {
5-
day: '2-digit',
6-
month: '2-digit',
7-
year: 'numeric',
8-
timeZone: OSLO_TIME_ZONE
9-
});
10-
11-
const timeFormatter = new Intl.DateTimeFormat('nb-NO', {
12-
hour: '2-digit',
13-
minute: '2-digit',
14-
hour12: false,
15-
timeZone: OSLO_TIME_ZONE
16-
});
17-
18-
const dateTimeFormatter = new Intl.DateTimeFormat('en-CA', {
19-
year: 'numeric',
20-
month: '2-digit',
21-
day: '2-digit',
22-
hour: '2-digit',
23-
minute: '2-digit',
24-
second: '2-digit',
25-
hour12: false,
26-
timeZone: OSLO_TIME_ZONE
27-
});
28-
29-
const dateTimeFormatterCache = new Map<string, Intl.DateTimeFormat>();
30-
31-
const timeZoneRegex = /(?:Z|[+-]\d{2}:?\d{2})$/i;
32-
33-
type DateTimeParts = {
34-
year: string;
35-
month: string;
36-
day: string;
37-
hour: string;
38-
minute: string;
39-
second: string;
40-
};
41-
427
const ensureDate = (date: Dateish): Date => {
438
return date instanceof Date ? date : new Date(date);
449
};
4510

46-
const getFormatterParts = (
47-
date: Date,
48-
formatter: Intl.DateTimeFormat
49-
): Partial<Record<Intl.DateTimeFormatPartTypes, string>> => {
50-
return formatter
51-
.formatToParts(date)
52-
.reduce<Partial<Record<Intl.DateTimeFormatPartTypes, string>>>((acc, part) => {
53-
if (part.type !== 'literal') {
54-
acc[part.type] = part.value;
55-
}
56-
return acc;
57-
}, {});
58-
};
59-
60-
const getDateTimeParts = (date: Date): DateTimeParts => {
61-
const parts = getFormatterParts(date, dateTimeFormatter);
62-
return {
63-
year: parts.year ?? '0000',
64-
month: parts.month ?? '01',
65-
day: parts.day ?? '01',
66-
hour: parts.hour ?? '00',
67-
minute: parts.minute ?? '00',
68-
second: parts.second ?? '00'
69-
};
70-
};
71-
72-
const getTimeZoneFormatter = (timeZone: string) => {
73-
if (!dateTimeFormatterCache.has(timeZone)) {
74-
dateTimeFormatterCache.set(
75-
timeZone,
76-
new Intl.DateTimeFormat('en-CA', {
77-
year: 'numeric',
78-
month: '2-digit',
79-
day: '2-digit',
80-
hour: '2-digit',
81-
minute: '2-digit',
82-
second: '2-digit',
83-
hour12: false,
84-
timeZone
85-
})
86-
);
87-
}
88-
89-
return dateTimeFormatterCache.get(timeZone)!;
90-
};
91-
92-
const getTimeZoneOffset = (date: Date, timeZone: string): number => {
93-
const formatter = getTimeZoneFormatter(timeZone);
94-
const parts = getFormatterParts(date, formatter);
95-
const inZoneUtc = Date.UTC(
96-
Number(parts.year ?? '0'),
97-
Number(parts.month ?? '1') - 1,
98-
Number(parts.day ?? '1'),
99-
Number(parts.hour ?? '0'),
100-
Number(parts.minute ?? '0'),
101-
Number(parts.second ?? '0')
102-
);
103-
104-
return inZoneUtc - date.getTime();
105-
};
106-
10711
export const formatDate = (date: Dateish) => {
108-
return dateFormatter.format(ensureDate(date));
12+
const utcDate = ensureDate(date);
13+
const osloTime = toZonedTime(utcDate, OSLO_TIME_ZONE);
14+
return format(osloTime, 'dd.MM.yyyy');
10915
};
11016

11117
export const time = (date: Dateish) => {
112-
return timeFormatter.format(ensureDate(date));
18+
const utcDate = ensureDate(date);
19+
const osloTime = toZonedTime(utcDate, OSLO_TIME_ZONE);
20+
return format(osloTime, 'HH:mm');
11321
};
11422

11523
export const normalDate = (date: Dateish) => {
116-
return `${formatDate(date)} ${time(date)}`;
24+
const utcDate = ensureDate(date);
25+
const osloTime = toZonedTime(utcDate, OSLO_TIME_ZONE);
26+
return format(osloTime, 'dd.MM.yyyy HH:mm');
11727
};
11828

119-
export const ISOStandard = (date: Dateish) => {
120-
const parts = getDateTimeParts(ensureDate(date));
121-
return `${parts.year}-${parts.month}-${parts.day}T${parts.hour}:${parts.minute}`;
29+
export const toLocalDateTimeString = (date: Dateish): string => {
30+
const utcDate = ensureDate(date);
31+
const osloTime = toZonedTime(utcDate, OSLO_TIME_ZONE);
32+
return format(osloTime, "yyyy-MM-dd'T'HH:mm");
12233
};
12334

124-
export const parseDateTimeLocal = (value: string, timeZone: string = OSLO_TIME_ZONE): Date => {
125-
const trimmedValue = value.trim();
126-
127-
if (!trimmedValue) {
128-
return new Date(trimmedValue);
129-
}
130-
131-
if (timeZoneRegex.test(trimmedValue) || !trimmedValue.includes('T')) {
132-
return new Date(trimmedValue);
133-
}
134-
135-
const [datePart, timePart] = trimmedValue.split('T');
136-
if (!datePart || !timePart) {
137-
return new Date(trimmedValue);
138-
}
139-
140-
const [year, month, day] = datePart.split('-').map((part) => Number(part));
141-
const timeSegments = timePart.split(':');
142-
const hour = Number(timeSegments[0] ?? '0');
143-
const minute = Number(timeSegments[1] ?? '0');
144-
const second = Number((timeSegments[2] ?? '0').split('.')[0]);
145-
146-
const targetUtc = Date.UTC(year, (month || 1) - 1, day || 1, hour, minute, second);
147-
let result = new Date(targetUtc);
148-
let offset = getTimeZoneOffset(result, timeZone);
149-
let adjusted = targetUtc - offset;
150-
151-
if (adjusted !== targetUtc) {
152-
result = new Date(adjusted);
153-
offset = getTimeZoneOffset(result, timeZone);
154-
adjusted = targetUtc - offset;
155-
}
35+
export const parseDateTimeLocal = (value: string): Date => {
36+
return new Date(value);
37+
};
15638

157-
return new Date(adjusted);
39+
export const toUtcISOStringFromLocal = (value: string) => {
40+
return new Date(value).toISOString();
15841
};
15942

160-
export const toUtcISOStringFromLocal = (value: string, timeZone: string = OSLO_TIME_ZONE) => {
161-
return parseDateTimeLocal(value, timeZone).toISOString();
43+
export const ISOStandard = (date: Dateish) => {
44+
return toLocalDateTimeString(date);
16245
};

‎programmerbar-web/src/routes/(portal)/portal/arrangementer/[id]/+page.svelte‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<script lang="ts">
2-
import { subHours } from 'date-fns';
32
import { formatDate, time } from '$lib/date';
43
import { capitalize } from '$lib/utils';
54
import { enhance } from '$app/forms';
@@ -111,7 +110,7 @@
111110
<div>
112111
<p class="text-sm text-gray-500">Tid</p>
113112
<p class="font-medium">
114-
{time(subHours(shift.startAt, 4))} - {time(subHours(shift.endAt, 4))}
113+
{time(shift.startAt)} - {time(shift.endAt)}
115114
</p>
116115
</div>
117116
</div>

0 commit comments

Comments
 (0)