181 lines
5.1 KiB
TypeScript
181 lines
5.1 KiB
TypeScript
/**
|
|
* 포맷 유틸리티 함수
|
|
* @description 포맷 처리에 필요한 유틸리티 함수를 제공합니다.
|
|
*/
|
|
|
|
/**
|
|
* JWT 디코딩
|
|
* @param base64EncodeVal JWT 인코딩 값
|
|
* @returns JWT 디코딩 값
|
|
*/
|
|
export const csrFormatJWT = (base64EncodeVal: string) => {
|
|
const decodeVal = JSON.parse(
|
|
decodeURIComponent(
|
|
window
|
|
.atob(base64EncodeVal)
|
|
.split('')
|
|
.map(function (c) {
|
|
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
|
|
})
|
|
.join('')
|
|
)
|
|
)
|
|
|
|
return decodeVal
|
|
}
|
|
|
|
/**
|
|
* 타임스탬프를 다양한 날짜 형식으로 변환합니다.
|
|
* @param timestamp 타임스탬프 (밀리초 또는 초)
|
|
* @param format 날짜 형식 ('YYYY-MM-DD', 'YYYY-MM-DD HH:mm:ss', 'MM/DD/YYYY', 'YYYY년 MM월 DD일' 등)
|
|
* @param locale 로케일 (기본값: 'ko-KR')
|
|
* @returns 포맷된 날짜 문자열
|
|
*/
|
|
export const formatTimestamp = (
|
|
timestamp: number | string,
|
|
format: string = 'YYYY.MM.DD',
|
|
_locale: string = 'ko-KR'
|
|
): string => {
|
|
if (!timestamp) return ''
|
|
|
|
// 타임스탬프를 숫자로 변환
|
|
let ts = typeof timestamp === 'string' ? parseInt(timestamp) : timestamp
|
|
|
|
// 초 단위인 경우 밀리초로 변환
|
|
if (ts < 10000000000) {
|
|
ts = ts * 1000
|
|
}
|
|
|
|
const date = new Date(ts)
|
|
|
|
// 유효하지 않은 날짜인 경우 빈 문자열 반환
|
|
if (isNaN(date.getTime())) {
|
|
return ''
|
|
}
|
|
|
|
// 미리 정의된 형식들
|
|
const predefinedFormats: Record<string, string> = {
|
|
'YYYY.MM.DD': date.toISOString().split('T')[0].replace(/-/g, '.'),
|
|
'YYYY-MM-DD': date.toISOString().split('T')[0],
|
|
'YYYY-MM-DD HH:mm': date.toISOString().replace('T', ' ').substring(0, 16),
|
|
'YYYY-MM-DD HH:mm:ss': date.toISOString().replace('T', ' ').split('.')[0],
|
|
'MM/DD/YYYY': date.toLocaleDateString('en-US'),
|
|
'DD/MM/YYYY': date.toLocaleDateString('en-GB'),
|
|
'YYYY년 MM월 DD일': date.toLocaleDateString('ko-KR', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
}),
|
|
'MM월 DD일': date.toLocaleDateString('ko-KR', {
|
|
month: 'long',
|
|
day: 'numeric',
|
|
}),
|
|
}
|
|
|
|
// 미리 정의된 형식이 있으면 사용
|
|
if (predefinedFormats[format]) {
|
|
return predefinedFormats[format]
|
|
}
|
|
|
|
// 커스텀 형식 처리
|
|
const year = date.getFullYear()
|
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
const day = String(date.getDate()).padStart(2, '0')
|
|
const hours = String(date.getHours()).padStart(2, '0')
|
|
const minutes = String(date.getMinutes()).padStart(2, '0')
|
|
const seconds = String(date.getSeconds()).padStart(2, '0')
|
|
|
|
return format
|
|
.replace('YYYY', String(year))
|
|
.replace('MM', month)
|
|
.replace('DD', day)
|
|
.replace('HH', hours)
|
|
.replace('mm', minutes)
|
|
.replace('ss', seconds)
|
|
}
|
|
|
|
/**
|
|
* 타임스탬프를 상대적 시간으로 변환합니다 (예: "3일 전", "2시간 전")
|
|
* @param timestamp 타임스탬프 (밀리초 또는 초)
|
|
* @param locale 로케일 (기본값: 'ko-KR')
|
|
* @returns 상대적 시간 문자열
|
|
*/
|
|
export const formatRelativeTime = (
|
|
timestamp: number | string,
|
|
locale: string = 'ko-KR'
|
|
): string => {
|
|
if (!timestamp) return ''
|
|
|
|
let ts = typeof timestamp === 'string' ? parseInt(timestamp) : timestamp
|
|
if (ts < 10000000000) {
|
|
ts = ts * 1000
|
|
}
|
|
|
|
const date = new Date(ts)
|
|
const now = new Date()
|
|
const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000)
|
|
|
|
if (diffInSeconds < 60) {
|
|
return locale === 'ko-KR' ? '방금 전' : 'just now'
|
|
}
|
|
|
|
const diffInMinutes = Math.floor(diffInSeconds / 60)
|
|
if (diffInMinutes < 60) {
|
|
return locale === 'ko-KR'
|
|
? `${diffInMinutes}분 전`
|
|
: `${diffInMinutes} minutes ago`
|
|
}
|
|
|
|
const diffInHours = Math.floor(diffInMinutes / 60)
|
|
if (diffInHours < 24) {
|
|
return locale === 'ko-KR'
|
|
? `${diffInHours}시간 전`
|
|
: `${diffInHours} hours ago`
|
|
}
|
|
|
|
const diffInDays = Math.floor(diffInHours / 24)
|
|
if (diffInDays < 30) {
|
|
return locale === 'ko-KR' ? `${diffInDays}일 전` : `${diffInDays} days ago`
|
|
}
|
|
|
|
const diffInMonths = Math.floor(diffInDays / 30)
|
|
if (diffInMonths < 12) {
|
|
return locale === 'ko-KR'
|
|
? `${diffInMonths}개월 전`
|
|
: `${diffInMonths} months ago`
|
|
}
|
|
|
|
const diffInYears = Math.floor(diffInMonths / 12)
|
|
return locale === 'ko-KR' ? `${diffInYears}년 전` : `${diffInYears} years ago`
|
|
}
|
|
|
|
/**
|
|
* 배열 또는 객체를 배열로 변환합니다.
|
|
* @param value 변환할 값 (배열, 객체, 또는 undefined/null)
|
|
* @returns 배열
|
|
*/
|
|
export const formatToArray = <T>(
|
|
value: T[] | Record<string, T> | undefined | null
|
|
): T[] => {
|
|
if (!value) return []
|
|
return Array.isArray(value) ? value : Object.values(value)
|
|
}
|
|
|
|
/**
|
|
* URL 경로에서 로케일 접두사를 제거합니다.
|
|
* @param path 경로 문자열
|
|
* @returns 로케일 접두사가 제거된 경로
|
|
*/
|
|
export const formatPathWithoutLocale = (path: string): string => {
|
|
return path.replace(/^\/[a-z]{2}(?=\/|$)/, '') || '/'
|
|
}
|
|
|
|
/**
|
|
* URL이 내부 링크인지 확인합니다.
|
|
* @param url 확인할 URL
|
|
* @returns 내부 링크 여부
|
|
*/
|
|
export const isInternalUrl = (url?: string): boolean => {
|
|
return !!url && !url.startsWith('http')
|
|
}
|