날짜포멧 수정

This commit is contained in:
Macbook
2026-05-29 02:25:03 +09:00
parent 990b3a0710
commit 03441337f2
19 changed files with 166 additions and 26 deletions
@@ -72,6 +72,11 @@ public class GcAppSettings {
@Builder.Default
private String chartTimeFormat = "MM-dd HH:mm";
/** 매매 시그널 알림 팝업 시간 표시 포맷 */
@Column(name = "trade_alert_time_format", length = 64, nullable = false)
@Builder.Default
private String tradeAlertTimeFormat = "MM-dd HH:mm";
/** 캔들 색상 JSON (frontend MainChartStyle 구조) */
@Column(name = "main_chart_style_json", columnDefinition = "JSON")
@JdbcTypeCode(SqlTypes.JSON)
@@ -83,6 +83,7 @@ public class AppSettingsService {
if (d.containsKey("defaultLayoutId")) s.setDefaultLayoutId((String) d.get("defaultLayoutId"));
if (d.containsKey("displayTimezone")) s.setDisplayTimezone((String) d.get("displayTimezone"));
if (d.containsKey("chartTimeFormat")) s.setChartTimeFormat(normalizeChartTimeFormat((String) d.get("chartTimeFormat")));
if (d.containsKey("tradeAlertTimeFormat")) s.setTradeAlertTimeFormat(normalizeChartTimeFormat((String) d.get("tradeAlertTimeFormat")));
if (d.containsKey("mainChartStyle")) s.setMainChartStyleJson(toJson(d.get("mainChartStyle")));
if (d.containsKey("syncOptions")) s.setSyncOptionsJson(toJson(d.get("syncOptions")));
if (d.containsKey("btAutoPopup")) s.setBtAutoPopup(
@@ -190,6 +191,7 @@ public class AppSettingsService {
m.put("defaultLayoutId", s.getDefaultLayoutId() != null ? s.getDefaultLayoutId() : "1");
m.put("displayTimezone", s.getDisplayTimezone() != null ? s.getDisplayTimezone() : "Asia/Seoul");
m.put("chartTimeFormat", s.getChartTimeFormat() != null ? s.getChartTimeFormat() : "MM-dd HH:mm");
m.put("tradeAlertTimeFormat", s.getTradeAlertTimeFormat() != null ? s.getTradeAlertTimeFormat() : "MM-dd HH:mm");
m.put("mainChartStyle", parseJson(s.getMainChartStyleJson()));
m.put("syncOptions", parseJson(s.getSyncOptionsJson()));
m.put("btAutoPopup", s.getBtAutoPopup() != null ? s.getBtAutoPopup() : true);
@@ -0,0 +1,2 @@
ALTER TABLE gc_app_settings
ADD COLUMN trade_alert_time_format VARCHAR(64) NOT NULL DEFAULT 'MM-dd HH:mm' AFTER chart_time_format;
+2 -1
View File
@@ -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
View File
@@ -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]);
+7 -1
View File
@@ -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}
+35 -9
View File
@@ -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}
+4 -5
View File
@@ -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);
+1
View File
@@ -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(
+11
View File
@@ -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 };
+6
View File
@@ -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;
+2
View File
@@ -446,6 +446,8 @@ export interface AppSettingsDto {
displayTimezone?: string;
/** 차트 하단 시간축 포맷 (예: yyyy-MM-dd HH:mm) */
chartTimeFormat?: string;
/** 매매 시그널 알림 팝업 시간 포맷 */
tradeAlertTimeFormat?: string;
/** 추세검색 기본 설정 */
trendSearchSettings?: import('./trendSearchAppSettings').TrendSearchAppSettings | null;
/** UI 설정 통합 (편집기·팔레트·가상투자 목록·패널 크기 등) */
+11 -1
View File
@@ -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,
);
}
+9 -3
View File
@@ -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 {