날짜포멧 수정
This commit is contained in:
@@ -9438,7 +9438,8 @@ html.theme-blue {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
max-width: 360px;
|
||||
max-width: 420px;
|
||||
width: 100%;
|
||||
}
|
||||
.stg-chart-time-format-custom {
|
||||
width: 100%;
|
||||
|
||||
+24
-5
@@ -21,6 +21,10 @@ import {
|
||||
setChartTimeFormat,
|
||||
useChartTimeFormat,
|
||||
} from './utils/chartTimeFormat';
|
||||
import {
|
||||
setTradeAlertTimeFormat,
|
||||
useTradeAlertTimeFormat,
|
||||
} from './utils/tradeAlertTimeFormat';
|
||||
import { normalizeTimezone, setDisplayTimezone, useDisplayTimezone } from './utils/timezone';
|
||||
import { calcStats, type PriceStats } from './utils/calculations';
|
||||
import { getKoreanName } from './utils/marketNameCache';
|
||||
@@ -192,6 +196,7 @@ function App() {
|
||||
const { permissions: menuPermissions, isLoaded: menuPermsLoaded, can: canMenu } = useMenuPermissions(sessionKey);
|
||||
const displayTimezone = useDisplayTimezone();
|
||||
const chartTimeFormat = useChartTimeFormat();
|
||||
const tradeAlertTimeFormat = useTradeAlertTimeFormat();
|
||||
|
||||
// DB 로드 전에는 localStorage 기본값, 로드 후 DB 값으로 오버라이드됨
|
||||
const [symbol, setSymbol] = useState(initial.symbol);
|
||||
@@ -266,13 +271,24 @@ function App() {
|
||||
managerRef.current?.setDisplayTimezone(next, tf, chartTimeFormat);
|
||||
}, [saveAppDef, layoutDef.count, activeSlotTf, timeframe, chartTimeFormat]);
|
||||
|
||||
const applyChartTimeFormatToManagers = useCallback((next: string) => {
|
||||
const tf = layoutDef.count > 1 ? activeSlotTf : timeframe;
|
||||
managerRef.current?.setChartTimeFormat(next);
|
||||
slotRefs.current.forEach(slot => slot?.setChartTimeFormat?.(next, tf));
|
||||
}, [layoutDef.count, activeSlotTf, timeframe]);
|
||||
|
||||
const handleChartTimeFormatChange = useCallback((format: string) => {
|
||||
const next = normalizeChartTimeFormat(format);
|
||||
setChartTimeFormat(next);
|
||||
saveAppDef({ chartTimeFormat: next });
|
||||
const tf = layoutDef.count > 1 ? activeSlotTf : timeframe;
|
||||
managerRef.current?.setDisplayTimezone(displayTimezone, tf, next);
|
||||
}, [saveAppDef, layoutDef.count, activeSlotTf, timeframe, displayTimezone]);
|
||||
applyChartTimeFormatToManagers(next);
|
||||
}, [saveAppDef, applyChartTimeFormatToManagers]);
|
||||
|
||||
const handleTradeAlertTimeFormatChange = useCallback((format: string) => {
|
||||
const next = normalizeChartTimeFormat(format);
|
||||
setTradeAlertTimeFormat(next);
|
||||
saveAppDef({ tradeAlertTimeFormat: next });
|
||||
}, [saveAppDef]);
|
||||
|
||||
const chartSymbol = layoutDef.count > 1 ? activeSlotSymbol : symbol;
|
||||
const { orderbook, wsStatus: obWsStatus, spread } = useUpbitOrderbook(chartSymbol);
|
||||
@@ -975,8 +991,9 @@ function App() {
|
||||
useEffect(() => {
|
||||
if (!appSettingsLoaded) return;
|
||||
const tf = layoutDef.count > 1 ? activeSlotTf : timeframe;
|
||||
managerRef.current?.setDisplayTimezone(displayTimezone, tf, chartTimeFormat);
|
||||
}, [appSettingsLoaded, displayTimezone, chartTimeFormat, timeframe, activeSlotTf, layoutDef.count]);
|
||||
managerRef.current?.setChartTimeFormat(chartTimeFormat);
|
||||
slotRefs.current.forEach(slot => slot?.setChartTimeFormat?.(chartTimeFormat, tf));
|
||||
}, [appSettingsLoaded, chartTimeFormat, timeframe, activeSlotTf, layoutDef.count]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!appSettingsLoaded) return;
|
||||
@@ -1910,6 +1927,8 @@ function App() {
|
||||
onDisplayTimezoneChange={handleTimezoneChange}
|
||||
chartTimeFormat={chartTimeFormat}
|
||||
onChartTimeFormatChange={handleChartTimeFormatChange}
|
||||
tradeAlertTimeFormat={tradeAlertTimeFormat}
|
||||
onTradeAlertTimeFormatChange={handleTradeAlertTimeFormatChange}
|
||||
verificationIssueNotify={appDefaults.verificationIssueNotify}
|
||||
onVerificationIssueNotify={v => saveAppDef({ verificationIssueNotify: v })}
|
||||
onFcmTest={async () => {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import type { ChartManager } from '../utils/ChartManager';
|
||||
import { subscribeChartTimeFormat } from '../utils/chartTimeFormat';
|
||||
|
||||
const AXIS_HEIGHT = 22;
|
||||
|
||||
@@ -26,12 +27,14 @@ const CandlePaneTimeAxis: React.FC<CandlePaneTimeAxisProps> = ({ manager, contai
|
||||
|
||||
const unsubLayout = manager.subscribePaneLayout(sync);
|
||||
const unsubRange = manager.subscribeViewport(sync);
|
||||
const unsubFormat = subscribeChartTimeFormat(sync);
|
||||
|
||||
const timers = [0, 50, 150, 400].map(ms => window.setTimeout(sync, ms));
|
||||
return () => {
|
||||
ro.disconnect();
|
||||
unsubLayout();
|
||||
unsubRange();
|
||||
unsubFormat();
|
||||
timers.forEach(clearTimeout);
|
||||
};
|
||||
}, [manager, containerEl, sync]);
|
||||
|
||||
@@ -116,6 +116,8 @@ export interface ChartSlotHandle {
|
||||
/** 거래량 pane on/off */
|
||||
setVolumeVisible: (visible: boolean) => void;
|
||||
setPaneSeparatorOptions: (opts: ChartPaneSeparatorOptions) => void;
|
||||
/** 차트 시간축·크로스헤어 날짜 형식 */
|
||||
setChartTimeFormat: (format: string, timeframe?: Timeframe) => void;
|
||||
}
|
||||
|
||||
// ── Props ─────────────────────────────────────────────────────────────────
|
||||
@@ -323,7 +325,11 @@ const ChartSlot = forwardRef<ChartSlotHandle, ChartSlotProps>(function ChartSlot
|
||||
setPaneSeparatorOptions: (opts: ChartPaneSeparatorOptions) => {
|
||||
managerRef.current?.setPaneSeparatorOptions(opts);
|
||||
},
|
||||
}), []);
|
||||
setChartTimeFormat: (format: string, tf?: Timeframe) => {
|
||||
managerRef.current?.setChartTimeFormat(format);
|
||||
if (tf) managerRef.current?.setDisplayTimezone(displayTimezone ?? 'Asia/Seoul', tf, format);
|
||||
},
|
||||
}), [displayTimezone]);
|
||||
|
||||
// 자석모드 변경 → ChartManager 크로스헤어 모드 즉시 반영
|
||||
useEffect(() => {
|
||||
|
||||
@@ -71,7 +71,7 @@ const ChartTimeFormatPicker: React.FC<ChartTimeFormatPickerProps> = ({ value, on
|
||||
<input
|
||||
type="text"
|
||||
className="stg-input stg-chart-time-format-custom"
|
||||
placeholder="예: yyyy-MM-dd HH:mm"
|
||||
placeholder="예: yyyy-MM-dd HH:mm:ss, dd/MM/yyyy HH:mm"
|
||||
value={customText}
|
||||
onChange={e => setCustomText(e.target.value)}
|
||||
onBlur={commitCustom}
|
||||
|
||||
@@ -16,6 +16,7 @@ import IndicatorMainDefaultsPanel, {
|
||||
import type { IndicatorConfig } from '../types';
|
||||
import type { PlotDef, HLineDef } from '../utils/indicatorRegistry';
|
||||
import type { IchimokuCloudColors } from '../utils/ichimokuConfig';
|
||||
import ChartTimeFormatPicker from './ChartTimeFormatPicker';
|
||||
import {
|
||||
TRADE_ALERT_SOUND_OPTIONS,
|
||||
normalizeTradeAlertSoundId,
|
||||
@@ -43,7 +44,6 @@ import {
|
||||
LINE_WIDTH_OPTIONS,
|
||||
} from '../utils/plotColorUtils';
|
||||
import TimezonePicker from './TimezonePicker';
|
||||
import ChartTimeFormatPicker from './ChartTimeFormatPicker';
|
||||
import AdminPasswordGate from './AdminPasswordGate';
|
||||
import AdminSettingsPanel from './AdminSettingsPanel';
|
||||
import {
|
||||
@@ -155,6 +155,8 @@ interface SettingsPageProps {
|
||||
onDisplayTimezoneChange?: (tz: string) => void;
|
||||
chartTimeFormat?: string;
|
||||
onChartTimeFormatChange?: (format: string) => void;
|
||||
tradeAlertTimeFormat?: string;
|
||||
onTradeAlertTimeFormatChange?: (format: string) => void;
|
||||
menuPermissions?: Record<string, boolean>;
|
||||
verificationIssueNotify?: boolean;
|
||||
onVerificationIssueNotify?: (v: boolean) => void;
|
||||
@@ -550,18 +552,25 @@ const PaperPanel: React.FC<PaperPanelProps> = ({
|
||||
const GeneralPanel: React.FC<{
|
||||
displayTimezone: string;
|
||||
onDisplayTimezoneChange: (tz: string) => void;
|
||||
chartTimeFormat?: string;
|
||||
onChartTimeFormatChange?: (format: string) => void;
|
||||
tradeAlertTimeFormat?: string;
|
||||
onTradeAlertTimeFormatChange?: (format: string) => void;
|
||||
verificationIssueNotify?: boolean;
|
||||
onVerificationIssueNotify?: (v: boolean) => void;
|
||||
showVerificationNotify?: boolean;
|
||||
}> = ({
|
||||
displayTimezone,
|
||||
onDisplayTimezoneChange,
|
||||
chartTimeFormat = 'MM-dd HH:mm',
|
||||
onChartTimeFormatChange,
|
||||
tradeAlertTimeFormat = 'MM-dd HH:mm',
|
||||
onTradeAlertTimeFormatChange,
|
||||
verificationIssueNotify = true,
|
||||
onVerificationIssueNotify,
|
||||
showVerificationNotify = true,
|
||||
}) => {
|
||||
const [lang, setLang] = useState('ko');
|
||||
const [dateFormat, setDateFmt] = useState('YYYY-MM-DD');
|
||||
const [lang, setLang] = useState('ko');
|
||||
const [startPage, setStart] = useState('chart');
|
||||
const [animations, setAnim] = useState(true);
|
||||
const [tooltips, setTool] = useState(true);
|
||||
@@ -576,12 +585,23 @@ const GeneralPanel: React.FC<{
|
||||
<option value="ja">日本語</option>
|
||||
</select>
|
||||
</SettingRow>
|
||||
<SettingRow label="날짜 형식" desc="차트 및 UI에서 날짜를 표시하는 방식입니다.">
|
||||
<select className="stg-select" value={dateFormat} onChange={e => setDateFmt(e.target.value)}>
|
||||
<option value="YYYY-MM-DD">YYYY-MM-DD</option>
|
||||
<option value="DD/MM/YYYY">DD/MM/YYYY</option>
|
||||
<option value="MM/DD/YYYY">MM/DD/YYYY</option>
|
||||
</select>
|
||||
<SettingRow
|
||||
label="날짜·시간 형식"
|
||||
desc="캔들·보조지표 차트 하단 시간축·크로스헤어에 적용됩니다. 프리셋 선택 또는 직접 입력(토큰: yyyy, yy, MM, dd, HH, mm, ss)이 가능합니다."
|
||||
>
|
||||
<ChartTimeFormatPicker
|
||||
value={chartTimeFormat}
|
||||
onChange={fmt => onChartTimeFormatChange?.(fmt)}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingRow
|
||||
label="매매 시그널 알림 시간 형식"
|
||||
desc="매매 시그널 알림 팝업·상세에 표시되는 캔들·수신 시각 형식입니다. 프리셋 선택 또는 직접 입력이 가능합니다."
|
||||
>
|
||||
<ChartTimeFormatPicker
|
||||
value={tradeAlertTimeFormat}
|
||||
onChange={fmt => onTradeAlertTimeFormatChange?.(fmt)}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingRow label="시간대" desc="차트·하단 시계·알림에 사용되는 표시 시간대입니다.">
|
||||
<TimezonePicker
|
||||
@@ -1598,6 +1618,8 @@ const SettingsPage: React.FC<SettingsPageProps> = ({
|
||||
onDisplayTimezoneChange,
|
||||
chartTimeFormat = 'MM-dd HH:mm',
|
||||
onChartTimeFormatChange,
|
||||
tradeAlertTimeFormat = 'MM-dd HH:mm',
|
||||
onTradeAlertTimeFormatChange,
|
||||
menuPermissions,
|
||||
verificationIssueNotify = true,
|
||||
onVerificationIssueNotify,
|
||||
@@ -1688,6 +1710,10 @@ const SettingsPage: React.FC<SettingsPageProps> = ({
|
||||
<GeneralPanel
|
||||
displayTimezone={displayTimezone}
|
||||
onDisplayTimezoneChange={onDisplayTimezoneChange ?? (() => {})}
|
||||
chartTimeFormat={chartTimeFormat}
|
||||
onChartTimeFormatChange={onChartTimeFormatChange}
|
||||
tradeAlertTimeFormat={tradeAlertTimeFormat}
|
||||
onTradeAlertTimeFormatChange={onTradeAlertTimeFormatChange}
|
||||
verificationIssueNotify={verificationIssueNotify}
|
||||
onVerificationIssueNotify={onVerificationIssueNotify}
|
||||
showVerificationNotify={menuPermissions == null || menuPermissions['verification-board'] === true}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useDraggablePanel } from '../hooks/useDraggablePanel';
|
||||
import { placePaperOrder, placeLiveOrder, loadLiveSummary } from '../utils/backendApi';
|
||||
import { formatUnixDateTime } from '../utils/timezone';
|
||||
import { formatSignalTime } from '../utils/tradeSignalDisplay';
|
||||
import { useTradeAlertTimeFormat } from '../utils/tradeAlertTimeFormat';
|
||||
|
||||
export interface TradeSignalInfo {
|
||||
market: string;
|
||||
@@ -30,9 +31,6 @@ interface Props {
|
||||
const fmt = (n: number): string =>
|
||||
n > 0 ? n.toLocaleString('ko-KR', { maximumFractionDigits: 0 }) : '0';
|
||||
|
||||
function fmtTime(unix: number): string {
|
||||
return formatUnixDateTime(unix);
|
||||
}
|
||||
|
||||
function execLabel(type?: string, candleType?: string): string {
|
||||
const base = type === 'REALTIME_TICK' ? '실시간 틱' : '봉 마감';
|
||||
@@ -61,6 +59,7 @@ export const TradeAlertModal: React.FC<Props> = ({
|
||||
paperAutoTradeBudgetPct = 95,
|
||||
onOrderDone,
|
||||
}) => {
|
||||
useTradeAlertTimeFormat();
|
||||
const isBuy = signal.signalType === 'BUY';
|
||||
const accentColor = isBuy ? '#3f7ef5' : '#ef5350';
|
||||
const accentGlow = isBuy ? 'rgba(63,126,245,0.22)' : 'rgba(239,83,80,0.22)';
|
||||
@@ -197,7 +196,7 @@ export const TradeAlertModal: React.FC<Props> = ({
|
||||
</span>
|
||||
</div>
|
||||
<div className="tam-price-time">
|
||||
발생 시각: {fmtTime(signal.candleTime)}
|
||||
발생 시각: {formatSignalTime(signal.candleTime)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
getSignalTypeKo,
|
||||
type TradeSignalDisplaySource,
|
||||
} from '../utils/tradeSignalDisplay';
|
||||
import { useTradeAlertTimeFormat } from '../utils/tradeAlertTimeFormat';
|
||||
|
||||
interface Props {
|
||||
item: TradeSignalDisplaySource;
|
||||
@@ -22,6 +23,7 @@ export const TradeSignalDetailBody: React.FC<Props> = ({
|
||||
variant = 'compact',
|
||||
showHeadline = true,
|
||||
}) => {
|
||||
useTradeAlertTimeFormat();
|
||||
const isBuy = item.signalType === 'BUY';
|
||||
const rows = buildSignalDetailRows(item);
|
||||
|
||||
|
||||
@@ -1026,6 +1026,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
managerRef.current?.setDisplayTimezone(displayTimezone, timeframe, chartTimeFormat);
|
||||
managerRef.current?.setChartTimeFormat(chartTimeFormat);
|
||||
}, [displayTimezone, timeframe, chartTimeFormat]);
|
||||
|
||||
// 종목·타임프레임 변경 시 barsKey 캐시만 초기화 (reloadAll 은 bars effect 에서 1회만)
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
getMarketDisplayLine,
|
||||
getSignalTypeKo,
|
||||
} from '../../utils/tradeSignalDisplay';
|
||||
import { useTradeAlertTimeFormat } from '../../utils/tradeAlertTimeFormat';
|
||||
import {
|
||||
buildStrategyChartIndicators,
|
||||
candleTypeToTimeframe,
|
||||
@@ -66,6 +67,7 @@ const TradeNotificationListRow: React.FC<Props> = ({
|
||||
onDetail,
|
||||
onGoToChart,
|
||||
}) => {
|
||||
useTradeAlertTimeFormat();
|
||||
const isBuy = item.signalType === 'BUY';
|
||||
const allDetailRows = buildSignalDetailRows(item);
|
||||
const metaRows = allDetailRows.filter(
|
||||
|
||||
@@ -41,6 +41,10 @@ import {
|
||||
normalizeChartTimeFormat,
|
||||
setChartTimeFormat,
|
||||
} from '../utils/chartTimeFormat';
|
||||
import {
|
||||
DEFAULT_TRADE_ALERT_TIME_FORMAT,
|
||||
setTradeAlertTimeFormat,
|
||||
} from '../utils/tradeAlertTimeFormat';
|
||||
import { DEFAULT_DISPLAY_TIMEZONE, normalizeTimezone, setDisplayTimezone } from '../utils/timezone';
|
||||
import {
|
||||
clampVirtualTargetMax,
|
||||
@@ -176,6 +180,7 @@ export function resolveAppDefaults(s: AppSettingsDto) {
|
||||
fcmPushEnabled: s.fcmPushEnabled ?? false,
|
||||
displayTimezone: normalizeTimezone(s.displayTimezone ?? DEFAULT_DISPLAY_TIMEZONE),
|
||||
chartTimeFormat: normalizeChartTimeFormat(s.chartTimeFormat ?? DEFAULT_CHART_TIME_FORMAT),
|
||||
tradeAlertTimeFormat: normalizeChartTimeFormat(s.tradeAlertTimeFormat ?? DEFAULT_TRADE_ALERT_TIME_FORMAT),
|
||||
trendSearchSettings: resolveTrendSearchAppSettings(
|
||||
s.trendSearchSettings as Partial<TrendSearchAppSettings> | null | undefined,
|
||||
),
|
||||
@@ -201,6 +206,7 @@ export function useAppSettings(sessionKey = 0) {
|
||||
setSettings(_cache);
|
||||
setDisplayTimezone(normalizeTimezone(_cache.displayTimezone ?? DEFAULT_DISPLAY_TIMEZONE));
|
||||
setChartTimeFormat(normalizeChartTimeFormat(_cache.chartTimeFormat ?? DEFAULT_CHART_TIME_FORMAT));
|
||||
setTradeAlertTimeFormat(normalizeChartTimeFormat(_cache.tradeAlertTimeFormat ?? DEFAULT_TRADE_ALERT_TIME_FORMAT));
|
||||
setIsLoaded(true);
|
||||
} else {
|
||||
setIsLoaded(false);
|
||||
@@ -210,6 +216,7 @@ export function useAppSettings(sessionKey = 0) {
|
||||
setSettings(_cache);
|
||||
setDisplayTimezone(normalizeTimezone(_cache.displayTimezone ?? DEFAULT_DISPLAY_TIMEZONE));
|
||||
setChartTimeFormat(normalizeChartTimeFormat(_cache.chartTimeFormat ?? DEFAULT_CHART_TIME_FORMAT));
|
||||
setTradeAlertTimeFormat(normalizeChartTimeFormat(_cache.tradeAlertTimeFormat ?? DEFAULT_TRADE_ALERT_TIME_FORMAT));
|
||||
setIsLoaded(true);
|
||||
} else {
|
||||
setIsLoaded(false);
|
||||
@@ -220,6 +227,7 @@ export function useAppSettings(sessionKey = 0) {
|
||||
setSettings(data);
|
||||
setDisplayTimezone(normalizeTimezone(data.displayTimezone ?? DEFAULT_DISPLAY_TIMEZONE));
|
||||
setChartTimeFormat(normalizeChartTimeFormat(data.chartTimeFormat ?? DEFAULT_CHART_TIME_FORMAT));
|
||||
setTradeAlertTimeFormat(normalizeChartTimeFormat(data.tradeAlertTimeFormat ?? DEFAULT_TRADE_ALERT_TIME_FORMAT));
|
||||
setIsLoaded(true);
|
||||
}
|
||||
}).catch(err => {
|
||||
@@ -250,6 +258,9 @@ export function useAppSettings(sessionKey = 0) {
|
||||
if (patch.chartTimeFormat != null) {
|
||||
setChartTimeFormat(normalizeChartTimeFormat(patch.chartTimeFormat));
|
||||
}
|
||||
if (patch.tradeAlertTimeFormat != null) {
|
||||
setTradeAlertTimeFormat(normalizeChartTimeFormat(patch.tradeAlertTimeFormat));
|
||||
}
|
||||
saveAppSettings(patch).then(updated => {
|
||||
if (updated) {
|
||||
_cache = { ...(_cache ?? {}), ...updated };
|
||||
|
||||
@@ -503,6 +503,12 @@ export class ChartManager {
|
||||
this._applyDisplayTimezoneOptions();
|
||||
}
|
||||
|
||||
/** 시간축·크로스헤어·캔들 pane 시간 라벨 포맷만 갱신 */
|
||||
setChartTimeFormat(chartTimeFormat: string): void {
|
||||
this.chartTimeFormat = normalizeChartTimeFormat(chartTimeFormat);
|
||||
this._applyDisplayTimezoneOptions();
|
||||
}
|
||||
|
||||
// ─── Theme ──────────────────────────────────────────────────────────────
|
||||
setTheme(theme: Theme): void {
|
||||
this.currentTheme = theme;
|
||||
|
||||
@@ -446,6 +446,8 @@ export interface AppSettingsDto {
|
||||
displayTimezone?: string;
|
||||
/** 차트 하단 시간축 포맷 (예: yyyy-MM-dd HH:mm) */
|
||||
chartTimeFormat?: string;
|
||||
/** 매매 시그널 알림 팝업 시간 포맷 */
|
||||
tradeAlertTimeFormat?: string;
|
||||
/** 추세검색 기본 설정 */
|
||||
trendSearchSettings?: import('./trendSearchAppSettings').TrendSearchAppSettings | null;
|
||||
/** UI 설정 통합 (편집기·팔레트·가상투자 목록·패널 크기 등) */
|
||||
|
||||
@@ -13,15 +13,25 @@ export interface ChartTimeFormatPreset {
|
||||
label: string;
|
||||
}
|
||||
|
||||
/** 통상적인 차트 시간축 프리셋 */
|
||||
/** 통상적인 차트 시간축 프리셋 (일반설정·차트 설정 공통) */
|
||||
export const CHART_TIME_FORMAT_PRESETS: ChartTimeFormatPreset[] = [
|
||||
{ id: 'yyyy-MM-dd HH:mm:ss', label: 'yyyy-MM-dd HH:mm:ss' },
|
||||
{ id: 'yyyy-MM-dd HH:mm', label: 'yyyy-MM-dd HH:mm' },
|
||||
{ id: 'yyyy-MM-dd', label: 'yyyy-MM-dd' },
|
||||
{ id: 'yyyy/MM/dd HH:mm:ss', label: 'yyyy/MM/dd HH:mm:ss' },
|
||||
{ id: 'yyyy/MM/dd HH:mm', label: 'yyyy/MM/dd HH:mm' },
|
||||
{ id: 'MM-dd HH:mm:ss', label: 'MM-dd HH:mm:ss' },
|
||||
{ id: 'MM-dd HH:mm', label: 'MM-dd HH:mm' },
|
||||
{ id: 'MM/dd/yyyy HH:mm:ss', label: 'MM/dd/yyyy HH:mm:ss' },
|
||||
{ id: 'MM/dd/yyyy HH:mm', label: 'MM/dd/yyyy HH:mm' },
|
||||
{ id: 'dd/MM/yyyy HH:mm:ss', label: 'dd/MM/yyyy HH:mm:ss' },
|
||||
{ id: 'dd/MM/yyyy HH:mm', label: 'dd/MM/yyyy HH:mm' },
|
||||
{ id: 'dd/MM HH:mm:ss', label: 'dd/MM HH:mm:ss' },
|
||||
{ id: 'dd/MM HH:mm', label: 'dd/MM HH:mm' },
|
||||
{ id: 'dd-MM HH:mm:ss', label: 'dd-MM HH:mm:ss' },
|
||||
{ id: 'dd-MM HH:mm', label: 'dd-MM HH:mm' },
|
||||
{ id: 'dd.MM.yyyy HH:mm:ss', label: 'dd.MM.yyyy HH:mm:ss' },
|
||||
{ id: 'dd.MM.yyyy HH:mm', label: 'dd.MM.yyyy HH:mm' },
|
||||
{ id: 'HH:mm:ss', label: 'HH:mm:ss' },
|
||||
{ id: 'HH:mm', label: 'HH:mm' },
|
||||
];
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 매매 시그널 알림 팝업·상세의 시간 표시 포맷 (일반설정, DB 동기화)
|
||||
*/
|
||||
import { useSyncExternalStore } from 'react';
|
||||
import {
|
||||
DEFAULT_CHART_TIME_FORMAT,
|
||||
normalizeChartTimeFormat,
|
||||
} from './chartTimeFormat';
|
||||
|
||||
export const DEFAULT_TRADE_ALERT_TIME_FORMAT = DEFAULT_CHART_TIME_FORMAT;
|
||||
|
||||
let _format = DEFAULT_TRADE_ALERT_TIME_FORMAT;
|
||||
const _listeners = new Set<() => void>();
|
||||
|
||||
export function getTradeAlertTimeFormat(): string {
|
||||
return _format;
|
||||
}
|
||||
|
||||
export function setTradeAlertTimeFormat(format: string): void {
|
||||
const next = normalizeChartTimeFormat(format);
|
||||
if (_format === next) return;
|
||||
_format = next;
|
||||
_listeners.forEach(l => l());
|
||||
}
|
||||
|
||||
export function subscribeTradeAlertTimeFormat(cb: () => void): () => void {
|
||||
_listeners.add(cb);
|
||||
return () => _listeners.delete(cb);
|
||||
}
|
||||
|
||||
export function useTradeAlertTimeFormat(): string {
|
||||
return useSyncExternalStore(
|
||||
subscribeTradeAlertTimeFormat,
|
||||
getTradeAlertTimeFormat,
|
||||
() => DEFAULT_TRADE_ALERT_TIME_FORMAT,
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
/**
|
||||
* 매매 시그널 알림 표시용 포맷·라벨
|
||||
*/
|
||||
import { formatUnixWithChartPattern, splitChartTimePattern } from './chartTimeFormat';
|
||||
import { getKoreanName } from './marketNameCache';
|
||||
import { formatUnixDateTime, formatUnixClock, getDisplayTimezone } from './timezone';
|
||||
import { getTradeAlertTimeFormat } from './tradeAlertTimeFormat';
|
||||
import { getDisplayTimezone } from './timezone';
|
||||
|
||||
export interface TradeSignalDisplaySource {
|
||||
market: string;
|
||||
@@ -31,8 +33,12 @@ export function formatSignalPrice(price: number): string {
|
||||
export function formatSignalTime(unixSec: number, withDate = true): string {
|
||||
if (!unixSec) return '—';
|
||||
const tz = getDisplayTimezone();
|
||||
if (!withDate) return formatUnixClock(unixSec, tz, true);
|
||||
return formatUnixDateTime(unixSec, tz);
|
||||
const fmt = getTradeAlertTimeFormat();
|
||||
if (!withDate) {
|
||||
const { time } = splitChartTimePattern(fmt);
|
||||
return formatUnixWithChartPattern(unixSec, time || 'HH:mm', tz);
|
||||
}
|
||||
return formatUnixWithChartPattern(unixSec, fmt, tz);
|
||||
}
|
||||
|
||||
export function getSignalTypeKo(type: 'BUY' | 'SELL'): string {
|
||||
|
||||
Reference in New Issue
Block a user