diff --git a/frontend/src/pages/mobile/public/ScheduleDetail.jsx b/frontend/src/pages/mobile/public/ScheduleDetail.jsx index a2cfcb2..6df9243 100644 --- a/frontend/src/pages/mobile/public/ScheduleDetail.jsx +++ b/frontend/src/pages/mobile/public/ScheduleDetail.jsx @@ -5,6 +5,7 @@ import { motion, AnimatePresence } from 'framer-motion'; import { Calendar, Clock, ChevronLeft, Check, Link2, MapPin, Navigation, ExternalLink, X, ChevronRight } from 'lucide-react'; import Linkify from 'react-linkify'; import { getSchedule } from '../../../api/public/schedules'; +import { formatXDateTime } from '../../../utils/date'; import '../../../mobile.css'; // 카카오맵 SDK 키 @@ -177,26 +178,6 @@ const extractXUsername = (url) => { return match ? match[1] : null; }; -// X용 날짜/시간 포맷팅 (오후 2:30 · 2026년 1월 15일) -const formatXDateTime = (dateStr, timeStr) => { - if (!dateStr) return ''; - const date = new Date(dateStr); - const year = date.getFullYear(); - const month = date.getMonth() + 1; - const day = date.getDate(); - - let result = `${year}년 ${month}월 ${day}일`; - - if (timeStr) { - const [hours, minutes] = timeStr.split(':').map(Number); - const period = hours < 12 ? '오전' : '오후'; - const hour12 = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours; - result = `${period} ${hour12}:${String(minutes).padStart(2, '0')} · ${result}`; - } - - return result; -}; - // 유튜브 섹션 컴포넌트 function YoutubeSection({ schedule }) { const videoId = extractYoutubeVideoId(schedule.source?.url); @@ -367,21 +348,6 @@ function XSection({ schedule }) { ); - // datetime 포맷팅 - const formatDateTime = (datetime) => { - if (!datetime) return ''; - const [datePart, timePart] = datetime.split(' '); - const [year, month, day] = datePart.split('-').map(Number); - let timeStr = ''; - if (timePart) { - const [hour, minute] = timePart.split(':').map(Number); - const period = hour >= 12 ? '오후' : '오전'; - const hour12 = hour > 12 ? hour - 12 : hour === 0 ? 12 : hour; - timeStr = `${period} ${hour12}:${String(minute).padStart(2, '0')} · `; - } - return `${timeStr}${year}년 ${month}월 ${day}일`; - }; - return ( <> - {formatDateTime(schedule.datetime)} + {formatXDateTime(schedule.datetime)} diff --git a/frontend/src/pages/pc/public/schedule-sections/XSection.jsx b/frontend/src/pages/pc/public/schedule-sections/XSection.jsx index 692aa2b..7da1278 100644 --- a/frontend/src/pages/pc/public/schedule-sections/XSection.jsx +++ b/frontend/src/pages/pc/public/schedule-sections/XSection.jsx @@ -3,24 +3,7 @@ import { motion } from 'framer-motion'; import Linkify from 'react-linkify'; import { decodeHtmlEntities } from './utils'; import Lightbox from '../../../../components/common/Lightbox'; - -// datetime 포맷팅 (2026-01-18 19:00 → 오후 7:00 · 2026년 1월 18일) -const formatXDateTime = (datetime) => { - if (!datetime) return ''; - - const [datePart, timePart] = datetime.split(' '); - const [year, month, day] = datePart.split('-').map(Number); - - let timeStr = ''; - if (timePart) { - const [hour, minute] = timePart.split(':').map(Number); - const period = hour >= 12 ? '오후' : '오전'; - const hour12 = hour > 12 ? hour - 12 : hour === 0 ? 12 : hour; - timeStr = `${period} ${hour12}:${String(minute).padStart(2, '0')} · `; - } - - return `${timeStr}${year}년 ${month}월 ${day}일`; -}; +import { formatXDateTime } from '../../../../utils/date'; // X(트위터) 섹션 컴포넌트 function XSection({ schedule }) { @@ -39,10 +22,6 @@ function XSection({ schedule }) { window.history.pushState({ lightbox: true }, ''); }, []); - const closeLightbox = useCallback(() => { - setLightboxOpen(false); - }, []); - // 뒤로가기 처리 useEffect(() => { const handlePopState = () => { diff --git a/frontend/src/utils/date.js b/frontend/src/utils/date.js index 3367fbb..04446da 100644 --- a/frontend/src/utils/date.js +++ b/frontend/src/utils/date.js @@ -77,5 +77,30 @@ export const isToday = (date) => { return isSameDay(date, dayjs()); }; +/** + * X(트위터) 스타일 날짜/시간 포맷팅 + * 입력: "2026-01-18 19:00" 또는 "2026-01-18" + * 출력: "오후 7:00 · 2026년 1월 18일" 또는 "2026년 1월 18일" + * @param {string} datetime - 날짜/시간 문자열 + * @returns {string} 포맷된 문자열 + */ +export const formatXDateTime = (datetime) => { + if (!datetime) return ''; + + const d = dayjs(datetime).tz(KST); + const datePart = d.format('YYYY년 M월 D일'); + + // 시간이 포함된 경우 + if (datetime.includes(' ') || datetime.includes('T')) { + const hour = d.hour(); + const minute = d.minute(); + const period = hour >= 12 ? '오후' : '오전'; + const hour12 = hour > 12 ? hour - 12 : hour === 0 ? 12 : hour; + return `${period} ${hour12}:${String(minute).padStart(2, '0')} · ${datePart}`; + } + + return datePart; +}; + // dayjs 인스턴스도 export (고급 사용용) export { dayjs };