20260927215912
This commit is contained in:
1 parent
27e49e2e45
commit
f22e67fc85
294 files changed
+29247
-3576
No files matched your search
@@ -4,9 +4,24 @@ import {
|
||||
defaultQueryValue,
|
||||
sqlLiteral,
|
||||
} from '@/components/fms-module-common/queryUtils'
|
||||
import { formatDateValue, parseOptions } from '@/components/fms-module-common/fieldUtils'
|
||||
import {
|
||||
formatDateValue,
|
||||
parseOptions,
|
||||
toUtcIsoDateTime,
|
||||
} from '@/components/fms-module-common/fieldUtils'
|
||||
import { setMessages, translate } from '@/stores/i18n'
|
||||
|
||||
const pad2 = (number) => String(number).padStart(2, '0')
|
||||
|
||||
/**
|
||||
* 本地墙钟串 → UTC 墙钟串(与实现同口径,断言不依赖运行环境时区)。
|
||||
* 库中 datetime 存 UTC,查询条件里的本地时刻会先折算(见后端 TimeUtils / queryUtils.utcWallTextOf)。
|
||||
*/
|
||||
function toUtcWall(localText) {
|
||||
const date = new Date(String(localText).replace(' ', 'T'))
|
||||
return `${date.getUTCFullYear()}-${pad2(date.getUTCMonth() + 1)}-${pad2(date.getUTCDate())} ${pad2(date.getUTCHours())}:${pad2(date.getUTCMinutes())}:${pad2(date.getUTCSeconds())}`
|
||||
}
|
||||
|
||||
/** buildConditionSql 需要按字段名查到字段定义,b_type 决定字面量形式(数字裸写 / 文本 N'...') */
|
||||
function fieldMapOf(...fields) {
|
||||
return new Map(fields.map((field) => [field.b_field, field]))
|
||||
@@ -58,17 +73,23 @@ describe('fms-module-list query utilities', () => {
|
||||
).toBe("[status] IN (N'a', N'b')")
|
||||
})
|
||||
|
||||
it('日期时间字面量:自带时分秒按原时刻比较,仅日精度才补当天起 / 当天末', () => {
|
||||
it('日期时间字面量:本地时刻折算成 UTC 后比较,date 字段不折算', () => {
|
||||
const datetimeField = { b_field: 'created', b_type: 'datetime' }
|
||||
const dateField = { b_field: 'created', b_type: 'date' }
|
||||
|
||||
// datetime 值自带时分秒 → 原样保留,结束端也不再被夹到当天末
|
||||
expect(sqlLiteral('2026-01-01 09:30:00', datetimeField)).toBe("N'2026-01-01 09:30:00'")
|
||||
expect(sqlLiteral('2026-01-31 18:00:00', datetimeField, true)).toBe("N'2026-01-31 18:00:00'")
|
||||
// 只有日精度 → 按端点补默认时间
|
||||
expect(sqlLiteral('2026-01-01', datetimeField)).toBe("N'2026-01-01 00:00:00'")
|
||||
expect(sqlLiteral('2026-01-31', datetimeField, true)).toBe("N'2026-01-31 23:59:59'")
|
||||
// date 字段始终截到日,不补时分秒
|
||||
// datetime:库中存 UTC(见后端 TimeUtils),本地端点先折算再拼字面量;
|
||||
// 自带时分秒按原时刻、仅日精度才补当天起 / 当天末,再统一折算
|
||||
expect(sqlLiteral('2026-01-01 09:30:00', datetimeField)).toBe(
|
||||
`N'${toUtcWall('2026-01-01 09:30:00')}'`,
|
||||
)
|
||||
expect(sqlLiteral('2026-01-31 18:00:00', datetimeField, true)).toBe(
|
||||
`N'${toUtcWall('2026-01-31 18:00:00')}'`,
|
||||
)
|
||||
expect(sqlLiteral('2026-01-01', datetimeField)).toBe(`N'${toUtcWall('2026-01-01 00:00:00')}'`)
|
||||
expect(sqlLiteral('2026-01-31', datetimeField, true)).toBe(
|
||||
`N'${toUtcWall('2026-01-31 23:59:59')}'`,
|
||||
)
|
||||
// date 字段是纯日历值(无时区语义):始终截到日,不折算
|
||||
expect(sqlLiteral('2026-01-01 09:30:00', dateField)).toBe("N'2026-01-01'")
|
||||
})
|
||||
|
||||
@@ -89,21 +110,39 @@ describe('fms-module-list query utilities', () => {
|
||||
})
|
||||
|
||||
it('formats ISO dates consistently', () => {
|
||||
expect(formatDateValue('2026-08-01T12:34:56Z')).toMatch(/^2026-08-01/)
|
||||
expect(formatDateValue('2026-08-01', false)).toBe('2026-08-01')
|
||||
expect(formatDateValue(null)).toBe('')
|
||||
})
|
||||
|
||||
/**
|
||||
* 时间契约:后端统一输出「显示格式」的墙钟串(见后端 DbUtils.normalizeJdbcValue,
|
||||
* 与旧项目读取 ResultSet 时格式化的做法一致):
|
||||
* date → "2026-09-15",datetime → "2026-09-15 21:16:47",即拿即显示、不做时区换算。
|
||||
* 展示层对标准输入应为透传;仅非标准输入(数字时间戳等)走 Date 兜底。
|
||||
* 时间契约(见后端 TimeUtils 与 fieldUtils 注释):
|
||||
* 库中 datetime 列存 UTC,接口出口是带 Z 的 ISO 串("2026-09-26T02:00:00Z");
|
||||
* 展示时按浏览器本地时区折算,提交时再由 toUtcIsoDateTime 转回 UTC。
|
||||
* date / time 与无时区标记的墙钟串没有时区语义,原样透传。
|
||||
*/
|
||||
it('passes through display-format strings without timezone math', () => {
|
||||
it('展示:带 Z 的 UTC 串折算成本地,墙钟串与日期串原样透传', () => {
|
||||
// 用往返断言,不依赖运行环境时区
|
||||
const local = '2026-09-15 21:16:47'
|
||||
const utc = toUtcIsoDateTime(local)
|
||||
expect(utc).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/)
|
||||
expect(formatDateValue(utc, true)).toBe(local)
|
||||
expect(formatDateValue(utc, false)).toBe('2026-09-15')
|
||||
|
||||
// 无时区标记的墙钟串(存量数据、本地值):透传
|
||||
expect(formatDateValue('2026-09-15 21:16:47', true)).toBe('2026-09-15 21:16:47')
|
||||
expect(formatDateValue('2026-09-15 21:16:47', false)).toBe('2026-09-15')
|
||||
expect(formatDateValue('2026-09-15', false)).toBe('2026-09-15')
|
||||
// 非日期开头的输入(数字时间戳等)仍走 Date 兜底,按本地时间格式化
|
||||
expect(formatDateValue(1789471007000, true)).toMatch(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/)
|
||||
})
|
||||
|
||||
it('提交:本地时间值转成带 Z 的 UTC 串,已是带标记的串原样返回', () => {
|
||||
expect(toUtcIsoDateTime('2026-09-15 21:16:47')).toMatch(
|
||||
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/,
|
||||
)
|
||||
expect(toUtcIsoDateTime('2026-09-15T13:16:47Z')).toBe('2026-09-15T13:16:47Z')
|
||||
expect(toUtcIsoDateTime('2026-09-15T21:16:47+08:00')).toBe('2026-09-15T21:16:47+08:00')
|
||||
// 本地 → UTC → 展示:往返一致
|
||||
expect(formatDateValue(toUtcIsoDateTime('2026-09-15 21:16:47'), true)).toBe('2026-09-15 21:16:47')
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user