Skip to content

Commit e683405

Browse files
authored
Merge pull request #35 from azvyae/fix/timezone-issue
Fix problem that jsonata cannot determine timezone offset
2 parents 68cfe32 + 0507fd2 commit e683405

6 files changed

Lines changed: 66 additions & 14 deletions

File tree

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "optimisticoder",
3-
"version": "1.1.0",
3+
"version": "1.1.1",
44
"private": false,
55
"contributors": [
66
{
@@ -24,7 +24,6 @@
2424
"decrypt-all": "env-decrypt local && env-decrypt production"
2525
},
2626
"dependencies": {
27-
"@types/js-cookie": "^3.0.6",
2827
"@vercel/analytics": "^1.3.1",
2928
"@vercel/speed-insights": "^1.0.12",
3029
"client-only": "^0.0.1",
@@ -54,6 +53,7 @@
5453
"sharp": "^0.33.5"
5554
},
5655
"devDependencies": {
56+
"@types/js-cookie": "^3.0.6",
5757
"@tailwindcss/typography": "^0.5.15",
5858
"@types/fs-extra": "^11.0.4",
5959
"@types/node": "^20.16.11",

pnpm-lock.yaml

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

src/app/stories/sections.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { FilteringHandler } from '@/app/stories/components';
22
import { StoryCard } from '@/components/common/story-card';
33
import { STORIES_URL } from '@/config/common';
4-
import { sfetch } from '@/helpers/common';
4+
import { sfetch, timezoneOffset } from '@/helpers/common';
55
import type { StoriesIndexEntry, StoriesMeta } from '@/types/common';
66
import type { CommonResponse } from '@/types/responses';
77
import Stars from '@public/static/svg/stars.svg';
88
import jsonata from 'jsonata';
9+
import { cookies } from 'next/headers';
910
import Image from 'next/image';
1011
import Link from 'next/link';
1112
import { redirect } from 'next/navigation';
@@ -60,9 +61,9 @@ async function listStories(
6061
} else if (key === 'search') {
6162
query = `$[$contains(title,/${value}/i) or keywords[$contains($,/${value}/i)]][]`;
6263
} else if (key === 'date') {
63-
const timezoneOffsetMillis =
64-
new Date().getTimezoneOffset() * 60 * 1000 * -1;
65-
query = `$[$contains($fromMillis($toMillis(date)+${timezoneOffsetMillis}),"${value}")][]`;
64+
const tz = cookies().get('tz')?.value ?? 'Asia/Jakarta';
65+
const timezoneOffsetMillis = timezoneOffset(tz);
66+
query = `$[$contains($fromMillis($toMillis(date)${timezoneOffsetMillis >= 0 ? '+' : ''}${timezoneOffsetMillis}),"${value}")][]`;
6667
}
6768
const expression = jsonata(query);
6869
const result: StoriesIndexEntry[] = await expression.evaluate(indexStories);

src/components/navigation/components.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

33
import { NavLink } from '@/components/link';
4-
import { links } from '@/config/common';
4+
import { APP_ENV, links } from '@/config/common';
55
import Cookies from 'js-cookie';
66
import { usePathname } from 'next/navigation';
77
import { useEffect, useState } from 'react';
@@ -130,10 +130,16 @@ function DarkModeSelector({
130130
if (!defaultTheme) {
131131
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
132132
document.documentElement.classList.add('dark');
133-
Cookies.set('theme', 'dark');
133+
Cookies.set('theme', 'dark', {
134+
expires: 365,
135+
secure: APP_ENV === 'production',
136+
});
134137
} else {
135138
document.documentElement.classList.remove('dark');
136-
Cookies.set('theme', 'light');
139+
Cookies.set('theme', 'light', {
140+
expires: 365,
141+
secure: APP_ENV === 'production',
142+
});
137143
}
138144
}
139145
setTheme(Cookies.get('theme') as Theme);
@@ -144,7 +150,10 @@ function DarkModeSelector({
144150
switch (t) {
145151
case 'dark':
146152
document.documentElement.classList.remove('dark');
147-
Cookies.set('theme', 'light');
153+
Cookies.set('theme', 'light', {
154+
expires: 365,
155+
secure: APP_ENV === 'production',
156+
});
148157
return 'light';
149158
case 'light':
150159
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
@@ -156,7 +165,10 @@ function DarkModeSelector({
156165
return;
157166
default:
158167
document.documentElement.classList.add('dark');
159-
Cookies.set('theme', 'dark');
168+
Cookies.set('theme', 'dark', {
169+
expires: 365,
170+
secure: APP_ENV === 'production',
171+
});
160172
return 'dark';
161173
}
162174
});

src/helpers/common.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,30 @@ function generateFallbackImage(initials: string) {
4848
return `/static/fallback/${randomNum}.jpg`;
4949
}
5050

51+
function timezoneOffset(ianaTimeZone: string) {
52+
const now = new Date();
53+
now.setSeconds(0, 0);
54+
55+
// Format current time in `ianaTimeZone` as `M/DD/YYYY, HH:MM:SS`:
56+
const tzDateString = now.toLocaleString('en-US', {
57+
timeZone: ianaTimeZone,
58+
hourCycle: 'h23',
59+
});
60+
61+
// Parse formatted date string:
62+
const match = /(\d+)\/(\d+)\/(\d+), (\d+):(\d+)/.exec(tzDateString);
63+
if (!match) {
64+
return 25200000;
65+
}
66+
const [, month, day, year, hour, min] = match.map(Number);
67+
68+
// Change date string's time zone to UTC and get timestamp:
69+
const tzTime = Date.UTC(year, month - 1, day, hour, min);
70+
71+
// Return the offset between UTC and target time zone:
72+
return Math.floor(tzTime - now.getTime());
73+
}
74+
5175
function isMobileBrowser() {
5276
if (!window) {
5377
return false;
@@ -69,4 +93,5 @@ export {
6993
generateFallbackImage,
7094
isMobileBrowser,
7195
sfetch,
96+
timezoneOffset,
7297
};

src/providers/client-provider.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
'use client';
22

3+
import { APP_ENV } from '@/config/common';
4+
import Cookies from 'js-cookie';
35
import { AppProgressBar as ProgressBar } from 'next-nprogress-bar';
6+
import { usePathname } from 'next/navigation';
7+
import { useEffect } from 'react';
48

59
const nprogressStyle = `
610
#nprogress {
@@ -45,6 +49,16 @@ const nprogressStyle = `
4549
}
4650
`;
4751
function ClientProvider() {
52+
const pathname = usePathname();
53+
useEffect(() => {
54+
const tz = Cookies.get('tz');
55+
if (!tz) {
56+
Cookies.set('tz', Intl.DateTimeFormat().resolvedOptions().timeZone, {
57+
expires: 1,
58+
secure: APP_ENV === 'production',
59+
});
60+
}
61+
}, [pathname]);
4862
return (
4963
<>
5064
<ProgressBar

0 commit comments

Comments
 (0)