【问题标题】:How can I represent and operate on time values greater than 24:00 in Java?如何在 Java 中表示和操作大于 24:00 的时间值?
【发布时间】:2025-11-29 08:50:01
【问题描述】:

我目前正在使用大于 24:00 的时间值表示午夜之后仍与前一天的详细信息相关联的时间值的应用程序域中做一些工作。例如,它可能使用星期一的 25:15 来表示星期二的凌晨 1:15,因为从域的角度来看,这个值仍然与星期一的数据相关联。

24-hour clock 的*文章中简要提到了这种类型的时间使用:

24:00 之后的时间表示法(例如 24:01 或 25:00 而不是 00:01 或 01:00)不常用,相关标准也未涵盖。但是,在营业时间超过午夜的 [各个国家/地区] 的某些特殊情况下,它们偶尔会被使用,例如广播电视制作和调度。

Java 提供了LocalTime 模型类来表示时间。但是,LocalTime 被限制在给定日期的午夜(包括)和第二天的午夜(不包括)之间,即在[0:00-23:59:59.999999999] 的范围内。

Java Time API 的编写很灵活,但核心 API 中排除了更专业的概念。正如 Java 8 日期和时间库的主要作者策划的 ThreeTen Extra 项目中所述:

并非每一段日期/时间逻辑都适用于 JDK。有些概念过于专业或过于庞大而无法融入其中。

我没有运气找到现有类型或其他直接方法来通过核心 Java 库或ThreeTen Extra 对这种约束较少的时间类型进行建模。但是,我很可能遗漏了一些东西,也许是相当明显的东西。

如何在 Java 中模拟这种约束较少的时间,理想情况下使用 java.time API 或其扩展,例如 ThreeTen-Extra 项目?我希望能够使用它以文本方式表示时间值(“25:15”)并理想地使用它执行时间计算(例如,将“星期一 @ 25:15”转换为“星期二 @ 1:15 AM”)。

如果没有直接的方法,我可能最终会在 Threeten-Extra 项目中提出问题。

【问题讨论】:

  • 我清楚地看到了需要。我曾经在开发一个公交车时刻表系统。我们在一天中的某个时间从 0:00 到 36:00 有自己的本土课程,精度为 1 分钟,因为夜间巴士通常被计划为前一天时间表的一部分(我从未见过它用于超过4 AM (28:00),但宁愿使范围太大而不是太小)。显然,我们拥有平时打印的功能。当一辆公共汽车23:58出发,0:03到达下一站时,时间表用户无需解释,他们理解。我们只需要在内部进行区分。

标签: java time java-time


【解决方案1】:

我的第一个想法是将其存储为 Duration 对象。当您想要显示值时,您可以轻松地使用该类上的 getter 来构造 HH:mm 字符串,或者使用类似 Apache Commons DurationFormatUtils 的东西。

LocalDateLocalDateTime 类支持加减持续时间等操作。

【讨论】:

    【解决方案2】:

    不,不存在

    我深信,三十家族中的任何一个班级或其他地方的常用班级都没有支持你想要的东西。我的搜索最接近的是 Time4J 库的 DayCycles 类(链接在底部)。它由一天中的时间(PlainTime)和相对于未指定日期的天数组成,所以你要求什么;但它专门用于返回从 PlainTime 中添加或减去时间的结果,并且不提供任何自己的功能。

    自旋

    我不认为开发自己的可以与 java.time 互操作的类会太难。正如 Mark B 所建议的那样,可能以 java.time.Duration 为基础,具体取决于更精确的要求。

    如果您对这些有苛刻的要求,解析和格式化可能是最难的部分。可能值得从 Joda-Time 库的源代码中汲取灵感,因为其中包括格式化和解析持续时间的工具。

    另一个灵感来源是java.time.LocalTime

    这里是前几位:

    public class SpecializedTime {
    
        private Duration timeSince0000;
        
        private SpecializedTime(Duration timeSince0000) {
            this.timeSince0000 = timeSince0000;
        }
    
        static SpecializedTime parse(String text, DateTimeFormatter formatter) {
            ParsePosition position = new ParsePosition(0);
            TemporalAccessor parsed = formatter.parseUnresolved(text, position);
            if (position.getErrorIndex() != -1) {
                throw new DateTimeParseException("Parse error", text, position.getErrorIndex());
            }
            if (position.getIndex() != text.length()) {
                throw new DateTimeParseException("Unparsed text", text, position.getIndex());
            }
            if (! parsed.isSupported(ChronoField.HOUR_OF_DAY)) {
                throw new DateTimeParseException("Cannot resolve", text, 0);
            }
            
            Duration time = Duration.ofHours(parsed.getLong(ChronoField.HOUR_OF_DAY));
            if (parsed.isSupported(ChronoField.MINUTE_OF_HOUR)) {
                int minuteOfHour = parsed.get(ChronoField.MINUTE_OF_HOUR);
                // Should validate, 0..59
                time = time.plusMinutes(minuteOfHour);
            }
            // Same for seconds and nanoseconds
            
            return new SpecializedTime(time);
        }
        
        @Override
        public String toString() {
            return String.format("%02d:%02d", timeSince0000.toHours(), timeSince0000.toMinutesPart());
        }
    
    }
    

    演示:

            DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("H:m");
            SpecializedTime st = SpecializedTime.parse("25:15", timeFormatter);
            System.out.println(st);
    

    输出:

    25:15

    链接

    Documentation of DayCycles 形成 Time4J

    【讨论】:

      【解决方案3】:

      目前在 ThreeTen-Extra 或核心 Java 库中没有直接模拟此概念的类型。但是,现在 ThreeTen-Extra 中存在一个功能请求,请求此功能 (threeten-extra#169)。

      ThreeTen-Extra 项目维护者 Stephen Colebourne has commented 认为这将是对库的明智补充,因此这似乎主要取决于何时或是否有人加紧实施该功能。

      Stephen Colebourne 还提出了一些在库中实现此功能的方法。这些建议的方法之一可以用作此问题的自定义解决方案的一部分。

      它可能是LocalTime 和天数的组合,或者更接近LocalTime。多种设计选择。

      【讨论】:

        最近更新 更多