2023-04-08追記: なんかもっとややこしい話が出てきたので、後で追記する。
Gatsby(gatsby-transformer-remark)は日付形式を全部UTCだと思っているらしい。
さらに、YAML自体がDate型を持ってたりするので余計ややこしい...。
date の形式は実は結構ややこしいのでメモ。
gatsby-transformer-remark の readme には残念ながら記載なし。
おそらく、JavaScript の Date()
に食わせてると思ったので、これの引数の形式を調べることに。
こっちは
MDN
に正式な解答があった。
A string value representing a date, in a format recognized by the
Date.parse()
method. (The ECMA262 spec specifies a simplified version of ISO 8601, but other formats can be implementation-defined, which commonly include IETF-compliant RFC 2822 timestamps.)
simplified version of ISO 8601
というのが正式な規格らしい。
よく見る YYYY-MM-DDTHH:mm:ss.SSSZ
ってやつ。
これ以外に、実装依存で別の形式も受け付けるとのこと。
YYYY-MM-DDTHH:mm:ss.SSSZ
は直感的じゃないのと、入力が手間なのであまり使いたくない。
YYYY-MM-DD HH:mm
でも Gatsby は受け付けてくれるっぽいので、これを採用。
YYYY-MM-DD
は GoogleIME で『きょう』で変換すれば出るし、
HH:mm
も同様に『いま』で変換すれば出るので、入力しやすい。
秒は無視して OK。1 秒を争う記事は書かんよ 😎
ハマりポイントその 1
YYYY/MM/DD
は受け付けてくれない 。
Node.js(v19.2.0)で new Date("2023/02/11")
とすると受け付けてくれるが、
Gatsby ではだめなようだ...。
厳密には、Gatsby でも一見受け付けてくれるように見える。が、これがクセモノ。
formatString
を指定して query したり、
filter
に date
を指定して query したりすると ↓、
query MyQuery {
allMarkdownRemark(
filter: { frontmatter: { date: { lt: "2023-02-12 22:26:59" } } }
sort: { frontmatter: { date: DESC } }
) {
nodes {
frontmatter {
date
}
}
}
}
ここで初めてエラーを吐く ↓。
{
"errors": [
{
"message": "Field \"lt\" is not defined by type \"StringQueryOperatorInput\".",
"locations": [
{
"line": 3,
"column": 35
}
]
}
]
}
どうやら日付としてではなく、ただの文字列として認識しちゃってるようだ。
なので、 lt
の指定ができなくてエラーを吐く。
ハマりポイントその 2
他にありがちなのは、時分秒のうち、時を 1 桁にしちゃってもエラーになる。
まさしくこの記事を書いててやらかしてるんだけど、
2023-02-12 0:39
は NG。
2023-02-12 00:39
は OK。
その 1 同様に、これも特定の query をするまで、エラーが見えないのが厄介。
ハマりポイントその 3
filter 側も YYYY-MM-DD
じゃないとだめ。
しかもこっちはエラーすら吐かない 。
↓ の query では YYYY/MM/DD
になっており、エラーを吐かないが、期待した動作にはならない。
query MyQuery {
allMarkdownRemark(
filter: { frontmatter: { date: { lt: "2023/02/12" } } }
sort: { frontmatter: { date: DESC } }
) {
nodes {
frontmatter {
date
}
}
}
}
query 結果の傾向を見てると、↑ の場合は 2024
年未満の記事が query されるようだ。
2023/02/11 の記事はもちろん、2023/12/31 の記事も query されちゃう。ナンジャソリャ。
対策
ブログ記事用の template で date
に formatString
を指定して query するようにして、
export const postQuery = graphql`
query ($id: String!, $formatString: String) {
current: markdownPost(id: { eq: $id }) {
date(formatString: $formatString)
}
}
`;
コンポーネント側で "Invalid date"
だったらエラーに落とすようにしたら良い。
if (date === `Invalid date`) {
throw new Error(
[
`Invalid date!!`,
`Don't use the \`/\` as separator.`,
`e.g., ❌: \`2023/02/11 09:12\` => ⭕: \`2023-02-11 09:12\` .`,
`Set the hour to 2 digits,`,
`e.g., ❌: \`2023-02-11 9:12\` => ⭕: \`2023-02-11 09:12\` .`,
].join(` `)
);
}
エラーに落とすと、画面にデカデカとポップアップ表示されるので、これで見逃さない。
余談
本当はページアクセス時じゃなくて、
ビルド時にエラーに落としたいんだけど、方法が分からなかった...。
gatsby-transformer-remark が parser を公開してれば良かったんだけど、そんなもんない。
ならばと、gatsby-node.js
で query する作戦に切り替えたが、これも仕様上無理そうだった。
残念。