JavaRush /Java 博客 /Random-ZH /Java 终于提供了一个直观、强大的处理时间和日期的库(第 1 部分)。
theGrass
第 24 级
Саратов

Java 终于提供了一个直观、强大的处理时间和日期的库(第 1 部分)。

已在 Random-ZH 群组中发布
Java     终于有了一种直观、可靠的方式来处理日期和时间。日期和时间的原则是许多应用程序的基础。出生日期、租赁日期、活动时间和商店营业时间等各种信息都基于日期和时间,但Java SE没有提供使用它们的便捷方法。从Java SE 8开始,出现了一组java.time包,它们提供了一个结构良好的API来处理日期和时间。
背景
    当Java首次出现时,在版本1.0中,唯一用于处理日期和时间的类是java.util.Date。开发人员注意到的第一件事是它并不代表“日期”。事实上,它代表的是从 1970 年 1 月 1 日开始测量的一个时刻,精确到毫秒。然而,基于DatetoString()方法显示机器的java设置中指定的时区的日期和时间,一些开发人员错误地认为Date可以与时区一起工作。事实证明修复这个类非常困难(或者说非常懒),以至于在1.1版本中我们不得不添加一个新类 - java.util.Calendar。不幸的是,Calendar类结果并不比Date好多少。 以下是其实施中存在的一小部分问题:
  • 多变。日期和时间等类应该是不可变的。
  • 偏移量。Date 中的年份从 1900 开始,两个类别中的月份都从零开始。
  • 名字。Date 实际上并不是“日期”,Calendar 也不是日历。
  • 格式化。格式化仅适用于日期,不适用于日历,并且不是线程安全的。
    2001年, Joda-Time项目创建。他的目标很简单 - 创建一个高质量的库来处理 Java中的日期和时间。 虽然花了一些时间,但1.0版本最终还是发布了,并很快变得非常流行和广泛使用。随着时间的推移,开发人员越来越要求将具有类似便利性的库作为 JDK的一部分提供。在来自巴西的 Michael Nascimento Santos 的参与下,启动了 JSR-310项目,这是创建和集成用于在 JDK中处理日期和时间的新 API的官方流程。
审查
新的 java.time API包含 5 个包:
  • java.time - 包含用于存储值的对象的基础包
  • java.time.chrono - 提供对不同日历的访问
  • java.time.format - 日期和时间格式化和识别
  • java.time.temporal - 低级库和高级功能
  • java.time.zone - 用于处理时区的类
    大多数开发人员将主要使用基础包和格式,也许还有 java.time.temporal。因此,即使添加了 68 种新类型,开发人员也只会使用其中的大约三分之一。
日期
LocalDate     类是新 API中最重要的类之一。它包含一个表示日期的不可变值。您无法设置时间或时区。“local”这个名字你可能对 Joda-Time很熟悉,最初来自 ISO-8601标准。这恰恰意味着没有时区。本质上, LocalDate是对日期的描述,例如“2014 年 4 月 5 日”。该日期的实际时间将根据您所在的时区而有所不同。例如,在澳大利亚,该日期将比伦敦早 10 小时,比旧金山早 18 小时。 LocalDate类具有所有常用的方法: LocalDate date = LocalDate.of(2014, Month.JUNE, 10); int year = date.getYear(); // 2014 Month month = date.getMonth(); // Июнь int dom = date.getDayOfMonth(); // 10 DayOfWeek dow = date.getDayOfWeek(); // Вторник int len = date.lengthOfMonth(); // 30 (дней в Июне) boolean leap = date.isLeapYear(); // false (не високосный год)     在我们的示例中,我们看到使用工厂方法创建的日期(所有构造函数都是私有的)。接下来我们向对象询问一些数据。请注意, MonthDayOfWeek枚举旨在使代码更具可读性和可靠性。在下面的例子中我们将看到如何修改日期。由于类是不可变的,因此结果将是新对象,但原始对象将保持原样。 LocalDate date = LocalDate.of(2014, Month.JUNE, 10); date = date.withYear(2015); // 2015-06-10 date = date.plusMonths(2); // 2015-08-10 date = date.minusDays(1); // 2015-08-09     这些都是相对简单的更改,但通常您需要进行更复杂的日期修改。 java.time API中有一个特殊的机制- TemporalAdjuster 。它的目的是提供一个内置工具,允许您操作日期,例如获取与该月最后一天相对应的对象。其中一些包含在 API中,但您可以添加自己的。使用修饰符非常简单,但它需要静态导入: import static java.time.DayOfWeek.* import static java.time.temporal.TemporalAdjusters.* LocalDate date = LocalDate.of(2014, Month.JUNE, 10); date = date.with(lastDayOfMonth()); date = date.with(nextOrSame(WEDNESDAY));     使用修饰符可以极大地简化您的代码。没有人愿意看到大量的手动日期操作。如果某种类型的日期操作在您的项目中多次发生,请编写您自己的修改器,您的团队可以将其用作已编写和测试的组件。
时间和日期作为值
花一点时间了解LocalDate 的价值     的原因是值得的。值是完全可以互换的简单数据类型;当它们相等时,对象的身份就变得毫无意义。值类的一个典型示例是 String。我们使用 equals()比较字符串,并且与 ==运算符比较时,我们不关心对象是否相同。大多数处理日期和时间的类也是值。因此,如文档中所述,使用 ==运算符比较它们是一个坏主意。对于那些有兴趣了解更多信息的人,请查看我最近对 ​​VALJO 的 定义,它概述了Java中的值对象必须遵循的一组严格规则,包括不变性、工厂方法以及 equals()hashCodetoString()的正确定义,和 compareTo . ()
替代日历
LocalDate     类与 java.time中的所有主类一样,绑定到单个日历 - 正如 ISO-8601标准中所述。标准 8601 描述了全球标准日历,也称为公历。标准年包括 365 天,闰年为 366 天。每四年为闰年,除非能被 100 整除或能被 400 整除。为了计算方便,新纪元第一年之前的一年被视为零。作为默认系统的第一个结果是结果并不总是与使用 GregorianCalendar计算的结果匹配。 GregorianCalendar类对于 1582 年 10 月 15 日之前的所有日期都有一个内置的儒略系统切换。在儒略制度中,每四年为闰年,无一例外。问题是,既然从一种系统过渡到另一种系统是历史事实,为什么 java.time不对其进行建模呢?是的,因为不同的国家在不同的时间转向公历系统,并且仅考虑梵蒂冈过渡的日期,我们将得到大多数其他国家的错误数据。例如,大英帝国,包括北美殖民地,在近200年后的1752年9月14日改用公历。俄罗斯直到 1918 年 2 月 14 日才更改日历,而瑞典的过渡总体上是一个模糊的问题。因此,1918 年之前的日期的实际含义在很大程度上取决于具体情况。 LocalDate代码的作者做出了一个完全理性的决定,根本不对从儒略历到公历的过渡进行建模,以避免出现差异。在所有核心类中使用 ISO-8601作为默认日历的第二个后果是需要一组附加的类来处理剩余的日历。 年表界面是使用替代日历的基础,允许您按区域设置名称查找所需的日历。 Java 8附带4 个附加日历 - 泰国佛教日历、明国日历(台湾日历)、日本日历和伊斯兰日历。其他日历可能附带程序。每个日历都有一个特殊的日期类,例如 ThaiBuddhistDateMinguoDateJapaneseDateHijrahDate。在高度本地化的应用程序中使用它们是有意义的,例如日本政府的应用程序。另一个接口 ChronoLocalDate与 LocalDate一起用作上述四个类的核心抽象 ,它允许您编写独立于所使用的日历类型的代码。尽管这种抽象存在,但不建议使用它。理解为什么不推荐这种抽象对于理解整个 java.time API 的工作原理非常重要。最重要的是,大多数在没有参考特定日历的情况下编写的代码最终都无法正常工作。例如,你无法确定一年有 12 个月,但有些开发人员添加了 12 个月,并认为他们添加了一整年。您无法确定所有月份都包含大致相同的天数 - 在科普特历中,有 12 个月有 30 天,而 1 个月有 5 或 6 天。另外,你不能确定下一年的数字会比当前的数字多 1,因为在日本历中,年份是从下一任天皇宣布登基开始计算的(在这种情况下,即使是同月的 2 天)可以属于不同年份)。编写可同时处理多个日历的高质量且可工作的代码的唯一方法是,必须使用标准日历来编写对日期和时间执行所有操作的代码核心,并且仅在输入/输出日期时进行是否应该转换为其他日历系统。也就是说,建议使用 LocalDate来存储应用程序中的所有日期操作。仅当本地化输入和输出日期时,才应使用 ChronoLocalDate,它通常是从用户配置文件中存储的日历类获取的。确实,大多数应用程序不需要如此严格的本地化。如果您需要对本章中描述的所有内容进行更详细的解释,欢迎使用 ChronoLocalDate类的 文档。      文章继续 原创文章     
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION