1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export function formatDate(
date: Date | string | number,
locale: string = "en-US",
opts: Intl.DateTimeFormatOptions = {},
includeTime: boolean = false
) {
const dateObj = new Date(date);
// 한국 로케일인 경우 하이픈 포맷 사용
if (locale === "ko-KR" || locale === "KR" || locale === "kr") {
const year = dateObj.getFullYear();
const month = String(dateObj.getMonth() + 1).padStart(2, "0");
const day = String(dateObj.getDate()).padStart(2, "0");
let result = `${year}-${month}-${day}`;
// 시간 포함 옵션이 활성화된 경우
if (includeTime) {
const hour = String(dateObj.getHours()).padStart(2, "0");
const minute = String(dateObj.getMinutes()).padStart(2, "0");
const second = String(dateObj.getSeconds()).padStart(2, "0");
result += ` ${hour}:${minute}:${second}`;
}
return result;
}
// 다른 로케일은 기존 방식 유지
return new Intl.DateTimeFormat(locale, {
month: opts.month ?? "long",
day: opts.day ?? "numeric",
year: opts.year ?? "numeric",
// Add time options when includeTime is true
...(includeTime && {
hour: opts.hour ?? "2-digit",
minute: opts.minute ?? "2-digit",
second: opts.second ?? "2-digit",
hour12: opts.hour12 ?? false, // Use 24-hour format by default
}),
...opts, // This allows overriding any of the above defaults
}).format(dateObj);
}
// formatDateTime 함수도 같은 방식으로 수정
export function formatDateTime(
date: Date | string | number | null | undefined,
locale: string = "en-US",
opts: Intl.DateTimeFormatOptions = {}
) {
if (date === null || date === undefined || date === '') {
return ''; // 또는 '-', 'N/A' 등 원하는 기본값 반환
}
const dateObj = new Date(date);
// 한국 로케일인 경우 하이픈 포맷 사용
if (locale === "ko-KR" || locale === "KR" || locale === "kr") {
const year = dateObj.getFullYear();
const month = String(dateObj.getMonth() + 1).padStart(2, "0");
const day = String(dateObj.getDate()).padStart(2, "0");
const hour = String(dateObj.getHours()).padStart(2, "0");
const minute = String(dateObj.getMinutes()).padStart(2, "0");
const second = String(dateObj.getSeconds()).padStart(2, "0");
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}
// 다른 로케일은 기존 방식 유지
return new Intl.DateTimeFormat(locale, {
month: opts.month ?? "long",
day: opts.day ?? "numeric",
year: opts.year ?? "numeric",
hour: opts.hour ?? "2-digit",
minute: opts.minute ?? "2-digit",
second: opts.second ?? "2-digit",
hour12: opts.hour12 ?? false,
...opts,
}).format(dateObj);
}
export function toSentenceCase(str: string) {
return str
.replace(/_/g, " ")
.replace(/([A-Z])/g, " $1")
.toLowerCase()
.replace(/^\w/, (c) => c.toUpperCase())
.replace(/\s+/g, " ")
.trim()
}
/**
* @see https://github.com/radix-ui/primitives/blob/main/packages/core/primitive/src/primitive.tsx
*/
export function composeEventHandlers<E>(
originalEventHandler?: (event: E) => void,
ourEventHandler?: (event: E) => void,
{ checkForDefaultPrevented = true } = {}
) {
return function handleEvent(event: E) {
originalEventHandler?.(event)
if (
checkForDefaultPrevented === false ||
!(event as unknown as Event).defaultPrevented
) {
return ourEventHandler?.(event)
}
}
}
/**
* 바이트 단위의 파일 크기를 사람이 읽기 쉬운 형식으로 변환합니다.
* (예: 1024 -> "1 KB", 1536 -> "1.5 KB")
*
* @param bytes 변환할 바이트 크기
* @param decimals 소수점 자릿수 (기본값: 1)
* @returns 포맷된 파일 크기 문자열
*/
export const formatFileSize = (bytes: number, decimals: number = 1): string => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
// 로그 계산으로 적절한 단위 찾기
const i = Math.floor(Math.log(bytes) / Math.log(k));
// 단위에 맞게 값 계산 (소수점 반올림)
const value = parseFloat((bytes / Math.pow(k, i)).toFixed(decimals));
return `${value} ${sizes[i]}`;
};
export function formatCurrency(
value: number,
currency: string | null | undefined = "KRW",
locale: string = "ko-KR"
): string {
return new Intl.NumberFormat(locale, {
style: "currency",
currency: currency ?? "KRW", // null이나 undefined면 "KRW" 사용
// minimumFractionDigits: 0,
// maximumFractionDigits: 2,
}).format(value)
}
/**
* YYYYMMDD 형태의 날짜를 YYYY nQ 형태의 분기로 변환
* @param dateString YYYYMMDD 형태의 문자열 (예: "20240315")
* @returns YYYY nQ 형태의 문자열 (예: "2024 1Q")
*/
export function formatDateToQuarter(dateString: string | null | undefined): string {
if (!dateString) return "-"
// YYYYMMDD 형태인지 확인
if (typeof dateString !== 'string' || dateString.length !== 8) {
return "-"
}
const year = dateString.substring(0, 4)
const month = parseInt(dateString.substring(4, 6), 10)
// 월을 분기로 변환
let quarter: number
if (month >= 1 && month <= 3) {
quarter = 1
} else if (month >= 4 && month <= 6) {
quarter = 2
} else if (month >= 7 && month <= 9) {
quarter = 3
} else if (month >= 10 && month <= 12) {
quarter = 4
} else {
return "-" // 잘못된 월
}
return `${year} ${quarter}Q`
}
export function formatBytes(bytes: number, decimals: number = 2): string {
if (bytes === 0) return "0 Bytes"
const k = 1024
const dm = decimals < 0 ? 0 : decimals
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i]
}
|