Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prisma DateTime Issue #20

Open
develjsw opened this issue Oct 15, 2024 · 0 comments
Open

Prisma DateTime Issue #20

develjsw opened this issue Oct 15, 2024 · 0 comments

Comments

@develjsw
Copy link
Owner

develjsw commented Oct 15, 2024

  1. 이슈 : Prisma를 사용하여 MySQL의 DateTime 타입 컬럼에 Insert / Update 작업을 할 때,
    9시간 이전의 날짜 값이 저장되는 문제가 발생

  2. 고민내용 :

    • MySQL DB 서버의 timezone 설정 문제일 수 있다고 생각해, 아래 명령어를 통해 확인한 결과, Asia/Seoul, Asia/Seoul, UTC로 정상적으로 설정되어 있었음
    SELECT @@global.time_zone, @@session.time_zone, @@system_time_zone;
    
    • Dockerfile에서 설정한 타임존도 Asia/Seoul로 잘 설정되어 있었음
    • Prisma에 timezone을 설정할 수 있는 방법을 찾아보았지만, Prisma는 timezone 설정을 제공하지 않는다는 것을 알게 됨
  3. 원인 : Prisma는 UTC(국제 표준시)가 기본으로 설정되어 있고 timezone 설정이 불가능하기 때문에 KST(한국 표준시)와의 9시간 차이가 발생한 것임

  4. 해결 방안 :
    UTC + 9 = KST 공식을 참고하여 3번 방식으로 이슈를 해소할 수 있었음

    [ 1 ] schema.prisma 파일에서 모델에 기본값을 설정하는 방법

    createdAt   DateTime  @default(dbgenerated("NOW()"))   @db.DateTime
    updatedAt  DateTime  @default(dbgenerated("NOW() ON UPDATE NOW()"))   @db.DateTime
    

    [ 2 ] 날짜가 입력되는 부분마다 +9시간을 추가하여 저장하는 방법

    [ 3 ] middleware를 구현하여 Insert / Update 작업 전 날짜 값에 +9시간을 추가하는 방식으로 변환하는 방법

     import { Injectable, OnModuleInit } from '@nestjs/common'
     import { Prisma, PrismaClient } from '@prisma/client'
     import { ConfigService } from '@nestjs/config'
     import * as moment from 'moment-timezone'
     
     @Injectable()
     export class PrismaService extends PrismaClient implements OnModuleInit {
         constructor(private readonly configService: ConfigService) {
             super({
                 datasources: {
                     db: {
                         url: configService.get<string>('DB 접속 정보'),
                     },
                 },
                 log: ['query', 'info', 'warn', 'error'],
             })
     
             this.$use(this.timezoneMiddleware)
         }
     
         async onModuleInit() {
             await this.$connect()
         }
     
         async runInTransaction<T>(callback: (prisma: PrismaClient) => Promise<T>): Promise<T> {
             return this.$transaction(callback)
         }
     
         private getFieldsToUpdate(tableName: string): string[] {
             switch (tableName) {
                 case '모델명1':
                     return ['dtCreate', 'dtUpdate']
                 case '모델명2':
                     return ['dtCreate', 'dtUpdate']
                 case '모델명3':
                     return ['dtCreate']
                 case '모델명4':
                     return ['dtStart', 'dtEnd', 'dtCreate', 'dtUpdate']
             }
         }
     
         private timezoneMiddleware: Prisma.Middleware = async (params: Prisma.MiddlewareParams, next) => {
             const fieldsToUpdate: string[] = this.getFieldsToUpdate(params.model)
     
             if (!params.args || (typeof params.args.data !== 'object' && !Array.isArray(params.args.data))) {
                 return next(params)
             }
     
             if (['create', 'update', 'createMany', 'updateMany'].includes(params.action)) {
                 if (['createMany', 'updateMany'].includes(params.action) && Array.isArray(params.args.data)) {
                     params.args.data = params.args.data.map((record: any) => {
                         fieldsToUpdate.forEach((field: string) => {
                             if (record[field]) {
                                 record[field] = moment(record[field]).add(9, 'hours').toDate()
                             }
                         })
                         return record
                     })
                 } else {
                     fieldsToUpdate.forEach((field: string) => {
                         if (params.args.data[field]) {
                             params.args.data[field] = moment(params.args.data[field]).add(9, 'hours').toDate()
                         }
                     })
                 }
             }
     
             return next(params)
         }
     }
    
  5. 참고한 부분 :

  1. 참고 내용 :
  • TypeOTM의 경우에는 MySQL 서버에 설정된 타임존을 그대로 따름
  • 따라서 MySQL 서버가 Asia/Seoul로 설정되어 있다면 정상적으로 저장됨
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant