跟后端对接口,发现关于日期区间的定义有点模糊.
比如查询昨天的数据,我们一般是传起始时间点. 对接后端 A 的参数是:
{
startTime:2024-01-04 00:00:00,
endTime:2024-01-04 23:59:59
}
对接后端 B 的参数是:
{
startTime:2024-01-04 00:00:00,
endTime:2024-01-05 00:00:00
}
后端 A 说他用的是小于等于,后端 B 说他用的是小于,前端的时间工具类还得写两个方法...
想统一,但感觉两种方式都有自己的道理. 个人是倾向于 00:00:00 这种,可能是来源于数组截取时顾头不顾尾的概念. 另一位后端大佬则觉得 23:59:59 更便于理解.
不知道大家都是用哪种呢?
1
luomao 348 天前 36
用 23:59:59 ,00:00:00 已经是第二天了
|
2
justlazydog 348 天前 1
都可以,统一就行,常识上一般是 23:59:59 。
|
3
corcre 348 天前 7
用 23:59:59, 因为永远不知道谁没事传个日期/加个字段只记日期, 用 datetime 类型还把时间给截了...
|
4
coderzhangsan 348 天前
让后端统一口径,前端没有必要兼容 2 种方案。
|
5
tusj 348 天前 4
前闭后开区间,用 00:00:00
|
6
belin520 348 天前 1
moment 等时间库的 endOfDay 是 23:59:59
|
7
lscho 348 天前
肯定是 23:59:59 ,0 点已经是第二天了
|
8
douxc 348 天前
不懂就问:如果记录到了毫秒级,23:59:59 会少 1 秒的数据吗?
|
9
Curtion 348 天前 3
A 更好, 左闭右开并不是一个常识, 大众常识应该左闭右闭, 选 00:00:00 会误以为包括第二天开始, 我们系统很多客户已经反馈过了
|
10
maocat 348 天前 via iPhone 13
左开右闭原则 00.00.00 ,如果用 23.59.59 后面还有毫秒呢
|
11
gdfsjunjun 348 天前
当然是 23:59:59 容易理解,0 点的话会被认为第二天也算
|
12
chronos 348 天前
除非你数据库里面存的时间精度只到秒,否则还是用 00:00:00 的好。
|
13
lqhunter233 348 天前
@douxc 2024-01-04 23:59:59.999(yyyy-MM-dd HH:mm:ss.SSS)精确到秒可以这样
|
14
leonshaw 348 天前 via Android
左闭右开
|
15
lqhunter233 348 天前
打错了,毫秒
|
16
caiqichang 348 天前 1
前端只穿日期
{ startTime:2024-01-04, endTime:2024-01-04 } 后端想怎么处理自己在 get 里面处理 |
17
zliea 348 天前
左闭右开 +1
|
18
yinmin 348 天前 via iPhone
如果某条记录的时间是 23:59:59.20 呢?
应该用 00:00:00 ,运算逻辑是:>=startTime and <endTime (即使数据库字段的时间秒不含小数,也不应该用 23:59:59 ,避免给自己留坑。谁也不知道几年之后,某个程序员写库的时候引入了秒的小数) |
19
darkengine 348 天前
如果只是日期区间,我的建议是只传日期,不传时间。以后如果有改动,直接改后端的逻辑就行,前端都不用再发版。
|
20
QKgf555H87Fp0cth 348 天前
用 23:59:59 ,00:00:00 已经是第二天了
|
23
Kin9 348 天前
24:00:00
|
24
milukun 348 天前 1
那你可以制造一条 23:59:59.xx 的数据,然后暴露出这个 bug
他们后端就统一了 |
25
xuanbg 348 天前
我在后端都是:start <= x < end 。所以,按日期的话,end 是前端传的日期/时间的下一天的 0 点
|
26
debuggerx 348 天前
我的习惯是,如果是日期范围的参数,比如 1 月 1 号-1 月 3 号,那么前端就传 1 月 1 号和 1 月 3 号这两天任意的时间即可(比如 2024-01-01 08:12:35 和 2024-01-03 18:22:02 就满足,),后端只取日期部分。这样的好处是不会把歧义泄露到前端代码,并且前端使用 api 非常方便,比如前端想要近 5 天的数据,只要写:
const now = new Date(); const endTime = now.toISOString(); now.setDate(now.getDate() - 5); const startTime = now.toISOString(); apis.someApi(startTime, endTime); |
27
crz 348 天前
toDate(timestamp) == date
|
28
libook 348 天前
小于等于这个逻辑就是错的,因为并没有完全覆盖时间段,除非业务能接受没覆盖的那一部分被排除在外。
|
29
qzh993 348 天前
很多兄弟都说了,左开右闭原则,前端只传日期,比如要查询 2024-01-04 这天的数据,后端这么处理,2024-01-04 00:00:00 <= create_time < 2024-01-05 00:00:00
|
30
nomytwins 348 天前
正常都是当天的 23:59:59
|
31
RainCats 348 天前
我倾向于后端 B 的做法,我也这样做
|
33
imldy 348 天前 via Android
后端用左闭右开的 0.0.0 ,但给用户显示的业务规则是用 59.59.59
|
34
adoal 348 天前
如果做比较的时候能保证传入的时间先 truncate 到确定的精度,可以用两端都闭合。否则只能左闭右开才能保证正确性。
|
35
douxc 348 天前
@lqhunter233 #13 这其实可以无限拆下去,如果我想完整包含一天的数据,应该用其他老哥提到的 < endTime ,也就是第二天的 00:00:00
|
36
JimMoen 348 天前
```
{ startTime: "2024-01-04 00:00:00", endTimeBefore: "2024-01-05 00:00:00" } ``` |
37
Bromine0x23 348 天前
前端来说右闭应该比较符合直觉;后端处理考虑到精度可能变化,用右开的应该比较好
|
38
zackzergzeng 348 天前
得让后端对齐了,前端不能因为后台的不统一写两套代码啊
|
39
Belmode 348 天前
23:59:59 包含 23:59:59.99.....9,前端没必要做特殊处理,左右都是闭区间
|
40
Huelse 348 天前
传给后端查询一般用 00 这种,59 那种还要后端再处理下才能全覆盖
|
42
adoal 348 天前
另外,除了小数部分之外,23:59:59 还有个问题是表示不了闰秒。
当然,实际应用中,如果业务不是全天候的,或者在那特定一两秒的业务量本来就少,也能承受少量错误的代价,就无所谓了。 |
43
dif 348 天前
[2024:01:01 00:00:00, 2024:01:01 23:59:59]
|
44
abc0123xyz 348 天前
用 000000 省事
|
45
tyrone2333 348 天前
:default-time="['00:00:00', '23:59:59']"
|
46
ysc3839 348 天前 via Android
一分钟 60 秒,取值范围是 0-59
|
47
54xavier 348 天前 1
对啊,我想说你要算截止和开始,其实应该用
00:00:00.000 23:59:59.999 这样更佳 |
48
nextvay 348 天前
开发了 8 年了,都是 start_time<= time < end_time ,我选 B
原因: 1 、万一数据库毫秒时间戳呢? 2 、开发、处理简单 |
49
JingXiao 348 天前
日期区间不是只传日期就完事了么,如果非要加时分秒这种也是时间组件加上时分秒可选让用户选
|
51
snylonue 348 天前
23:59:59
好几次因为 00:00:00 没赶上 ddl ( |
52
vagusss 348 天前
左闭右开就完事儿了, 到 59 秒是什么鬼
|
53
Seulgi 348 天前
我的建议是时间戳一把梭
|
54
focuxin 348 天前
/**
* The maximum supported {@code LocalTime}, '23:59:59.999999999'. * This is the time just before midnight at the end of the day. */ public static final LocalTime MAX; |
56
wangtian2020 348 天前
dayjs().endOf('d') 调用出来是啥就是啥,我不管
|
57
brader 348 天前
单说程序、技术,看完大家说的,其实都有道理,讨论不出一个明确的对错的。我们不妨把视野再放大一点,从页面使用人员的惯性思维去反推理解:
比如使用人员查询 1 月份报表,正常应该是点开始 1 月 1 日,结束 1 月 31 日。使用人员潜意识里,其实会认为他点了 1 月 31 日,就应该包含 31 日的数据的,而不是让他去点 2 月 1 日。 所以引导出:就给 1 月 31 日最后一刻的时间戳没问题的。 |
58
label 348 天前
前端正常只需要到日, 或者时分, 怎么说也不用精确到秒, 这应该由后端来做
|
61
yhnbgfd 348 天前
首先 23:59:59 是<错误>的时间区间判断条件, 我们需要先排除错误答案, 修复程序潜在 bug, 而不是给错误找理由.
|
62
liubaicai 348 天前
0:0:0 的时间戳-1 就行了
|
63
adoal 348 天前
@brader 也就是说,其实 Calendar 相关的东西是跟 time 不同逻辑的数据类型,它不是物理意义上的时间长度和时刻坐标,而是人为的、人文的编号。如果要处理日历上的日期,复杂度远比“存储用 UTC timestamp ,给人看的交给前端去做转换”复杂得多。以前在一个教务系统招生模块里就是因为大意用普通的时间类型保存生日而碰到了中国实行夏时制的年份里出生在夏时制边缘日子里的学生因为算出来的“年龄”不对而无法报名的问题。
|
64
lambdaq 348 天前
23:59:59.999 秒的时候客户下了一个订单呢?
|
66
mytharcher 348 天前 1
所有这些问题都是对日期和时间描述精度的问题。以下举例:
1. 用户要查 2023 年的数据,此时精度是“年”,那么界面上只需要选年,但后端要执行的查询应该是 >= 2023-01-01 00:00:00 && < 2024-01-01 00:00:00 。 2. 用户要查 2023 年 11 月的数据,此时精度是“月”,界面上需要选年-月,但后端要执行的查询应该是 >= 2023-11-01 00:00:00 && < 2023-12-01 00:00:00 。 3. 用户要查 2023 年 11 月 5 日的数据,此时精度是“日”,界面上只需要选年-月-日,但后端要执行的查询应该是是 >= 2023-11-05 00:00:00 && < 2023-11-06 00:00:00 。 4. ... 时、分、秒、毫秒、微秒等精度类似,不再继续举例。 以上举例我们可以得到的结论是,要查什么精度,那么后端需要执行的区间范围就是其精度所在时间的起始(闭区间)到其精度 +1 单位后的时间结束(开区间)。所以传开闭区间的条件比较合理。如果 HTTP 接口上不支持开闭条件的描述(>= 和 <),那么就只能后端拿到查询参数串后根据提供的精度解析,比如传了 2023 ,就认为精度是年,传了 2023-11-05 则认为精度是日,传 2023-11-05 12:41:32 则认为是秒,均按此精度统一规则处理,查询就不会有问题了。 尤其避免一些传 23:59:59 却不包含最后一秒内数据的误解。 所以区别两者,最核心的是你的 HTTP 接口上是否能表达“>=”/“<”这些非相等的运算符,如果可以,那么前端可以按精确时间传闭开区间的表达。如果不行,那么只能按精度字符串传,由后端根据精度信息进行理解。 |
67
quandou 348 天前
00:00:00 已经是 after day 了 23:59:59 合理
|
69
Philippa 348 天前 via iPhone
前后端判断用左闭右开,因为还有 1s 的间隙。显示用 23:59:59
|
70
Bingchunmoli 348 天前 via Android
@douxc 会的..,不要问我是怎么知道的
|
71
kevin1452 348 天前
通常是左闭右开
|
72
Bingchunmoli 348 天前 via Android
@brader 但是最后一刻时间戳你是精确到秒还是毫秒还是微秒,,实际上不如处理一下左闭又开,不然线上确实会丢这一段数据
|
73
lovelylain 348 天前 via Android
达成共识都可以,待讨论的话建议双闭区间,因为这个有效期可能涉及展示,开区间展示时需要减 1 秒处理
|
74
fionasit007 348 天前
00:00:00 算是第二天了,以后会是个大坑,比如抖音的巨量后台你用 00:00:00 会查出第二天这个点的数据,因为数据量太大 00:00:00 也会产生数据的
|
75
fionasit007 348 天前
@Seulgi 这个时候有问题又回到原地了 那到底是 59 的时间戳还是 00 的时间戳呢
|
76
Huelse 348 天前
@fionasit007 #74 左闭右开区间,不会有你说的这个问题。
|
77
adoal 348 天前
@mytharcher 如果业务逻辑的关注点是年、月、日这些“人为编制的序号”,那么更好的做法是抽出年、月、日建索引,检索时在索引字段上按照更符合人类认知的闭区间来筛选,而不是在 timestamp 上划分范围。
|
78
sarices 348 天前
>=2024-01-04 00:00:00 <2024-01-05 00:00:00
|
79
Fule 348 天前
我觉得这个问题不能一刀切,需要根据是否需要“时间”这个概念来确定如何保存和查询。
对于数据存储来说,例如,对于“下单时间”这个概念,显然时分秒是必要的,而对于生日这个概念,(通常)时分秒是没有意义的,所以前者需要存储时分秒数据,后者只需要存储年月日数据而时分秒都是 0 或者存储系统支持纯年月日类型的话更好。 对于查询,如果是上述下单日期,同样基于业务要求来决定是否需要传入和处理时分秒数据。比如业务有需求要查询某日几点几分到几点几分的数据,则接口参数需要包含时分秒而查询就是基于大于等于并且小于等于的范围。而如果只是需求查询几号到几号的数据,则接口参数不需要包含时分秒或者时分秒为 0 而执行时基于大于等于 BeginDate 并且小于 QueryEndDate + 1 day 的范围。如果是上述生日数据,因为本身存储不带时分秒,所以接口参数也不带时分秒或时分秒为 0 而查询基于大于等于并且小于等于的逻辑。 |
80
sephiroka 348 天前
我写得时候都是小于第二天 0 点的
|
81
5sheep 348 天前
标准答案
前端用户选 59 后端程序开 00 |
82
Fule 348 天前
当然,如果需求变更,下单时间查询原来只是某天到某天,现在改为某天某时到某天某日,上述做法导致前后端都需要修改,所以如果想避免/预判需求更改导致的代码更改,那么前端依然按照需求要求带时分秒或不带时分秒,而后端则根据传入的参数是否带有时分秒数据而分别进行查询。
|
83
yannxia 348 天前
这个不是取决于后端的比较逻辑?没听过这里有什么常识。
start < x < end || start <= x <= end || start <=x < end 都见过,一个系统保持一致就好了 |
84
polo3584 348 天前
23:59:59+1
|
85
Lexgni 348 天前
查询范围的话肯定是 B ,不然数据在后面一秒中的话岂不是怎么都查不到了
|
86
lovedebug 348 天前
ISO 8601 有提到 https://en.wikipedia.org/wiki/ISO_8601
An amendment was published in October 2022 featuring minor technical clarifications and attempts to remove ambiguities in definitions. The most significant change, however, was the reintroduction of the "24:00:00" format to refer to the instant at the end of a calendar day. |
87
Atomo 348 天前 via Android
B 端正确
|
88
lovedebug 348 天前
As of ISO 8601-1:2019/Amd 1:2022, "00:00:00" may be used to refer to midnight corresponding to the instant at the beginning of a calendar day; and "24:00:00" to refer to midnight corresponding to the instant at the end of a calendar day.[1] ISO 8601-1:2019 as originally published removed "24:00:00" as a representation for the end of day although it had been permitted in earlier versions of the standard.
从标准看 24:00:00 代表一天的结束,00:00:00 代表一天的开始 |
89
vacuitym 348 天前
这种只要精确到日期不需要时间的应该前端只要传到日就好了吧,后端自己去处理精确时间就好
|
90
haizi 348 天前
23:59:59 好,00 表示第二天了。理解上会有误差
|
91
xjpicism 348 天前
时间筛选接口建议用 timeGte, timeGt, timeLte, timeLt 命名
这样不会有歧义 A 的参数对应 timeGte, timeLte B 的参数对应 timeGte, timeLt |
92
iX8NEGGn 348 天前
编程上,大多数涉及到集合索引区间时,习惯上都是包括头不包括尾,所以 00 开始,59 结束
|
93
alexhx 348 天前
所有时间类字段能用时间戳的全用时间戳
|
94
codingguy 348 天前
小于第二天 00:00:00 的时间戳都是当天。
你们时间数据不用时间戳的吗? |
95
kjcm150 348 天前
https://en.wikipedia.org/wiki/24-hour_clock#Midnight_00:00_and_24:00
In the 24-hour time notation, the day begins at midnight, 00:00 or 0:00, and the last minute of the day begins at 23:59. Where convenient, the notation 24:00 may also be used to refer to midnight at the end of a given date — that is, 24:00 of one day is the same time as 00:00 of the following day. |
96
zpf124 348 天前
在编程里面,一般情况都是包左不包右, 所以我原本是习惯 00:00:00 的方式。
然而在 SQL 最起码 MySQL 中 between and 是左右边界值都包含的, 而我又懒得用 "x >=a 、x<b" 去 替换 "x BETWEEN a and b" 那么最简单的方式就是把 b 改成 23:59:59, 觉得不精确那你就再加精度 23:59:59.999 。 如果你用 java 的话,jdk8 还可以 LocalDate.atTime(LocalTime.Max) |
97
zhandi4 348 天前
如果用户可以选时分秒,传 endTime:2024-01-04 23:59:59 ,至于 2024-01-04 23:59:59.xx 的数据得要后端考虑怎样处理了。如果用户不能选时分秒,传 endTime:2024-01-04 即可
|
98
SD10 348 天前 via iPhone
区间采用前闭后开
|
99
icyliang 347 天前
最近刚好也碰到这个问题,我猜大部分支持 00:00:00 都是不需要处理展示的,这个时间戳在客户端呈现给客户已经是第二天。显示的时候必须 -1 ,这样的操作很难评。在编写显示和数据处理存在两种时间戳很容易造成困惑和 bug😅
|
100
shenjinpeng 347 天前
00:00:00 - 23:59:59
前端给用户展示也无需再处理, 传给后端就能直接用, 无需计算日期+1 , sql 语句可以直接用 between |