jskim-node #420

Merged
kjs merged 16 commits from jskim-node into main 2026-03-17 21:09:11 +09:00
38 changed files with 144 additions and 83 deletions
Showing only changes of commit ae4fe7a66e - Show all commits

6
.gitignore vendored
View File

@ -182,3 +182,9 @@ scripts/browser-test-*.js
# 개인 작업 문서
popdocs/
.cursor/rules/popdocs-safety.mdc
# 멀티 에이전트 MCP 태스크 큐
mcp-task-queue/
.cursor/rules/multi-agent-pm.mdc
.cursor/rules/multi-agent-worker.mdc
.cursor/rules/multi-agent-tester.mdc

View File

@ -1,4 +1,5 @@
import "dotenv/config";
process.env.TZ = "Asia/Seoul";
import "express-async-errors"; // async 라우트 핸들러의 에러를 Express 에러 핸들러로 자동 전달
import express from "express";
import cors from "cors";

View File

@ -66,8 +66,9 @@ export const initializePool = (): Pool => {
// 연결 풀 이벤트 핸들러
pool.on("connect", (client) => {
client.query("SET timezone = 'Asia/Seoul'");
if (config.debug) {
console.log("✅ PostgreSQL 클라이언트 연결 생성");
console.log("✅ PostgreSQL 클라이언트 연결 생성 (timezone: Asia/Seoul)");
}
});

View File

@ -82,7 +82,8 @@ export function DashboardTopMenu({
) => {
if (format === "png") {
const link = document.createElement("a");
const filename = `${dashboardTitle || "dashboard"}_${new Date().toISOString().split("T")[0]}.png`;
const _fd = new Date();
const filename = `${dashboardTitle || "dashboard"}_${_fd.getFullYear()}-${String(_fd.getMonth() + 1).padStart(2, "0")}-${String(_fd.getDate()).padStart(2, "0")}.png`;
link.download = filename;
link.href = dataUrl;
document.body.appendChild(link);
@ -111,7 +112,8 @@ export function DashboardTopMenu({
});
pdf.addImage(dataUrl, "PNG", 0, 0, imgWidth, imgHeight);
const filename = `${dashboardTitle || "dashboard"}_${new Date().toISOString().split("T")[0]}.pdf`;
const _pd = new Date();
const filename = `${dashboardTitle || "dashboard"}_${_pd.getFullYear()}-${String(_pd.getMonth() + 1).padStart(2, "0")}-${String(_pd.getDate()).padStart(2, "0")}.pdf`;
pdf.save(filename);
}
};

View File

@ -100,36 +100,37 @@ export function getQuickDateRange(range: "today" | "week" | "month" | "year"): {
} {
const now = new Date();
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const fmtDate = (d: Date) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
switch (range) {
case "today":
return {
startDate: today.toISOString().split("T")[0],
endDate: today.toISOString().split("T")[0],
startDate: fmtDate(today),
endDate: fmtDate(today),
};
case "week": {
const weekStart = new Date(today);
weekStart.setDate(today.getDate() - today.getDay()); // 일요일부터
weekStart.setDate(today.getDate() - today.getDay());
return {
startDate: weekStart.toISOString().split("T")[0],
endDate: today.toISOString().split("T")[0],
startDate: fmtDate(weekStart),
endDate: fmtDate(today),
};
}
case "month": {
const monthStart = new Date(today.getFullYear(), today.getMonth(), 1);
return {
startDate: monthStart.toISOString().split("T")[0],
endDate: today.toISOString().split("T")[0],
startDate: fmtDate(monthStart),
endDate: fmtDate(today),
};
}
case "year": {
const yearStart = new Date(today.getFullYear(), 0, 1);
return {
startDate: yearStart.toISOString().split("T")[0],
endDate: today.toISOString().split("T")[0],
startDate: fmtDate(yearStart),
endDate: fmtDate(today),
};
}

View File

@ -217,7 +217,8 @@ export function DashboardViewer({
if (format === "png") {
console.log("💾 PNG 다운로드 시작...");
const link = document.createElement("a");
const filename = `${dashboardTitle || "dashboard"}_${new Date().toISOString().split("T")[0]}.png`;
const _dvd = new Date();
const filename = `${dashboardTitle || "dashboard"}_${_dvd.getFullYear()}-${String(_dvd.getMonth() + 1).padStart(2, "0")}-${String(_dvd.getDate()).padStart(2, "0")}.png`;
link.download = filename;
link.href = dataUrl;
document.body.appendChild(link);
@ -253,7 +254,8 @@ export function DashboardViewer({
});
pdf.addImage(dataUrl, "PNG", 0, 0, imgWidth, imgHeight);
const filename = `${dashboardTitle || "dashboard"}_${new Date().toISOString().split("T")[0]}.pdf`;
const _dvp = new Date();
const filename = `${dashboardTitle || "dashboard"}_${_dvp.getFullYear()}-${String(_dvp.getMonth() + 1).padStart(2, "0")}-${String(_dvp.getDate()).padStart(2, "0")}.pdf`;
pdf.save(filename);
console.log("✅ PDF 다운로드 완료:", filename);
}

View File

@ -61,7 +61,8 @@ export default function DeliveryTodayStatsWidget({ element }: DeliveryTodayStats
// 데이터 처리
if (result.success && result.data?.rows) {
const rows = result.data.rows;
const today = new Date().toISOString().split("T")[0];
const _td = new Date();
const today = `${_td.getFullYear()}-${String(_td.getMonth() + 1).padStart(2, "0")}-${String(_td.getDate()).padStart(2, "0")}`;
// 오늘 발송 건수 (created_at 기준)
const shippedToday = rows.filter((row: any) => {

View File

@ -101,7 +101,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
const [routePoints, setRoutePoints] = useState<RoutePoint[]>([]);
const [selectedUserId, setSelectedUserId] = useState<string | null>(null);
const [routeLoading, setRouteLoading] = useState(false);
const [routeDate, setRouteDate] = useState<string>(new Date().toISOString().split("T")[0]); // YYYY-MM-DD 형식
const [routeDate, setRouteDate] = useState<string>(() => { const d = new Date(); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`; });
// 공차/운행 정보 상태
const [tripInfo, setTripInfo] = useState<Record<string, any>>({});

View File

@ -1120,7 +1120,8 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
const blob = new Blob([response.data], {
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
});
const timestamp = new Date().toISOString().slice(0, 10);
const _rpd = new Date();
const timestamp = `${_rpd.getFullYear()}-${String(_rpd.getMonth() + 1).padStart(2, "0")}-${String(_rpd.getDate()).padStart(2, "0")}`;
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;

View File

@ -86,13 +86,16 @@ export const EnhancedInteractiveScreenViewer: React.FC<EnhancedInteractiveScreen
const generateAutoValue = useCallback(
async (autoValueType: string, ruleId?: string): Promise<string> => {
const now = new Date();
const pad = (n: number) => String(n).padStart(2, "0");
const localDate = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
const localTime = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
switch (autoValueType) {
case "current_datetime":
return now.toISOString().slice(0, 19).replace("T", " ");
return `${localDate} ${localTime}`;
case "current_date":
return now.toISOString().slice(0, 10);
return localDate;
case "current_time":
return now.toTimeString().slice(0, 8);
return localTime;
case "current_user":
return userName || "사용자";
case "uuid":

View File

@ -1155,13 +1155,16 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
const generateAutoValue = useCallback(
(autoValueType: string): string => {
const now = new Date();
const pad = (n: number) => String(n).padStart(2, "0");
const localDate = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
const localTime = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
switch (autoValueType) {
case "current_datetime":
return now.toISOString().slice(0, 19); // YYYY-MM-DDTHH:mm:ss
return `${localDate} ${localTime}`;
case "current_date":
return now.toISOString().slice(0, 10); // YYYY-MM-DD
return localDate;
case "current_time":
return now.toTimeString().slice(0, 8); // HH:mm:ss
return localTime;
case "current_user":
return currentUser?.userName || currentUser?.userId || "unknown_user";
case "uuid":

View File

@ -357,13 +357,16 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
// 자동값 생성 함수
const generateAutoValue = useCallback(async (autoValueType: string, ruleId?: string): Promise<string> => {
const now = new Date();
const pad = (n: number) => String(n).padStart(2, "0");
const localDate = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
const localTime = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
switch (autoValueType) {
case "current_datetime":
return now.toISOString().slice(0, 19).replace("T", " "); // YYYY-MM-DD HH:mm:ss
return `${localDate} ${localTime}`;
case "current_date":
return now.toISOString().slice(0, 10); // YYYY-MM-DD
return localDate;
case "current_time":
return now.toTimeString().slice(0, 8); // HH:mm:ss
return localTime;
case "current_user":
// 실제 접속중인 사용자명 사용
return userName || "사용자"; // 사용자명이 없으면 기본값

View File

@ -183,13 +183,16 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
const generateAutoValue = useCallback(
(autoValueType: string): string => {
const now = new Date();
const pad = (n: number) => String(n).padStart(2, "0");
const localDate = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
const localTime = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
switch (autoValueType) {
case "current_datetime":
return now.toISOString().slice(0, 19).replace("T", " ");
return `${localDate} ${localTime}`;
case "current_date":
return now.toISOString().slice(0, 10);
return localDate;
case "current_time":
return now.toTimeString().slice(0, 8);
return localTime;
case "current_user":
return userName || "사용자";
case "uuid":

View File

@ -3852,7 +3852,6 @@ function ControlManagementTab({
openModalWithData: "데이터+모달",
openRelatedModal: "연관모달",
transferData: "데이터전달",
quickInsert: "즉시저장",
control: "제어흐름",
view_table_history: "이력보기",
excel_download: "엑셀다운",

View File

@ -56,9 +56,11 @@ export const DateConfigPanel: React.FC<WebTypeConfigPanelProps> = ({
// 현재 날짜 설정
const setCurrentDate = (field: "minDate" | "maxDate" | "defaultValue") => {
const now = new Date();
const pad = (n: number) => String(n).padStart(2, "0");
const d = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
const dateString = localConfig.showTime
? now.toISOString().slice(0, 16) // YYYY-MM-DDTHH:mm
: now.toISOString().slice(0, 10); // YYYY-MM-DD
? `${d}T${pad(now.getHours())}:${pad(now.getMinutes())}`
: d;
updateConfig(field, dateString);
};

View File

@ -263,7 +263,6 @@ export const BasicTab: React.FC<ButtonTabProps> = ({
</SelectItem>
{/* 고급 기능 */}
<SelectItem value="quickInsert"> </SelectItem>
<SelectItem value="control"> </SelectItem>
<SelectItem value="approval"> </SelectItem>
@ -271,9 +270,6 @@ export const BasicTab: React.FC<ButtonTabProps> = ({
<SelectItem value="barcode_scan"> </SelectItem>
<SelectItem value="operation_control"> </SelectItem>
{/* 이벤트 버스 */}
<SelectItem value="event"> </SelectItem>
{/* 복사 */}
<SelectItem value="copy"> ( )</SelectItem>

View File

@ -1018,7 +1018,8 @@ export function FlowWidget({
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Data");
const fileName = `${flowName || "flow"}_data_${new Date().toISOString().split("T")[0]}.xlsx`;
const _fxd = new Date();
const fileName = `${flowName || "flow"}_data_${_fxd.getFullYear()}-${String(_fxd.getMonth() + 1).padStart(2, "0")}-${String(_fxd.getDate()).padStart(2, "0")}.xlsx`;
XLSX.writeFile(wb, fileName);
toast.success(`${exportData.length}개 행이 Excel로 내보내기 되었습니다.`);
@ -1183,7 +1184,8 @@ export function FlowWidget({
}
}
const fileName = `${flowName || "flow"}_data_${new Date().toISOString().split("T")[0]}.pdf`;
const _fpd = new Date();
const fileName = `${flowName || "flow"}_data_${_fpd.getFullYear()}-${String(_fpd.getMonth() + 1).padStart(2, "0")}-${String(_fpd.getDate()).padStart(2, "0")}.pdf`;
doc.save(fileName);
toast.success(`${exportData.length}개 행이 PDF로 내보내기 되었습니다.`, { id: "pdf-export" });

View File

@ -52,8 +52,10 @@ export const DateWidget: React.FC<WebTypeComponentProps> = ({ component, value,
const getDefaultValue = (): string => {
if (config?.defaultValue === "current") {
const now = new Date();
if (isDatetime) return now.toISOString().slice(0, 16);
return now.toISOString().slice(0, 10);
const pad = (n: number) => String(n).padStart(2, "0");
const d = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
if (isDatetime) return `${d}T${pad(now.getHours())}:${pad(now.getMinutes())}`;
return d;
}
return "";
};

View File

@ -680,11 +680,15 @@ export const UnifiedRepeater: React.FC<UnifiedRepeaterProps> = ({
const now = new Date();
switch (col.autoFill.type) {
case "currentDate":
return now.toISOString().split("T")[0]; // YYYY-MM-DD
case "currentDate": {
const pad = (n: number) => String(n).padStart(2, "0");
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
}
case "currentDateTime":
return now.toISOString().slice(0, 19).replace("T", " "); // YYYY-MM-DD HH:mm:ss
case "currentDateTime": {
const pad = (n: number) => String(n).padStart(2, "0");
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
}
case "sequence":
return rowIndex + 1; // 1부터 시작하는 순번

View File

@ -1041,12 +1041,15 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
const now = new Date();
const pad = (n: number) => String(n).padStart(2, "0");
const localDate = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
const localTime = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
switch (col.autoFill.type) {
case "currentDate":
return now.toISOString().split("T")[0]; // YYYY-MM-DD
return localDate;
case "currentDateTime":
return now.toISOString().slice(0, 19).replace("T", " "); // YYYY-MM-DD HH:mm:ss
return `${localDate} ${localTime}`;
case "sequence":
return rowIndex + 1; // 1부터 시작하는 순번

View File

@ -130,12 +130,6 @@ const ACTION_TYPE_CARDS = [
title: "엑셀 업로드",
description: "엑셀 파일을 올려요",
},
{
value: "quickInsert",
icon: Zap,
title: "즉시 저장",
description: "바로 저장해요",
},
{
value: "approval",
icon: Check,
@ -148,12 +142,6 @@ const ACTION_TYPE_CARDS = [
title: "제어 흐름",
description: "흐름을 제어해요",
},
{
value: "event",
icon: Send,
title: "이벤트 발송",
description: "이벤트를 보내요",
},
{
value: "copy",
icon: Copy,

View File

@ -56,10 +56,10 @@ export default function VehicleReport() {
// 일별 통계
const [dailyData, setDailyData] = useState<DailyStat[]>([]);
const [dailyStartDate, setDailyStartDate] = useState(
new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().split("T")[0]
(() => { const d = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`; })()
);
const [dailyEndDate, setDailyEndDate] = useState(
new Date().toISOString().split("T")[0]
(() => { const d = new Date(); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`; })()
);
const [dailyLoading, setDailyLoading] = useState(false);

View File

@ -53,7 +53,8 @@ export const DateInputComponent: React.FC<DateInputComponentProps> = ({
// 자동생성 로직
useEffect(() => {
if (finalAutoGeneration?.enabled) {
const today = new Date().toISOString().split("T")[0]; // YYYY-MM-DD
const n = new Date();
const today = `${n.getFullYear()}-${String(n.getMonth() + 1).padStart(2, "0")}-${String(n.getDate()).padStart(2, "0")}`;
setAutoGeneratedValue(today);
// 인터랙티브 모드에서 폼 데이터에도 설정

View File

@ -653,9 +653,9 @@ export function RepeaterTable({
if (typeof val === "string" && val.includes("T")) {
return val.split("T")[0];
}
// Date 객체이면 변환
// Date 객체이면 로컬 날짜로 변환
if (val instanceof Date) {
return val.toISOString().split("T")[0];
return `${val.getFullYear()}-${String(val.getMonth() + 1).padStart(2, "0")}-${String(val.getDate()).padStart(2, "0")}`;
}
return String(val);
};

View File

@ -448,7 +448,8 @@ export function SimpleRepeaterTableComponent({
} else if (col.type === "number") {
newRow[col.field] = 0;
} else if (col.type === "date") {
newRow[col.field] = new Date().toISOString().split("T")[0];
const _n = new Date();
newRow[col.field] = `${_n.getFullYear()}-${String(_n.getMonth() + 1).padStart(2, "0")}-${String(_n.getDate()).padStart(2, "0")}`;
} else {
newRow[col.field] = "";
}

View File

@ -2707,7 +2707,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
XLSX.utils.book_append_sheet(wb, ws, tableLabel || "데이터");
// 파일명 생성
const fileName = `${tableLabel || tableConfig.selectedTable || "export"}_${new Date().toISOString().split("T")[0]}.xlsx`;
const _en = new Date();
const fileName = `${tableLabel || tableConfig.selectedTable || "export"}_${_en.getFullYear()}-${String(_en.getMonth() + 1).padStart(2, "0")}-${String(_en.getDate()).padStart(2, "0")}.xlsx`;
// 파일 다운로드
XLSX.writeFile(wb, fileName);

View File

@ -3006,7 +3006,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
XLSX.utils.book_append_sheet(wb, ws, tableLabel || "데이터");
// 파일명 생성
const fileName = `${tableLabel || tableConfig.selectedTable || "export"}_${new Date().toISOString().split("T")[0]}.xlsx`;
const _en = new Date();
const fileName = `${tableLabel || tableConfig.selectedTable || "export"}_${_en.getFullYear()}-${String(_en.getMonth() + 1).padStart(2, "0")}-${String(_en.getDate()).padStart(2, "0")}.xlsx`;
// 파일 다운로드
XLSX.writeFile(wb, fileName);

View File

@ -227,7 +227,7 @@ export function TimelineSchedulerComponent({
if (onCellClick) {
onCellClick({
resourceId,
date: date.toISOString().split("T")[0],
date: `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`,
});
}
},
@ -343,7 +343,7 @@ export function TimelineSchedulerComponent({
if (onAddSchedule && effectiveResources.length > 0) {
onAddSchedule(
effectiveResources[0].id,
new Date().toISOString().split("T")[0]
(() => { const _n = new Date(); return `${_n.getFullYear()}-${String(_n.getMonth() + 1).padStart(2, "0")}-${String(_n.getDate()).padStart(2, "0")}`; })()
);
}
}, [onAddSchedule, effectiveResources]);
@ -383,7 +383,8 @@ export function TimelineSchedulerComponent({
const items = Array.from(grouped.entries()).map(([code, rows]) => {
const totalQty = rows.reduce((sum: number, r: any) => sum + (Number(r[qtyField]) || 0), 0);
const dates = rows.map((r: any) => r[dateField]).filter(Boolean).sort();
const earliestDate = dates[0] || new Date().toISOString().split("T")[0];
const _dn = new Date();
const earliestDate = dates[0] || `${_dn.getFullYear()}-${String(_dn.getMonth() + 1).padStart(2, "0")}-${String(_dn.getDate()).padStart(2, "0")}`;
const first = rows[0];
return {
item_code: code,

View File

@ -28,7 +28,7 @@ interface ItemTimelineCardProps {
onScheduleClick?: (schedule: ScheduleItem) => void;
}
const toDateString = (d: Date) => d.toISOString().split("T")[0];
const toDateString = (d: Date) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
const addDays = (d: Date, n: number) => {
const r = new Date(d);

View File

@ -13,7 +13,7 @@ const SCHEDULE_TABLE = "schedule_mng";
* ISO ( )
*/
const toDateString = (date: Date): string => {
return date.toISOString().split("T")[0];
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
};
/**

View File

@ -54,5 +54,5 @@ export function detectConflicts(schedules: ScheduleItem[]): Set<string> {
export function addDaysToDateString(dateStr: string, days: number): string {
const date = new Date(dateStr);
date.setDate(date.getDate() + days);
return date.toISOString().split("T")[0];
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
}

View File

@ -251,7 +251,7 @@ export function computeDateRange(
preset: DatePresetOption
): { preset: DatePresetOption; from: string; to: string } | null {
const now = new Date();
const fmt = (d: Date) => d.toISOString().split("T")[0];
const fmt = (d: Date) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
switch (preset) {
case "today":

View File

@ -349,7 +349,7 @@ export class EnhancedFormService {
if (lowerDataType.includes("date")) {
const date = new Date(value);
return isNaN(date.getTime()) ? null : date.toISOString().split("T")[0];
return isNaN(date.getTime()) ? null : `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
}
if (lowerDataType.includes("time")) {

View File

@ -1,6 +1,7 @@
"use client";
import { AutoGenerationType, AutoGenerationConfig } from "@/types/screen";
import { toLocalDate, toLocalTime, toLocalDateTime } from "@/lib/utils/localDate";
/**
*
@ -52,19 +53,19 @@ export class AutoGenerationUtils {
let result: string;
switch (format) {
case "date":
result = now.toISOString().split("T")[0]; // YYYY-MM-DD
result = toLocalDate(now);
break;
case "time":
result = now.toTimeString().split(" ")[0]; // HH:mm:ss
result = toLocalTime(now);
break;
case "datetime":
result = now.toISOString().replace("T", " ").split(".")[0]; // YYYY-MM-DD HH:mm:ss
result = toLocalDateTime(now);
break;
case "timestamp":
result = now.getTime().toString();
break;
default:
result = now.toISOString(); // ISO 8601 format
result = toLocalDateTime(now);
break;
}

View File

@ -5156,7 +5156,8 @@ export class ButtonActionExecutor {
const menuName = localStorage.getItem("currentMenuName");
if (menuName) defaultFileName = menuName;
}
const fileName = config.excelFileName || `${defaultFileName}_${new Date().toISOString().split("T")[0]}.xlsx`;
const _xd = new Date();
const fileName = config.excelFileName || `${defaultFileName}_${_xd.getFullYear()}-${String(_xd.getMonth() + 1).padStart(2, "0")}-${String(_xd.getDate()).padStart(2, "0")}.xlsx`;
const sheetName = config.excelSheetName || "Sheet1";
await exportToExcel(dataToExport, fileName, sheetName, true);
@ -5262,7 +5263,8 @@ export class ButtonActionExecutor {
}
}
const fileName = config.excelFileName || `${defaultFileName}_${new Date().toISOString().split("T")[0]}.xlsx`;
const _xd2 = new Date();
const fileName = config.excelFileName || `${defaultFileName}_${_xd2.getFullYear()}-${String(_xd2.getMonth() + 1).padStart(2, "0")}-${String(_xd2.getDate()).padStart(2, "0")}.xlsx`;
const sheetName = config.excelSheetName || "Sheet1";
const includeHeaders = config.excelIncludeHeaders !== false;

View File

@ -440,7 +440,7 @@ const validateDateField = (fieldName: string, value: any, config?: Record<string
}
}
return { isValid: true, transformedValue: dateValue.toISOString().split("T")[0] };
return { isValid: true, transformedValue: `${dateValue.getFullYear()}-${String(dateValue.getMonth() + 1).padStart(2, "0")}-${String(dateValue.getDate()).padStart(2, "0")}` };
};
/**

View File

@ -0,0 +1,30 @@
/**
* () /
* DB Asia/Seoul로
*
* toISOString() UTC를
*/
export function toLocalDate(date: Date = new Date()): string {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, "0");
const d = String(date.getDate()).padStart(2, "0");
return `${y}-${m}-${d}`;
}
export function toLocalTime(date: Date = new Date()): string {
const h = String(date.getHours()).padStart(2, "0");
const min = String(date.getMinutes()).padStart(2, "0");
const sec = String(date.getSeconds()).padStart(2, "0");
return `${h}:${min}:${sec}`;
}
export function toLocalDateTime(date: Date = new Date()): string {
return `${toLocalDate(date)} ${toLocalTime(date)}`;
}
export function toLocalDateTimeForInput(date: Date = new Date()): string {
const h = String(date.getHours()).padStart(2, "0");
const min = String(date.getMinutes()).padStart(2, "0");
return `${toLocalDate(date)}T${h}:${min}`;
}

View File

@ -91,8 +91,8 @@ function getDefaultPeriod(): { start: string; end: string } {
const start = new Date(now.getFullYear(), now.getMonth(), 1);
const end = new Date(now.getFullYear(), now.getMonth() + 1, 0);
return {
start: start.toISOString().split("T")[0],
end: end.toISOString().split("T")[0],
start: `${start.getFullYear()}-${String(start.getMonth() + 1).padStart(2, "0")}-${String(start.getDate()).padStart(2, "0")}`,
end: `${end.getFullYear()}-${String(end.getMonth() + 1).padStart(2, "0")}-${String(end.getDate()).padStart(2, "0")}`,
};
}