V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
xubingok
V2EX  ›  程序员

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

  •  2
     
  •   xubingok · 356 天前 · 17237 次点击
    这是一个创建于 356 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    比如查询昨天的数据,我们一般是传起始时间点. 对接后端 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 条附言  ·  353 天前
    感谢大家的观点.
    最终决定了用 23:59:59.

    主要原因就是前端界面上必须是 23:59:59,用 00:00:00 会让用户看不懂.

    为啥不用时间戳:

    1.我们没有国际化需求,也没有时区需求.

    2.为了后端接口的可复用性.快捷时间范围查询的参数构建由前端来完成.

    会不会漏掉 59s-00s 之间的数据?

    是有可能的.后端开发说他们会转成 23:59:59:99 这样的数据.
    132 条回复    2024-01-08 12:25:04 +08:00
    1  2  
    luomao
        1
    luomao  
       356 天前   ❤️ 36
    用 23:59:59 ,00:00:00 已经是第二天了
    justlazydog
        2
    justlazydog  
       356 天前   ❤️ 1
    都可以,统一就行,常识上一般是 23:59:59 。
    corcre
        3
    corcre  
       356 天前   ❤️ 7
    用 23:59:59, 因为永远不知道谁没事传个日期/加个字段只记日期, 用 datetime 类型还把时间给截了...
    coderzhangsan
        4
    coderzhangsan  
       356 天前
    让后端统一口径,前端没有必要兼容 2 种方案。
    tusj
        5
    tusj  
       356 天前   ❤️ 4
    前闭后开区间,用 00:00:00
    belin520
        6
    belin520  
       356 天前   ❤️ 1
    moment 等时间库的 endOfDay 是 23:59:59
    lscho
        7
    lscho  
       356 天前
    肯定是 23:59:59 ,0 点已经是第二天了
    douxc
        8
    douxc  
       356 天前
    不懂就问:如果记录到了毫秒级,23:59:59 会少 1 秒的数据吗?
    Curtion
        9
    Curtion  
       356 天前   ❤️ 3
    A 更好, 左闭右开并不是一个常识, 大众常识应该左闭右闭, 选 00:00:00 会误以为包括第二天开始, 我们系统很多客户已经反馈过了
    maocat
        10
    maocat  
       356 天前 via iPhone   ❤️ 13
    左开右闭原则 00.00.00 ,如果用 23.59.59 后面还有毫秒呢
    gdfsjunjun
        11
    gdfsjunjun  
       356 天前
    当然是 23:59:59 容易理解,0 点的话会被认为第二天也算
    chronos
        12
    chronos  
       356 天前
    除非你数据库里面存的时间精度只到秒,否则还是用 00:00:00 的好。
    lqhunter233
        13
    lqhunter233  
       356 天前
    @douxc 2024-01-04 23:59:59.999(yyyy-MM-dd HH:mm:ss.SSS)精确到秒可以这样
    leonshaw
        14
    leonshaw  
       356 天前 via Android
    左闭右开
    lqhunter233
        15
    lqhunter233  
       356 天前
    打错了,毫秒
    caiqichang
        16
    caiqichang  
       356 天前   ❤️ 1
    前端只穿日期
    {
    startTime:2024-01-04,
    endTime:2024-01-04
    }
    后端想怎么处理自己在 get 里面处理
    zliea
        17
    zliea  
       356 天前
    左闭右开 +1
    yinmin
        18
    yinmin  
       356 天前 via iPhone
    如果某条记录的时间是 23:59:59.20 呢?

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

    (即使数据库字段的时间秒不含小数,也不应该用 23:59:59 ,避免给自己留坑。谁也不知道几年之后,某个程序员写库的时候引入了秒的小数)
    darkengine
        19
    darkengine  
       356 天前
    如果只是日期区间,我的建议是只传日期,不传时间。以后如果有改动,直接改后端的逻辑就行,前端都不用再发版。
    QKgf555H87Fp0cth
        20
    QKgf555H87Fp0cth  
       356 天前
    用 23:59:59 ,00:00:00 已经是第二天了
    shuax
        21
    shuax  
       356 天前
    @likunyan 23:59:59 没有包含完整的一天
    xhyzidane
        22
    xhyzidane  
       356 天前
    @belin520 实际上是 23:59:59.999
    Kin9
        23
    Kin9  
       356 天前
    24:00:00
    milukun
        24
    milukun  
       356 天前   ❤️ 1
    那你可以制造一条 23:59:59.xx 的数据,然后暴露出这个 bug
    他们后端就统一了
    xuanbg
        25
    xuanbg  
       356 天前
    我在后端都是:start <= x < end 。所以,按日期的话,end 是前端传的日期/时间的下一天的 0 点
    debuggerx
        26
    debuggerx  
       356 天前
    我的习惯是,如果是日期范围的参数,比如 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
        27
    crz  
       356 天前
    toDate(timestamp) == date
    libook
        28
    libook  
       356 天前
    小于等于这个逻辑就是错的,因为并没有完全覆盖时间段,除非业务能接受没覆盖的那一部分被排除在外。
    qzh993
        29
    qzh993  
       356 天前
    很多兄弟都说了,左开右闭原则,前端只传日期,比如要查询 2024-01-04 这天的数据,后端这么处理,2024-01-04 00:00:00 <= create_time < 2024-01-05 00:00:00
    nomytwins
        30
    nomytwins  
       356 天前
    正常都是当天的 23:59:59
    RainCats
        31
    RainCats  
       356 天前
    我倾向于后端 B 的做法,我也这样做
    belin520
        32
    belin520  
       356 天前
    @xhyzidane 点赞,你说这个才是对的,需要更正为你这个
    imldy
        33
    imldy  
       356 天前 via Android
    后端用左闭右开的 0.0.0 ,但给用户显示的业务规则是用 59.59.59
    adoal
        34
    adoal  
       356 天前
    如果做比较的时候能保证传入的时间先 truncate 到确定的精度,可以用两端都闭合。否则只能左闭右开才能保证正确性。
    douxc
        35
    douxc  
       356 天前
    @lqhunter233 #13 这其实可以无限拆下去,如果我想完整包含一天的数据,应该用其他老哥提到的 < endTime ,也就是第二天的 00:00:00
    JimMoen
        36
    JimMoen  
       356 天前
    ```
    {
    startTime: "2024-01-04 00:00:00",
    endTimeBefore: "2024-01-05 00:00:00"
    }
    ```
    Bromine0x23
        37
    Bromine0x23  
       356 天前
    前端来说右闭应该比较符合直觉;后端处理考虑到精度可能变化,用右开的应该比较好
    zackzergzeng
        38
    zackzergzeng  
       356 天前
    得让后端对齐了,前端不能因为后台的不统一写两套代码啊
    Belmode
        39
    Belmode  
       356 天前
    23:59:59 包含 23:59:59.99.....9,前端没必要做特殊处理,左右都是闭区间
    Huelse
        40
    Huelse  
       356 天前
    传给后端查询一般用 00 这种,59 那种还要后端再处理下才能全覆盖
    qzh993
        41
    qzh993  
       356 天前
    @qzh993 左闭右开
    adoal
        42
    adoal  
       356 天前
    另外,除了小数部分之外,23:59:59 还有个问题是表示不了闰秒。

    当然,实际应用中,如果业务不是全天候的,或者在那特定一两秒的业务量本来就少,也能承受少量错误的代价,就无所谓了。
    dif
        43
    dif  
       356 天前
    [2024:01:01 00:00:00, 2024:01:01 23:59:59]
    abc0123xyz
        44
    abc0123xyz  
       356 天前
    用 000000 省事
    tyrone2333
        45
    tyrone2333  
       356 天前
    :default-time="['00:00:00', '23:59:59']"
    ysc3839
        46
    ysc3839  
       356 天前 via Android
    一分钟 60 秒,取值范围是 0-59
    54xavier
        47
    54xavier  
       356 天前   ❤️ 1
    对啊,我想说你要算截止和开始,其实应该用
    00:00:00.000
    23:59:59.999
    这样更佳
    nextvay
        48
    nextvay  
       356 天前
    开发了 8 年了,都是 start_time<= time < end_time ,我选 B

    原因:
    1 、万一数据库毫秒时间戳呢?
    2 、开发、处理简单
    JingXiao
        49
    JingXiao  
       356 天前
    日期区间不是只传日期就完事了么,如果非要加时分秒这种也是时间组件加上时分秒可选让用户选
    doctli
        50
    doctli  
       356 天前
    @tusj +1
    snylonue
        51
    snylonue  
       356 天前
    23:59:59

    好几次因为 00:00:00 没赶上 ddl (
    vagusss
        52
    vagusss  
       356 天前
    左闭右开就完事儿了, 到 59 秒是什么鬼
    Seulgi
        53
    Seulgi  
       356 天前
    我的建议是时间戳一把梭
    focuxin
        54
    focuxin  
       356 天前
    /**
    * 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
        55
    yyancy517  
       356 天前
    @tusj #5 +1
    wangtian2020
        56
    wangtian2020  
       356 天前
    dayjs().endOf('d') 调用出来是啥就是啥,我不管
    brader
        57
    brader  
       356 天前
    单说程序、技术,看完大家说的,其实都有道理,讨论不出一个明确的对错的。我们不妨把视野再放大一点,从页面使用人员的惯性思维去反推理解:
    比如使用人员查询 1 月份报表,正常应该是点开始 1 月 1 日,结束 1 月 31 日。使用人员潜意识里,其实会认为他点了 1 月 31 日,就应该包含 31 日的数据的,而不是让他去点 2 月 1 日。
    所以引导出:就给 1 月 31 日最后一刻的时间戳没问题的。
    label
        58
    label  
       356 天前
    前端正常只需要到日, 或者时分, 怎么说也不用精确到秒, 这应该由后端来做
    label
        59
    label  
       356 天前
    @Belmode 数据库是不包含的, 如果前后端都不处理, 是查不到的
    leonshaw
        60
    leonshaw  
       356 天前
    @brader 日期是离散值,时间是连续值
    yhnbgfd
        61
    yhnbgfd  
       356 天前
    首先 23:59:59 是<错误>的时间区间判断条件, 我们需要先排除错误答案, 修复程序潜在 bug, 而不是给错误找理由.
    liubaicai
        62
    liubaicai  
       356 天前
    0:0:0 的时间戳-1 就行了
    adoal
        63
    adoal  
       356 天前
    @brader 也就是说,其实 Calendar 相关的东西是跟 time 不同逻辑的数据类型,它不是物理意义上的时间长度和时刻坐标,而是人为的、人文的编号。如果要处理日历上的日期,复杂度远比“存储用 UTC timestamp ,给人看的交给前端去做转换”复杂得多。以前在一个教务系统招生模块里就是因为大意用普通的时间类型保存生日而碰到了中国实行夏时制的年份里出生在夏时制边缘日子里的学生因为算出来的“年龄”不对而无法报名的问题。
    lambdaq
        64
    lambdaq  
       356 天前
    23:59:59.999 秒的时候客户下了一个订单呢?
    brader
        65
    brader  
       356 天前
    @leonshaw 传日期和时间戳一样的意思,看你细到什么粒度,反正都能互转,就看后端接口喜欢用哪种了。最终面临的都是楼主说的这个问题
    mytharcher
        66
    mytharcher  
       356 天前   ❤️ 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
        67
    quandou  
       356 天前
    00:00:00 已经是 after day 了 23:59:59 合理
    leonshaw
        68
    leonshaw  
       356 天前
    @brader 问题就在“看你细到什么粒度”,这要求在 API 上约定粒度,并且影响系统内部实现。
    Philippa
        69
    Philippa  
       356 天前 via iPhone
    前后端判断用左闭右开,因为还有 1s 的间隙。显示用 23:59:59
    Bingchunmoli
        70
    Bingchunmoli  
       356 天前 via Android
    @douxc 会的..,不要问我是怎么知道的
    kevin1452
        71
    kevin1452  
       356 天前
    通常是左闭右开
    Bingchunmoli
        72
    Bingchunmoli  
       356 天前 via Android
    @brader 但是最后一刻时间戳你是精确到秒还是毫秒还是微秒,,实际上不如处理一下左闭又开,不然线上确实会丢这一段数据
    lovelylain
        73
    lovelylain  
       356 天前 via Android
    达成共识都可以,待讨论的话建议双闭区间,因为这个有效期可能涉及展示,开区间展示时需要减 1 秒处理
    fionasit007
        74
    fionasit007  
       356 天前
    00:00:00 算是第二天了,以后会是个大坑,比如抖音的巨量后台你用 00:00:00 会查出第二天这个点的数据,因为数据量太大 00:00:00 也会产生数据的
    fionasit007
        75
    fionasit007  
       356 天前
    @Seulgi 这个时候有问题又回到原地了 那到底是 59 的时间戳还是 00 的时间戳呢
    Huelse
        76
    Huelse  
       356 天前
    @fionasit007 #74 左闭右开区间,不会有你说的这个问题。
    adoal
        77
    adoal  
       356 天前
    @mytharcher 如果业务逻辑的关注点是年、月、日这些“人为编制的序号”,那么更好的做法是抽出年、月、日建索引,检索时在索引字段上按照更符合人类认知的闭区间来筛选,而不是在 timestamp 上划分范围。
    sarices
        78
    sarices  
       356 天前
    >=2024-01-04 00:00:00 <2024-01-05 00:00:00
    Fule
        79
    Fule  
       356 天前
    我觉得这个问题不能一刀切,需要根据是否需要“时间”这个概念来确定如何保存和查询。

    对于数据存储来说,例如,对于“下单时间”这个概念,显然时分秒是必要的,而对于生日这个概念,(通常)时分秒是没有意义的,所以前者需要存储时分秒数据,后者只需要存储年月日数据而时分秒都是 0 或者存储系统支持纯年月日类型的话更好。

    对于查询,如果是上述下单日期,同样基于业务要求来决定是否需要传入和处理时分秒数据。比如业务有需求要查询某日几点几分到几点几分的数据,则接口参数需要包含时分秒而查询就是基于大于等于并且小于等于的范围。而如果只是需求查询几号到几号的数据,则接口参数不需要包含时分秒或者时分秒为 0 而执行时基于大于等于 BeginDate 并且小于 QueryEndDate + 1 day 的范围。如果是上述生日数据,因为本身存储不带时分秒,所以接口参数也不带时分秒或时分秒为 0 而查询基于大于等于并且小于等于的逻辑。
    sephiroka
        80
    sephiroka  
       356 天前
    我写得时候都是小于第二天 0 点的
    5sheep
        81
    5sheep  
       356 天前
    标准答案
    前端用户选 59
    后端程序开 00
    Fule
        82
    Fule  
       356 天前
    当然,如果需求变更,下单时间查询原来只是某天到某天,现在改为某天某时到某天某日,上述做法导致前后端都需要修改,所以如果想避免/预判需求更改导致的代码更改,那么前端依然按照需求要求带时分秒或不带时分秒,而后端则根据传入的参数是否带有时分秒数据而分别进行查询。
    yannxia
        83
    yannxia  
       356 天前
    这个不是取决于后端的比较逻辑?没听过这里有什么常识。
    start < x < end || start <= x <= end || start <=x < end 都见过,一个系统保持一致就好了
    polo3584
        84
    polo3584  
       356 天前
    23:59:59+1
    Lexgni
        85
    Lexgni  
       356 天前
    查询范围的话肯定是 B ,不然数据在后面一秒中的话岂不是怎么都查不到了
    lovedebug
        86
    lovedebug  
       356 天前
    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.
    Atomo
        87
    Atomo  
       356 天前 via Android
    B 端正确
    lovedebug
        88
    lovedebug  
       356 天前
    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 代表一天的开始
    vacuitym
        89
    vacuitym  
       356 天前
    这种只要精确到日期不需要时间的应该前端只要传到日就好了吧,后端自己去处理精确时间就好
    haizi
        90
    haizi  
       356 天前
    23:59:59 好,00 表示第二天了。理解上会有误差
    xjpicism
        91
    xjpicism  
       356 天前
    时间筛选接口建议用 timeGte, timeGt, timeLte, timeLt 命名
    这样不会有歧义
    A 的参数对应 timeGte, timeLte
    B 的参数对应 timeGte, timeLt
    iX8NEGGn
        92
    iX8NEGGn  
       356 天前
    编程上,大多数涉及到集合索引区间时,习惯上都是包括头不包括尾,所以 00 开始,59 结束
    alexhx
        93
    alexhx  
       356 天前
    所有时间类字段能用时间戳的全用时间戳
    codingguy
        94
    codingguy  
       356 天前
    小于第二天 00:00:00 的时间戳都是当天。
    你们时间数据不用时间戳的吗?
    kjcm150
        95
    kjcm150  
       356 天前
    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.
    zpf124
        96
    zpf124  
       356 天前
    在编程里面,一般情况都是包左不包右, 所以我原本是习惯 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)
    zhandi4
        97
    zhandi4  
       356 天前
    如果用户可以选时分秒,传 endTime:2024-01-04 23:59:59 ,至于 2024-01-04 23:59:59.xx 的数据得要后端考虑怎样处理了。如果用户不能选时分秒,传 endTime:2024-01-04 即可
    SD10
        98
    SD10  
       356 天前 via iPhone
    区间采用前闭后开
    icyliang
        99
    icyliang  
       356 天前
    最近刚好也碰到这个问题,我猜大部分支持 00:00:00 都是不需要处理展示的,这个时间戳在客户端呈现给客户已经是第二天。显示的时候必须 -1 ,这样的操作很难评。在编写显示和数据处理存在两种时间戳很容易造成困惑和 bug😅
    shenjinpeng
        100
    shenjinpeng  
       356 天前
    00:00:00 - 23:59:59

    前端给用户展示也无需再处理, 传给后端就能直接用, 无需计算日期+1 , sql 语句可以直接用 between
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1087 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 18:59 · PVG 02:59 · LAX 10:59 · JFK 13:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.