1

日期区间的终点是用第二天的 00:00:00 还是当天的 23:59:59 比较好?

 8 months ago
source link: https://www.v2ex.com/t/1006014
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

V2EX  ›  程序员

日期区间的终点是用第二天的 00:00:00 还是当天的 23:59:59 比较好?

  xubingok · 3 小时 12 分钟前 · 2982 次点击

跟后端对接口,发现关于日期区间的定义有点模糊.

比如查询昨天的数据,我们一般是传起始时间点. 对接后端 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 更便于理解.

不知道大家都是用哪种呢?

75 条回复    2024-01-05 12:26:03 +08:00
luomao

luomao      3 小时 10 分钟前   ❤️ 3

用 23:59:59 ,00:00:00 已经是第二天了
justlazydog

justlazydog      3 小时 9 分钟前

都可以,统一就行,常识上一般是 23:59:59 。
corcre

corcre      3 小时 5 分钟前   ❤️ 2

用 23:59:59, 因为永远不知道谁没事传个日期/加个字段只记日期, 用 datetime 类型还把时间给截了...
coderzhangsan

coderzhangsan      3 小时 1 分钟前

让后端统一口径,前端没有必要兼容 2 种方案。
tusj

tusj      3 小时 1 分钟前   ❤️ 2

前闭后开区间,用 00:00:00
belin520

belin520      3 小时 0 分钟前

moment 等时间库的 endOfDay 是 23:59:59
lscho

lscho      2 小时 59 分钟前

肯定是 23:59:59 ,0 点已经是第二天了
douxc

douxc      2 小时 58 分钟前

不懂就问:如果记录到了毫秒级,23:59:59 会少 1 秒的数据吗?
Curtion

Curtion      2 小时 58 分钟前   ❤️ 2

A 更好, 左闭右开并不是一个常识, 大众常识应该左闭右闭, 选 00:00:00 会误以为包括第二天开始, 我们系统很多客户已经反馈过了
maocat

maocat      2 小时 58 分钟前 via iPhone   ❤️ 4

左开右闭原则 00.00.00 ,如果用 23.59.59 后面还有毫秒呢
gdfsjunjun

gdfsjunjun      2 小时 54 分钟前

当然是 23:59:59 容易理解,0 点的话会被认为第二天也算
chronos

chronos      2 小时 54 分钟前

除非你数据库里面存的时间精度只到秒,否则还是用 00:00:00 的好。
lqhunter233

lqhunter233      2 小时 52 分钟前

@douxc 2024-01-04 23:59:59.999(yyyy-MM-dd HH:mm:ss.SSS)精确到秒可以这样
leonshaw

leonshaw      2 小时 51 分钟前 via Android

lqhunter233

lqhunter233      2 小时 51 分钟前

打错了,毫秒
caiqichang

caiqichang      2 小时 49 分钟前   ❤️ 1

前端只穿日期
{
startTime:2024-01-04,
endTime:2024-01-04
}
后端想怎么处理自己在 get 里面处理
zliea

zliea      2 小时 48 分钟前

左闭右开 +1
yinmin

yinmin      2 小时 48 分钟前 via iPhone

如果某条记录的时间是 23:59:59.20 呢?

应该用 00:00:00 ,运算逻辑是:>=startTime and <endTime

(即使数据库字段的时间秒不含小数,也不应该用 23:59:59 ,避免给自己留坑。谁也不知道几年之后,某个程序员写库的时候引入了秒的小数)
darkengine

darkengine      2 小时 48 分钟前

如果只是日期区间,我的建议是只传日期,不传时间。以后如果有改动,直接改后端的逻辑就行,前端都不用再发版。
likunyan

likunyan      2 小时 42 分钟前

用 23:59:59 ,00:00:00 已经是第二天了
shuax

shuax      2 小时 41 分钟前

@likunyan 23:59:59 没有包含完整的一天
xhyzidane

xhyzidane      2 小时 40 分钟前

@belin520 实际上是 23:59:59.999
Kin9

Kin9      2 小时 39 分钟前

24:00:00
milukun

milukun      2 小时 37 分钟前

那你可以制造一条 23:59:59.xx 的数据,然后暴露出这个 bug
他们后端就统一了
xuanbg

xuanbg      2 小时 37 分钟前

我在后端都是:start <= x < end 。所以,按日期的话,end 是前端传的日期/时间的下一天的 0 点
debuggerx

debuggerx      2 小时 33 分钟前

我的习惯是,如果是日期范围的参数,比如 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);
crz

crz      2 小时 30 分钟前

toDate(timestamp) == date
libook

libook      2 小时 29 分钟前

小于等于这个逻辑就是错的,因为并没有完全覆盖时间段,除非业务能接受没覆盖的那一部分被排除在外。
qzh993

qzh993      2 小时 25 分钟前

很多兄弟都说了,左开右闭原则,前端只传日期,比如要查询 2024-01-04 这天的数据,后端这么处理,2024-01-04 00:00:00 <= create_time < 2024-01-05 00:00:00
nomytwins

nomytwins      2 小时 25 分钟前

正常都是当天的 23:59:59
RainCats

RainCats      2 小时 22 分钟前

我倾向于后端 B 的做法,我也这样做
belin520

belin520      2 小时 21 分钟前

@xhyzidane 点赞,你说这个才是对的,需要更正为你这个
imldy

imldy      2 小时 20 分钟前 via Android

后端用左闭右开的 0.0.0 ,但给用户显示的业务规则是用 59.59.59
adoal

adoal      2 小时 12 分钟前

如果做比较的时候能保证传入的时间先 truncate 到确定的精度,可以用两端都闭合。否则只能左闭右开才能保证正确性。
douxc

douxc      2 小时 12 分钟前

@lqhunter233 #13 这其实可以无限拆下去,如果我想完整包含一天的数据,应该用其他老哥提到的 < endTime ,也就是第二天的 00:00:00
JimMoen

JimMoen      2 小时 11 分钟前

```
{
startTime: "2024-01-04 00:00:00",
endTimeBefore: "2024-01-05 00:00:00"
}
```
Bromine0x23

Bromine0x23      2 小时 11 分钟前

前端来说右闭应该比较符合直觉;后端处理考虑到精度可能变化,用右开的应该比较好
zackzergzeng

zackzergzeng      2 小时 9 分钟前

得让后端对齐了,前端不能因为后台的不统一写两套代码啊
Belmode

Belmode      2 小时 9 分钟前

23:59:59 包含 23:59:59.99.....9,前端没必要做特殊处理,左右都是闭区间
Huelse

Huelse      2 小时 9 分钟前

传给后端查询一般用 00 这种,59 那种还要后端再处理下才能全覆盖
qzh993

qzh993      2 小时 7 分钟前

@qzh993 左闭右开
adoal

adoal      2 小时 5 分钟前

另外,除了小数部分之外,23:59:59 还有个问题是表示不了闰秒。

当然,实际应用中,如果业务不是全天候的,或者在那特定一两秒的业务量本来就少,也能承受少量错误的代价,就无所谓了。
dif

dif      2 小时 0 分钟前

[2024:01:01 00:00:00, 2024:01:01 23:59:59]
abc0123xyz

abc0123xyz      2 小时 0 分钟前

用 000000 省事
tyrone2333

tyrone2333      1 小时 47 分钟前

:default-time="['00:00:00', '23:59:59']"
ysc3839

ysc3839      1 小时 37 分钟前 via Android

一分钟 60 秒,取值范围是 0-59
54xavier

54xavier      1 小时 34 分钟前

对啊,我想说你要算截止和开始,其实应该用
00:00:00.000
23:59:59.999
这样更佳
nextvay

nextvay      1 小时 30 分钟前

开发了 8 年了,都是 start_time<= time < end_time ,我选 B

原因:
1 、万一数据库毫秒时间戳呢?
2 、开发、处理简单
JingXiao

JingXiao      1 小时 25 分钟前

日期区间不是只传日期就完事了么,如果非要加时分秒这种也是时间组件加上时分秒可选让用户选
doctli

doctli      1 小时 21 分钟前

@tusj +1
snylonue

snylonue      1 小时 18 分钟前

23:59:59

好几次因为 00:00:00 没赶上 ddl (
vagusss

vagusss      1 小时 18 分钟前

左闭右开就完事儿了, 到 59 秒是什么鬼
Seulgi

Seulgi      1 小时 14 分钟前

我的建议是时间戳一把梭
focuxin

focuxin      1 小时 11 分钟前

/**
* 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;
yyancy517

yyancy517      1 小时 9 分钟前

@tusj #5 +1
wangtian2020

wangtian2020      1 小时 6 分钟前

dayjs().endOf('d') 调用出来是啥就是啥,我不管
brader

brader      1 小时 6 分钟前

单说程序、技术,看完大家说的,其实都有道理,讨论不出一个明确的对错的。我们不妨把视野再放大一点,从页面使用人员的惯性思维去反推理解:
比如使用人员查询 1 月份报表,正常应该是点开始 1 月 1 日,结束 1 月 31 日。使用人员潜意识里,其实会认为他点了 1 月 31 日,就应该包含 31 日的数据的,而不是让他去点 2 月 1 日。
所以引导出:就给 1 月 31 日最后一刻的时间戳没问题的。
label

label      1 小时 3 分钟前

前端正常只需要到日, 或者时分, 怎么说也不用精确到秒, 这应该由后端来做
label

label      58 分钟前

@Belmode 数据库是不包含的, 如果前后端都不处理, 是查不到的
leonshaw

leonshaw      58 分钟前

@brader 日期是离散值,时间是连续值
yhnbgfd

yhnbgfd      48 分钟前

首先 23:59:59 是<错误>的时间区间判断条件, 我们需要先排除错误答案, 修复程序潜在 bug, 而不是给错误找理由.
liubaicai

liubaicai      48 分钟前

0:0:0 的时间戳-1 就行了
adoal

adoal      47 分钟前

@brader 也就是说,其实 Calendar 相关的东西是跟 time 不同逻辑的数据类型,它不是物理意义上的时间长度和时刻坐标,而是人为的、人文的编号。如果要处理日历上的日期,复杂度远比“存储用 UTC timestamp ,给人看的交给前端去做转换”复杂得多。以前在一个教务系统招生模块里就是因为大意用普通的时间类型保存生日而碰到了中国实行夏时制的年份里出生在夏时制边缘日子里的学生因为算出来的“年龄”不对而无法报名的问题。
lambdaq

lambdaq      47 分钟前

23:59:59.999 秒的时候客户下了一个订单呢?
brader

brader      47 分钟前

@leonshaw 传日期和时间戳一样的意思,看你细到什么粒度,反正都能互转,就看后端接口喜欢用哪种了。最终面临的都是楼主说的这个问题
mytharcher

mytharcher      46 分钟前   ❤️ 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 接口上是否能表达“>=”/“<”这些非相等的运算符,如果可以,那么前端可以按精确时间传闭开区间的表达。如果不行,那么只能按精度字符串传,由后端根据精度信息进行理解。
quandou

quandou      39 分钟前

00:00:00 已经是 after day 了 23:59:59 合理
leonshaw

leonshaw      28 分钟前

@brader 问题就在“看你细到什么粒度”,这要求在 API 上约定粒度,并且影响系统内部实现。
Philippa

Philippa      24 分钟前 via iPhone

前后端判断用左闭右开,因为还有 1s 的间隙。显示用 23:59:59
Bingchunmoli

Bingchunmoli      19 分钟前 via Android

@douxc 会的..,不要问我是怎么知道的
kevin1452

kevin1452      17 分钟前

通常是左闭右开
Bingchunmoli

Bingchunmoli      15 分钟前 via Android

@brader 但是最后一刻时间戳你是精确到秒还是毫秒还是微秒,,实际上不如处理一下左闭又开,不然线上确实会丢这一段数据
lovelylain

lovelylain      14 分钟前 via Android

达成共识都可以,待讨论的话建议双闭区间,因为这个有效期可能涉及展示,开区间展示时需要减 1 秒处理
fionasit007

fionasit007      11 分钟前

00:00:00 算是第二天了,以后会是个大坑,比如抖音的巨量后台你用 00:00:00 会查出第二天这个点的数据,因为数据量太大 00:00:00 也会产生数据的
fionasit007

fionasit007      9 分钟前

@Seulgi 这个时候有问题又回到原地了 那到底是 59 的时间戳还是 00 的时间戳呢

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK