diff --git a/package.json b/package.json index e3379f91..56252cae 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "@vanilla-extract/css": "^1.15.1", "@vanilla-extract/css-utils": "^0.1.4", "axios": "^1.6.8", - "date-fns": "^3.6.0", "firebase": "^10.12.4", "lottie-react": "^2.4.0", "nanoid": "^5.0.7", @@ -86,4 +85,4 @@ "vite": "^5.2.0", "vitest": "^2.0.5" } -} \ No newline at end of file +} diff --git a/src/common/apis/tokenInstance.ts b/src/common/apis/tokenInstance.ts index 2b1e6c16..6eb9ce5e 100644 --- a/src/common/apis/tokenInstance.ts +++ b/src/common/apis/tokenInstance.ts @@ -1,5 +1,5 @@ +import { isBefore } from '@utils/dateFormatter'; import axios from 'axios'; -import { isBefore } from 'date-fns/isBefore'; const tokenInstance = axios.create({ baseURL: import.meta.env.VITE_BASE_URL, diff --git a/src/common/components/Input/components/Timer/index.tsx b/src/common/components/Input/components/Timer/index.tsx index 2fa854f7..0eb3ee06 100644 --- a/src/common/components/Input/components/Timer/index.tsx +++ b/src/common/components/Input/components/Timer/index.tsx @@ -1,4 +1,3 @@ -import { differenceInSeconds } from 'date-fns/differenceInSeconds'; import { useEffect, useState } from 'react'; import { useDeviceType } from 'contexts/DeviceTypeProvider'; @@ -6,6 +5,7 @@ import { useDeviceType } from 'contexts/DeviceTypeProvider'; import { timerVar } from './style.css'; import { TimerProps } from './types'; import formatTimer from './utils/formatTimer'; +import { differenceInSeconds } from '@utils/dateFormatter'; const INITIAL_TIME = 300; diff --git a/src/common/hooks/useDate.tsx b/src/common/hooks/useDate.tsx index 026da42b..489a5187 100644 --- a/src/common/hooks/useDate.tsx +++ b/src/common/hooks/useDate.tsx @@ -1,10 +1,9 @@ -import { isAfter } from 'date-fns/isAfter'; -import { isBefore } from 'date-fns/isBefore'; import { useEffect } from 'react'; import { useRecruitingInfo } from 'contexts/RecruitingInfoProvider'; import useGetRecruitingInfo from './useGetRecruitingInfo'; +import { isAfter, isBefore } from '@utils/dateFormatter'; const useDate = () => { const { handleSaveRecruitingInfo } = useRecruitingInfo(); diff --git a/src/common/utils/dateFormatter.ts b/src/common/utils/dateFormatter.ts new file mode 100644 index 00000000..d8dd135e --- /dev/null +++ b/src/common/utils/dateFormatter.ts @@ -0,0 +1,49 @@ +const toDate = (date: Date | string): Date => { + const newDate = typeof date === 'string' ? new Date(date) : date; + if (isNaN(newDate.getTime())) { + // TODO : 배포 시 주석 해제 + // throw new Error('변환할 수 없는 날짜입니다.'); + } + return newDate; +}; + +export const isBefore = (date: Date | string, dateToCompare: Date | string): boolean => { + return toDate(date).getTime() < toDate(dateToCompare).getTime(); +}; + +export const isAfter = (date: Date | string, dateToCompare: Date | string): boolean => { + return toDate(date).getTime() > toDate(dateToCompare).getTime(); +}; + +export const differenceInSeconds = (laterDate: Date | string, earlierDate: Date | string): number => { + return Math.floor((toDate(laterDate).getTime() - toDate(earlierDate).getTime()) / 1000); +}; + +export const subMinutes = (date: Date | string, amount: number): Date => { + const newDate = new Date(); + newDate.setTime(toDate(date).getTime() - amount * 60 * 1000); + return newDate; +}; + +export const _subMinutes = (date: Date | string, amount: number): Date => { + date = toDate(date); + date.setTime(date.getTime() - amount * 60 * 1000); + return date; +}; + +export const format = (date: Date | string, formatStr: string): string => { + date = toDate(date); + const days = ['일', '월', '화', '수', '목', '금', '토']; + const formatter: { [key: string]: string } = { + M: (date.getMonth() + 1).toString(), + dd: date.getDate().toString().padStart(2, '0'), + E: days[date.getDay()], + EEEE: days[date.getDay()] + '요일', + aaa: date.getHours() < 12 ? '오전' : '오후', + HH: date.getHours().toString().padStart(2, '0'), + hh: (date.getHours() % 12 || 12).toString().padStart(2, '0'), + mm: date.getMinutes().toString().padStart(2, '0'), + }; + + return formatStr.replace(/M|dd|E|EEE|aaa|HH|hh|mm/g, (substr) => formatter[substr]); +}; diff --git a/src/views/ApplyPage/components/ApplyInfo/index.tsx b/src/views/ApplyPage/components/ApplyInfo/index.tsx index 7bbec85a..305905d2 100644 --- a/src/views/ApplyPage/components/ApplyInfo/index.tsx +++ b/src/views/ApplyPage/components/ApplyInfo/index.tsx @@ -1,6 +1,3 @@ -import { format } from 'date-fns/format'; -import { ko } from 'date-fns/locale/ko'; -import { subMinutes } from 'date-fns/subMinutes'; import { memo } from 'react'; import Callout from '@components/Callout'; @@ -18,6 +15,7 @@ import { infoWrapperVar, } from './style.css'; import { APPLY_INFO } from '../../constant'; +import { format, subMinutes } from '@utils/dateFormatter'; const ApplyInfo = memo(({ isReview = false }: { isReview?: boolean }) => { const { deviceType } = useDeviceType(); @@ -34,22 +32,15 @@ const ApplyInfo = memo(({ isReview = false }: { isReview?: boolean }) => { if (!applicationStart) return; - const formattedApplicationStart = format(new Date(applicationStart || ''), 'M월 dd일 (E) aaa HH시 mm분', { - locale: ko, - }); - const formattedApplicationEnd = format(subMinutes(new Date(applicationEnd || ''), 1), 'M월 dd일 (E) aaa HH시 mm분', { - locale: ko, - }); + const formattedApplicationStart = format(new Date(applicationStart || ''), 'M월 dd일 (E) aaa HH시 mm분'); + const formattedApplicationEnd = format(subMinutes(new Date(applicationEnd || ''), 1), 'M월 dd일 (E) aaa HH시 mm분'); const formattedApplicationConfirmStart = format( new Date(applicationPassConfirmStart || ''), 'M월 dd일 (E) aaa HH시 mm분', - { - locale: ko, - }, ); - const formattedInterviewStart = format(new Date(interviewStart || ''), 'M월 dd일 (E)', { locale: ko }); - const formattedInterviewEnd = format(new Date(interviewEnd || ''), 'M월 dd일 (E)', { locale: ko }); - const formattedFinalPassConfirmStart = format(new Date(finalPassConfirmStart || ''), 'M월 dd일 (E)', { locale: ko }); + const formattedInterviewStart = format(new Date(interviewStart || ''), 'M월 dd일 (E)'); + const formattedInterviewEnd = format(new Date(interviewEnd || ''), 'M월 dd일 (E)'); + const formattedFinalPassConfirmStart = format(new Date(finalPassConfirmStart || ''), 'M월 dd일 (E)'); return (
diff --git a/src/views/ResultPage/components/FinalResult.tsx b/src/views/ResultPage/components/FinalResult.tsx index 0424de81..c73947b0 100644 --- a/src/views/ResultPage/components/FinalResult.tsx +++ b/src/views/ResultPage/components/FinalResult.tsx @@ -1,6 +1,4 @@ import { track } from '@amplitude/analytics-browser'; -import { format } from 'date-fns/format'; -import { ko } from 'date-fns/locale/ko'; import { useEffect } from 'react'; import Title from '@components/Title'; @@ -23,6 +21,7 @@ import IconMakersLogo from '../assets/IconMakersLogo'; import imgSoptLogo from '../assets/imgSoptLogo.png'; import imgSoptLogoWebp from '../assets/imgSoptLogo.webp'; import useGetFinalResult from '../hooks/useGetFinalResult'; +import { format } from '@utils/dateFormatter'; const Content = ({ pass }: { pass?: boolean }) => { const { deviceType } = useDeviceType(); @@ -33,9 +32,7 @@ const Content = ({ pass }: { pass?: boolean }) => { if (!name) return; const finalDate = new Date(finalPassConfirmStart || ''); - const formattedFinalPassConfirmStart = format(finalDate, 'M월 dd일 EEEE', { - locale: ko, - }); + const formattedFinalPassConfirmStart = format(finalDate, 'M월 dd일 EEEE'); const SOPT_NAME = isMakers ? `SOPT ${soptName}` : soptName; return ( diff --git a/src/views/ResultPage/components/ScreeningResult.tsx b/src/views/ResultPage/components/ScreeningResult.tsx index fdbeab74..6ab38fbb 100644 --- a/src/views/ResultPage/components/ScreeningResult.tsx +++ b/src/views/ResultPage/components/ScreeningResult.tsx @@ -1,6 +1,4 @@ import { track } from '@amplitude/analytics-browser'; -import { format } from 'date-fns/format'; -import { ko } from 'date-fns/locale/ko'; import { useEffect } from 'react'; import Title from '@components/Title'; @@ -23,6 +21,7 @@ import IconMakersLogo from '../assets/IconMakersLogo'; import imgSoptLogo from '../assets/imgSoptLogo.png'; import imgSoptLogoWebp from '../assets/imgSoptLogo.webp'; import useGetScreeningResult from '../hooks/useGetScreeningResult'; +import { format } from '@utils/dateFormatter'; const Content = ({ pass }: { pass?: boolean }) => { const { deviceType } = useDeviceType(); @@ -36,14 +35,10 @@ const Content = ({ pass }: { pass?: boolean }) => { const applicationPassConfirmNextDay = new Date(applicationDate); applicationPassConfirmNextDay.setDate(applicationDate.getDate() + 1); - const formattedInterviewStart = format(new Date(interviewStart || ''), 'M월 dd일', { locale: ko }); - const formattedInterviewEnd = format(new Date(interviewEnd || ''), 'M월 dd일', { locale: ko }); - const formattedApplicationPassConfirmStart = format(applicationDate, 'M월 dd일 EEEE', { - locale: ko, - }); - const formattedApplicationPassConfirmNextDay = format(new Date(applicationPassConfirmNextDay || ''), 'M월 dd일', { - locale: ko, - }); + const formattedInterviewStart = format(new Date(interviewStart || ''), 'M월 dd일'); + const formattedInterviewEnd = format(new Date(interviewEnd || ''), 'M월 dd일'); + const formattedApplicationPassConfirmStart = format(applicationDate, 'M월 dd일 EEEE'); + const formattedApplicationPassConfirmNextDay = format(new Date(applicationPassConfirmNextDay || ''), 'M월 dd일'); const SOPT_NAME = isMakers ? `SOPT ${soptName}` : soptName; return ( diff --git a/yarn.lock b/yarn.lock index 278562a2..784d0191 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2514,11 +2514,6 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" -date-fns@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.6.0.tgz#f20ca4fe94f8b754951b24240676e8618c0206bf" - integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww== - debug@4, debug@^4.3.5: version "4.3.6" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b"