인디스쿨의 교사인증 시스템에는 인증이 만료되는 시점을 계산하는 로직이 있는데 원래 코드는 Carbon::now()->startOfDay()->addYear(3)
과 같이 인증 받는 시점 기준으로 n년 후의 자정으로 설정이 된다. As-is 시스템에서는 timezone 설정을 모두 Asia/Seoul
로 했기 때문에 문제가 없었다.
그런데 얼마전 timezone 때문에 골치아팠던 경험이 있어서 합정 프로젝트는 마음 편하게 서버, DB, Laravel의 모든 timezone을 UTC로 통일하고 있다. 그러다보니 인증 만료 시점이 한국 기준으로 자정이 되는 게 아니라 DB에 00:00:00
으로 저장이 되어서 한국 기준으로 오전 9시가 되는 문제가 발생했다.
한국 기준으로 자정이 되려면 DB에는 9시간 전인 전날 날짜의 15:00:00
이 되어야 한다. 정말 다양한 방법으로 Carbon 객체를 만들고 저장을 했는데 계속 실패했다.
>>> Carbon\Carbon::now()->timezone('Asia/Seoul')->startOfDay()->addYear(3);
=> Carbon\Carbon @1658502000 {#3439
date: 2022-07-23 00:00:00.0 Asia/Seoul (+09:00),
}
먼저 이렇게 하면 Asia/Seoul
기준으로 00:00:00
이니까 DB에 저장할 때는 당연히 전날 날짜의 15:00:00
으로 저장이 될거라 생각했는데 모델 인스턴스에 이렇게 timezone이 적용된 Carbon 객체를 지정했어도 실제로 저장을 할 때는 Carbon
객체를 toDateString()
으로 그냥 변환한 후 저장하는 것 같았다. 즉 설정된 timezone에 상관없이 그냥 시간이 그대로 저장된다는 의미이다.
그래서 계속 삽질을 하다가 아래와 같이 정답을 찾을 수 있었다.
>>> today('Asia/Seoul')->timezone('UTC')->addYear(3);
=> Illuminate\Support\Carbon @1658502000 {#3441
date: 2022-07-22 15:00:00.0 UTC (+00:00),
}
참고로 Laravel에는 today()
라는 helper가 있는데 오늘의 자정 시각으로 Carbon 객체를 만들어 준다. Carbon::now()->startOfDay()
와 같은 의미이다. 그런데 today()
사이에 아래와 같이 timezone을 설정할 수 있다.
>>> today('Asia/Seoul')
=> Illuminate\Support\Carbon @1563807600 {#3439
date: 2019-07-23 00:00:00.0 Asia/Seoul (+09:00),
}
이게 결국 위에서 실패한 코드와 같은 의미인데, 실패했던 코드 제일 마지막에 다시 UTC로 timezone을 바꿔도 같은 결과를 얻을 수 있다.
Carbon\Carbon::now()->timezone('Asia/Seoul')->startOfDay()->addYear(3)->timezone('UTC')
=> Carbon\Carbon @1658502000 {#3443
date: 2022-07-22 15:00:00.0 UTC (+00:00),
}
timezone
대신 줄여서 아래와 같이 tz
로 나타낼 수도 있다.
>>> today('Asia/Seoul')->tz('UTC')->addYear(3);
=> Illuminate\Support\Carbon @1658502000 {#3441
date: 2022-07-22 15:00:00.0 UTC (+00:00),
}