Commit 47e1468
Changed files (7)
src
src/contents/posts/test.typ
@@ -6,12 +6,12 @@
title: "Test page",
author: "HPCesia",
desc: "Test Page",
- date: datetime(
+ date: datetime(
year: 2025,
month: 2,
day: 3,
),
- )
+ ),
)<frontmatter>
// ======================================
src/pages/posts/index.astro
@@ -1,14 +1,16 @@
---
import { getCollection } from "astro:content";
import GlobalLayout from "@/layouts/GlobalLayout.astro";
+import { DateTime } from "luxon";
+import { SITE_CONFIG } from "@/config";
const posts = await getCollection("posts");
const sortedPosts = posts.sort(
- (a, b) => new Date(b.data.date).getTime() - new Date(a.data.date).getTime(),
+ (a, b) => b.data.date.getTime() - a.data.date.getTime(),
);
const postsByYear = sortedPosts.reduce(
(acc, post) => {
- const year = new Date(post.data.date).getFullYear();
+ const year = post.data.date.getFullYear();
if (!acc[year]) {
acc[year] = [];
}
@@ -29,17 +31,22 @@ const postsByYear = sortedPosts.reduce(
<section>
<h2>{year}</h2>
<ul class="text-lg lg:text-xl">
- {posts.map((post) => (
- <li class="grid grid-rows-1 grid-cols-[1fr_auto] gap-2">
- <a href={`/posts/${post.id}`}>{post.data.title}</a>
- <time
- datetime={post.data.date.toISOString().split("T")[0]}
- class="text-sm lg:text-base text-neutral-600 dark:text-neutral-400"
- >
- {new Date(post.data.date).toLocaleDateString()}
- </time>
- </li>
- ))}
+ {posts.map((post) => {
+ const dt = DateTime.fromJSDate(post.data.date, {
+ zone: SITE_CONFIG.timeZone,
+ });
+ return (
+ <li class="grid grid-rows-1 grid-cols-[1fr_auto] gap-2">
+ <a href={`/posts/${post.id}`}>{post.data.title}</a>
+ <time
+ datetime={dt.toISO()}
+ class="text-sm lg:text-base text-neutral-600 dark:text-neutral-400"
+ >
+ {dt.toISODate()}
+ </time>
+ </li>
+ );
+ })}
</ul>
</section>
))
src/types/config.d.ts
@@ -15,4 +15,10 @@ export type SiteConfig = {
* Description of you website.
*/
description: string;
+ /**
+ * Time zone of typst posts' date.
+ * It should be a valid IANA time zone name,
+ * e.g. "Etc/UTC", "America/New_York", "Asia/Shanghai".
+ */
+ timeZone: string;
};
src/config.ts
@@ -6,4 +6,5 @@ export const SITE_CONFIG: SiteConfig = {
author: "HPCesia",
description:
"A Tufted style static blog template built with Astro and Typst.",
+ timeZone: "Etc/UTC",
};
src/content.config.ts
@@ -1,6 +1,8 @@
import { defineCollection } from "astro:content";
import { glob } from "astro/loaders";
import { z } from "astro/zod";
+import { DateTime } from "luxon";
+import { SITE_CONFIG } from "./config";
const typCollection = defineCollection({
loader: glob({
@@ -13,14 +15,30 @@ const typCollection = defineCollection({
desc: z.any().optional(),
date: z.string().transform((str) => {
const match = str.match(
- /datetime\(year:\s*(\d+),\s*month:\s*(\d+),\s*day:\s*(\d+)\)/,
+ /datetime\(\s*year:\s*(\d+),?\s*month:\s*(\d+),?\s*day:\s*(\d+)(?:,?\s*hour:\s*(\d+))?(?:,?\s*minute:\s*(\d+))?(?:,?\s*second:\s*(\d+))?,?\s*\)/,
);
if (match) {
- return new Date(
- parseInt(match[1], 10),
- parseInt(match[2], 10) - 1,
- parseInt(match[3], 10),
+ const year = parseInt(match[1], 10);
+ const month = parseInt(match[2], 10);
+ const day = parseInt(match[3], 10);
+ const hour = match[4] ? parseInt(match[4], 10) : 0;
+ const minute = match[5] ? parseInt(match[5], 10) : 0;
+ const second = match[6] ? parseInt(match[6], 10) : 0;
+
+ const dt = DateTime.fromObject(
+ {
+ year,
+ month,
+ day,
+ hour,
+ minute,
+ second,
+ },
+ {
+ zone: SITE_CONFIG.timeZone,
+ },
);
+ return dt.toJSDate();
}
return z.NEVER;
}),
package.json
@@ -16,10 +16,12 @@
"@tailwindcss/vite": "^4.2.1",
"astro": "^5.18.0",
"astro-typst": "^0.12.3",
+ "luxon": "^3.7.2",
"tailwindcss": "^4.2.1",
"unist-util-visit": "^5.1.0"
},
"devDependencies": {
- "@biomejs/biome": "^2.4.5"
+ "@biomejs/biome": "^2.4.5",
+ "@types/luxon": "^3.7.1"
}
}
pnpm-lock.yaml
@@ -29,6 +29,9 @@ importers:
astro-typst:
specifier: ^0.12.3
version: 0.12.3(@myriaddreamin/typst-ts-node-compiler@0.7.0-rc2)(@myriaddreamin/typst-ts-renderer@0.7.0-rc2)(@myriaddreamin/typst.ts@0.7.0-rc2(@myriaddreamin/typst-ts-renderer@0.7.0-rc2))(astro@5.18.0(jiti@2.6.1)(lightningcss@1.31.1)(rollup@4.59.0)(typescript@5.9.3))(cheerio@1.2.0)(rollup@4.59.0)
+ luxon:
+ specifier: ^3.7.2
+ version: 3.7.2
tailwindcss:
specifier: ^4.2.1
version: 4.2.1
@@ -39,6 +42,9 @@ importers:
'@biomejs/biome':
specifier: ^2.4.5
version: 2.4.5
+ '@types/luxon':
+ specifier: ^3.7.1
+ version: 3.7.1
packages:
@@ -954,6 +960,9 @@ packages:
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+ '@types/luxon@3.7.1':
+ resolution: {integrity: sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==}
+
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
@@ -1531,6 +1540,10 @@ packages:
resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==}
engines: {node: 20 || >=22}
+ luxon@3.7.2:
+ resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==}
+ engines: {node: '>=12'}
+
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
@@ -2891,6 +2904,8 @@ snapshots:
dependencies:
'@types/unist': 3.0.3
+ '@types/luxon@3.7.1': {}
+
'@types/mdast@4.0.4':
dependencies:
'@types/unist': 3.0.3
@@ -3644,6 +3659,8 @@ snapshots:
lru-cache@11.2.6: {}
+ luxon@3.7.2: {}
+
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5