👋
PrismaでUTC以外のTimezoneを扱いたい
GraphQLを使う際にNode.jsのORM、Prismaで日本時刻で保存したかったのですがprismaではUTC以外対応していないようでした。
githubに解決策を掲載してくださっていた方がいましたので、備忘録のためにもまとめます。
掲載されていたコード
Create or update with custom now() method
(カスタム now() メソッドで作成または更新する)
// UTC-9 using dayjs
export const dbNow = (): Date => dayjs().add(9, 'hour').toDate()
const now = dbNow()
await prisma.user.create({
data: {
createdAt: now,
updatedAt: now,
},
})
Use a middleware that modifies all the times from Prisma's result object
(Prismaの結果オブジェクトからすべての時刻を修正するミドルウェアを使用する)
// Subtract 9 hours from all the Date objects recursively
function subtract9Hours(obj: Record<string, unknown>) {
if (!obj) return
for (const key of Object.keys(obj)) {
const val = obj[key]
if (val instanceof Date) {
obj[key] = dayjs(val).subtract(9, 'hour').toDate()
} else if (!isPrimitive(val)) {
subtract9Hours(val as any)
}
}
}
function prismaTimeMod<T>(value: T): T {
if (value instanceof Date) {
return dayjs(value).subtract(9, 'hour').toDate() as any
}
if (isPrimitive(value)) {
return value
}
subtract9Hours(value as any)
return value
}
// Create a prisma client instance with timemod
const prisma = new PrismaClient()
prisma.$use(async (params, next) => {
const result = await next(params)
return prismaTimeMod(result)
})
※コードに載せている日本語はあとから追加しているので、もともとはなかったです。
2つめのコードのミドルウェアはなぜ必要か
保存する際に、1つめのコードで+9時間するのでDBには日本時間で保存されます。
DBに保存されているcreatedAt
の日付が2022-02-01 12:00:00
の場合で
findFirst()
で値を問い合わせた際に、createdAt
の時刻は2022-02-01T12:00:00Z
になり、時間がISO8601でフォーマットされZ
が値に付きUTC時刻になってしまうので、
矛盾がおきます。UTCになるのであれば、2022-02-01T03:00:00Z
になっていないといけないので、これを解消するためにミドルウェアを使う必要があります。
Discussion
すごく参考になりました!
なんですが、いまのPrisma(v4)でこれをやると作成したモデルにたいして取得時に-9時間された結果が取得される可能性があるため、気をつけた方がいいです
v4になってからかは詳しく見たわけではありませんが、自分の環境だとそうなったことだけ同じようになった方用に書き留めておきます。
保存/取得方法ですが、個人的には
が望ましいかなと思います。
手元で確認したところ、
@default(now())
や@updatedAt
などのPrismaが自動的に挿入するところは、MySQLのタイムゾーンによらずUTCになってしまう感じでした。そのため、取得するときに-9時間されてしまいます。JSTで保存するためには、代わりに@default(dbgenerated("NOW(3)"))
などを使う必要がありました。Prismaの対応状況を考えると、強い理由(既存のデータがJSTで保存されているなど)がなければUTCで保存するのが無難かもしれません><