diff --git a/web/app.py b/web/app.py index 19da4dd..776ab87 100644 --- a/web/app.py +++ b/web/app.py @@ -184,14 +184,35 @@ def api_pullbacks(): @app.template_filter('datetime_format') def datetime_format(value, format='%Y-%m-%d %H:%M'): - """日期时间格式化过滤器""" + """日期时间格式化过滤器 - 转换为东八区时间""" if value is None: return '' + + # 导入时区支持 + from datetime import timezone, timedelta + if isinstance(value, str): try: - value = datetime.fromisoformat(value.replace('Z', '+00:00')) + # 解析ISO格式时间字符串 + if 'Z' in value: + value = datetime.fromisoformat(value.replace('Z', '+00:00')) + elif '+' not in value and 'T' in value: + # 假设是UTC时间 + value = datetime.fromisoformat(value).replace(tzinfo=timezone.utc) + else: + value = datetime.fromisoformat(value) except: return value + + # 如果是naive datetime,假设是UTC时间 + if isinstance(value, datetime) and value.tzinfo is None: + value = value.replace(tzinfo=timezone.utc) + + # 转换为东八区时间 (UTC+8) + if isinstance(value, datetime) and value.tzinfo is not None: + china_tz = timezone(timedelta(hours=8)) + value = value.astimezone(china_tz) + return value.strftime(format) diff --git a/web/static/js/main.js b/web/static/js/main.js index 539dded..bd371d5 100644 --- a/web/static/js/main.js +++ b/web/static/js/main.js @@ -238,6 +238,49 @@ function formatNumbers() { }); } +/** + * 时间格式化 - 转换为东八区时间 + */ +function formatDatetimeToChina(datetimeStr, format = '%m-%d %H:%M') { + if (!datetimeStr) return ''; + + let date; + try { + // 解析时间字符串 + if (datetimeStr.endsWith('Z')) { + // UTC时间 + date = new Date(datetimeStr); + } else if (datetimeStr.includes('T') && !datetimeStr.includes('+')) { + // 假设是UTC时间 + date = new Date(datetimeStr + 'Z'); + } else { + date = new Date(datetimeStr); + } + + // 转换为东八区时间 + const chinaTime = new Date(date.getTime() + (8 * 60 * 60 * 1000) - (date.getTimezoneOffset() * 60 * 1000)); + + // 格式化显示 + const year = chinaTime.getFullYear(); + const month = String(chinaTime.getMonth() + 1).padStart(2, '0'); + const day = String(chinaTime.getDate()).padStart(2, '0'); + const hour = String(chinaTime.getHours()).padStart(2, '0'); + const minute = String(chinaTime.getMinutes()).padStart(2, '0'); + + // 根据格式返回 + if (format === '%Y-%m-%d') { + return `${year}-${month}-${day}`; + } else if (format === '%m-%d %H:%M') { + return `${month}-${day} ${hour}:${minute}`; + } else { + return `${year}-${month}-${day} ${hour}:${minute}`; + } + } catch (e) { + console.error('时间格式化错误:', e); + return datetimeStr; + } +} + /** * 导出数据 */ @@ -342,18 +385,22 @@ function updateSignalTable(signals) { tbody.empty(); signals.forEach(function(signal) { + const scanTime = signal.scan_time ? formatDatetimeToChina(signal.scan_time, '%m-%d %H:%M') : ''; + const signalDate = signal.signal_date ? formatDatetimeToChina(signal.signal_date, '%Y-%m-%d') : ''; + const row = ` ${signal.stock_code} ${signal.stock_name || '未知'} ${signal.strategy_name} ${signal.timeframe} - ${signal.signal_date} + ${signalDate} ${signal.breakout_price.toFixed(2)}元 ${signal.yin_high.toFixed(2)}元 ${signal.breakout_pct.toFixed(2)}% ${signal.final_yang_entity_ratio.toFixed(2)}% ${signal.turnover_ratio.toFixed(2)}% + ${scanTime} `; tbody.append(row);