@@ -9,6 +9,8 @@ import UserInfoModal from '@/components/modals/ticketing/UserInfoModal';
99import { fetchClient } from '@/api/clients/fetchClient' ;
1010import { FetchSnackDetailResponse , TicketEvent } from '@/interfaces/SnackEvent' ;
1111import { formatDateTimeWithDay } from '@/utils/date' ;
12+ import { convertToKoreanDate } from '@/utils/convertToKoreanDate' ;
13+
1214
1315export default function SnackDetail ( ) {
1416 const router = useRouter ( ) ;
@@ -55,6 +57,34 @@ export default function SnackDetail() {
5557 }
5658 } ;
5759
60+ // "2025.10.15 (Wed) 12:00" 같은 문자열을 로컬시간으로 파싱 → ms
61+ const parseBackendLocalMs = ( raw : string ) : number | null => {
62+ if ( ! raw ) return null ;
63+
64+ // 괄호 속 요일(영/한) 제거: (Mon|Tue|...|Sun|월|화|수|목|금|토|일)
65+ const cleaned = raw . replace (
66+ / \s * \( (?: M o n | T u e | W e d | T h u | F r i | S a t | S u n | 월 | 화 | 수 | 목 | 금 | 토 | 일 ) \) \s * / i,
67+ ' '
68+ ) . trim ( ) ;
69+
70+ // 기대 포맷: YYYY.MM.DD HH:mm 또는 YYYY-MM-DD HH:mm
71+ const m = cleaned . match (
72+ / ^ ( \d { 4 } ) [ . \- ] ( \d { 2 } ) [ . \- ] ( \d { 2 } ) \s + ( \d { 2 } ) : ( \d { 2 } ) $ /
73+ ) ;
74+
75+ if ( m ) {
76+ const [ , y , mo , d , hh , mm ] = m . map ( Number ) ;
77+ // 로컬(KST) 기준 Date 생성
78+ const dt = new Date ( y , mo - 1 , d , hh , mm , 0 , 0 ) ;
79+ return dt . getTime ( ) ;
80+ }
81+
82+ // 혹시 다른 변형이 와도 마지막으로 Date.parse 시도
83+ const fallback = Date . parse ( cleaned ) ;
84+ return Number . isNaN ( fallback ) ? null : fallback ;
85+ } ;
86+
87+
5888 // --- SSE 구독: 재고 실시간 업데이트 + 서버 시각 보정 ---------------------------------
5989 useEffect ( ( ) => {
6090 if ( ! idStr ) return ;
@@ -157,7 +187,13 @@ export default function SnackDetail() {
157187 if ( ! eventData ) return ;
158188
159189 const updateTicketStatus = ( ) => {
160- const ticketMs = new Date ( eventData . eventTime ) . getTime ( ) ;
190+ const ticketMs = parseBackendLocalMs ( eventData . eventTime ) ;
191+ if ( ticketMs === null ) {
192+ console . warn ( '[TIME] eventTime 파싱 실패:' , eventData . eventTime ) ;
193+ setTicketStatus ( 'closed' ) ;
194+ return ;
195+ }
196+
161197 const nowMs = Date . now ( ) + serverOffsetRef . current ; // 서버 시간 보정
162198 const diffMs = ticketMs - nowMs ;
163199
@@ -229,11 +265,17 @@ export default function SnackDetail() {
229265 const handleTicketClick = async ( ) => {
230266 if ( ! eventData ) return ;
231267
232- const ticketMs = new Date ( eventData . eventTime ) . getTime ( ) ;
233- const nowAdj = Date . now ( ) + serverOffsetRef . current ;
234- const diff = ticketMs - nowAdj ;
268+ const ticketMs = parseBackendLocalMs ( eventData . eventTime ) ;
269+ if ( ticketMs === null ) {
270+ alert ( '이벤트 시작 시간을 해석할 수 없습니다. 잠시 후 다시 시도해주세요.' ) ;
271+ return ;
272+ }
273+
274+ const nowAdj = Date . now ( ) + serverOffsetRef . current ;
275+ const diff = ticketMs - nowAdj ;
235276
236- const safetyMs = 300 ; // 경계 보호용 버퍼
277+ const safetyMs = 300 ; // 경계 보호용 버퍼
278+
237279 if ( diff > safetyMs ) {
238280 console . log ( `[TICKET] too early by ${ diff } ms (safety ${ safetyMs } )` ) ;
239281 alert ( '오픈까지 잠시만 기다려주세요!' ) ;
@@ -350,11 +392,11 @@ export default function SnackDetail() {
350392 { isSelected === 'info' && (
351393 < >
352394 < div className = "font-bold text-[14px] mb-2" > { eventData . eventTitle } </ div >
353- < div > 일시: { formatDateTimeWithDay ( eventData . eventEndTime ) } </ div >
395+ < div > 일시: { convertToKoreanDate ( eventData . eventEndTime ) } </ div >
354396 < div > 장소: { eventData . locationInfo } </ div >
355397 < div > 대상: { eventData . target } </ div >
356398 < div > 수량: { eventData . quantity } </ div >
357- < div > 티켓팅 시작 시간: { formatDateTimeWithDay ( eventData . eventTime ) } </ div >
399+ < div > 티켓팅 시작 시간: { convertToKoreanDate ( eventData . eventTime ) } </ div >
358400 < div className = "text-black/50 self-center mt-[18px]" > { eventData . description } </ div >
359401 < a href = { eventData . promotionLink } className = "text-[#0D99FF] mt-[18px]" > 학생회 간식나눔 홍보글 링크</ a >
360402 < div className = "self-end text-[#AEAEAE]" > 문의: { eventData . inquiryNumber } </ div >
0 commit comments