Skip to content

Instantly share code, notes, and snippets.

@naporin0624
Last active June 2, 2022 06:44
Show Gist options
  • Save naporin0624/39bb418a3daff81c9ec3f93620d8b407 to your computer and use it in GitHub Desktop.
Save naporin0624/39bb418a3daff81c9ec3f93620d8b407 to your computer and use it in GitHub Desktop.

Prisma は timezone という概念を持っていない

結論

  • Prisma は DB に時間を書き込むとき DB, System の timezone に関わらず UTC で保存する。

対策方法

UTC で統一する

  • DB で時刻を持つときはすべて UTC を規定にする

middleware を使って timezoneOffset の分だけ時間をずらす

prisma.use を使用して読み出すときは UTC 時間にして値を返す
// 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)
  })
}
prisma.xxx.create, update をするときは timezoneOffset が足された CustomDate を作りそれを使用する
const originDate = Date;
const HOURS_OFFSET = -9;

class DateWithOffset extends Date{
  constructor(options) {
    if (options) {
      if (typeof options === 'string'){
        const t = new originDate(options);
        const d = new originDate(t.setHours(t.getHours()+HOURS_OFFSET));

        super(d);
      }else{
        super(options);
      }
    } else {
      super(originDate.now());
    }
  }
}

MySQL

この例では、PSL定義のデフォルト値が1975-08-19T23:15:30+07:00であり、異なるタイプのdatetimeカラムで何が間違っているのかを観察しています。すべての場合において、クエリーエンジンは、挿入および更新ステートメントにおいて、値を 1975-08-19T16:15:30Z に変換しています。

db.DateTime

Prismaは1975-08-19 16:15:30を挿入します。データベースには、他のシステムから挿入された値があり、データベースのデフォルト値である 1975-08-19 23:15:30 という値を尊重しています。この時点で、Prismaによってデータベースに2つの異なるデフォルト値が存在することになります。

これは、Prismaがデータタイムに使用するデフォルトのネイティブ型です。

db.Time

Prismaはデフォルト値16:15:30を挿入します。他のシステムでは23:15:30が挿入されるため、データベースには2つの異なるデフォルト値が存在し、どちらが正しいのかが分からなくなります。

db.Date

この例では、PSLにデフォルトの1975-08-19T00:05:30+01:00が格納されています。UTCへの変換により、この値は1975-08-18T23:05:30Zに変換されます。Prismaは1975-08-18という値を挿入し、データベースに書き込む他のシステムは1975-08-19という値を挿入します。

db.Timestamp

タイムスタンプ値は、常にUTCの1970-01-01 00:00:00からの(マイクロ)秒数である。タイムゾーン間で変換しても、基本的な値は変わりません。TIMESTAMPタイプは、ユーザーがUTC以外のタイムゾーンを使用している場合、Prismaによって間違って設定されない唯一のものです。

参考文献

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment