跟后端对接口,发现关于日期区间的定义有点模糊.
比如查询昨天的数据,我们一般是传起始时间点.
对接后端 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 更便于理解.
不知道大家都是用哪种呢?

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

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

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

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

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

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

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

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

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

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

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

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

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

左闭右开

打错了,毫秒

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

左闭右开 +1

如果某条记录的时间是 23:59:59.20 呢?应该用 00:00:00 ,运算逻辑是:>=startTime and <endTime(即使数据库字段的时间秒不含小数,也不应该用 23:59:59 ,避免给自己留坑。谁也不知道几年之后,某个程序员写库的时候引入了秒的小数)

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

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

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

实际上是 23:59:59.999

24:00:00

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

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

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

toDate(timestamp) == date

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

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

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

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

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

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

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

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

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

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

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

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

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

左闭右开

另外,除了小数部分之外,23:59:59 还有个问题是表示不了闰秒。当然,实际应用中,如果业务不是全天候的,或者在那特定一两秒的业务量本来就少,也能承受少量错误的代价,就无所谓了。

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

用 000000 省事

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

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

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

开发了 8 年了,都是 start_time<= time < end_time ,我选 B原因:1 、万一数据库毫秒时间戳呢?2 、开发、处理简单

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

+1

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

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

我的建议是时间戳一把梭

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

#5 +1

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

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

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

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

日期是离散值,时间是连续值

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

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

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

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

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

所有这些问题都是对日期和时间描述精度的问题。以下举例: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 接口上是否能表达“>=”/“<”这些非相等的运算符,如果可以,那么前端可以按精确时间传闭开区间的表达。如果不行,那么只能按精度字符串传,由后端根据精度信息进行理解。

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

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

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

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

通常是左闭右开

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

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

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

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

#74 左闭右开区间,不会有你说的这个问题。

如果业务逻辑的关注点是年、月、日这些“人为编制的序号”,那么更好的做法是抽出年、月、日建索引,检索时在索引字段上按照更符合人类认知的闭区间来筛选,而不是在 timestamp 上划分范围。

=2024-01-04 00:00:00 <2024-01-05 00:00:00

我觉得这个问题不能一刀切,需要根据是否需要“时间”这个概念来确定如何保存和查询。对于数据存储来说,例如,对于“下单时间”这个概念,显然时分秒是必要的,而对于生日这个概念,(通常)时分秒是没有意义的,所以前者需要存储时分秒数据,后者只需要存储年月日数据而时分秒都是 0 或者存储系统支持纯年月日类型的话更好。对于查询,如果是上述下单日期,同样基于业务要求来决定是否需要传入和处理时分秒数据。比如业务有需求要查询某日几点几分到几点几分的数据,则接口参数需要包含时分秒而查询就是基于大于等于并且小于等于的范围。而如果只是需求查询几号到几号的数据,则接口参数不需要包含时分秒或者时分秒为 0 而执行时基于大于等于 BeginDate 并且小于 QueryEndDate + 1 day 的范围。如果是上述生日数据,因为本身存储不带时分秒,所以接口参数也不带时分秒或时分秒为 0 而查询基于大于等于并且小于等于的逻辑。

我写得时候都是小于第二天 0 点的

标准答案前端用户选 59后端程序开 00

当然,如果需求变更,下单时间查询原来只是某天到某天,现在改为某天某时到某天某日,上述做法导致前后端都需要修改,所以如果想避免/预判需求更改导致的代码更改,那么前端依然按照需求要求带时分秒或不带时分秒,而后端则根据传入的参数是否带有时分秒数据而分别进行查询。

这个不是取决于后端的比较逻辑?没听过这里有什么常识。start < x < end || start <= x <= end || start <=x < end 都见过,一个系统保持一致就好了

23:59:59+1

查询范围的话肯定是 B ,不然数据在后面一秒中的话岂不是怎么都查不到了

ISO 8601 有提到 en.wikipedia.org/wiki/ISO_8601An 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.

B 端正确

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 代表一天的开始

这种只要精确到日期不需要时间的应该前端只要传到日就好了吧,后端自己去处理精确时间就好

23:59:59 好,00 表示第二天了。理解上会有误差

时间筛选接口建议用 timeGte, timeGt, timeLte, timeLt 命名这样不会有歧义A 的参数对应 timeGte, timeLteB 的参数对应 timeGte, timeLt

编程上,大多数涉及到集合索引区间时,习惯上都是包括头不包括尾,所以 00 开始,59 结束

所有时间类字段能用时间戳的全用时间戳

小于第二天 00:00:00 的时间戳都是当天。你们时间数据不用时间戳的吗?

en.wikipedia.org/wiki/24-hour_clock#Midnight_00:00_and_24:00In 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.

在编程里面,一般情况都是包左不包右, 所以我原本是习惯 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)

如果用户可以选时分秒,传 endTime:2024-01-04 23:59:59 ,至于 2024-01-04 23:59:59.xx 的数据得要后端考虑怎样处理了。如果用户不能选时分秒,传 endTime:2024-01-04 即可

区间采用前闭后开

最近刚好也碰到这个问题,我猜大部分支持 00:00:00 都是不需要处理展示的,这个时间戳在客户端呈现给客户已经是第二天。显示的时候必须 -1 ,这样的操作很难评。在编写显示和数据处理存在两种时间戳很容易造成困惑和 bug😅

00:00:00 - 23:59:59前端给用户展示也无需再处理, 传给后端就能直接用, 无需计算日期+1 , sql 语句可以直接用 between