|
@@ -0,0 +1,301 @@
|
|
|
+---
|
|
|
+title: 提示Java基础之如何取舍Joda与 Java8 日期库
|
|
|
+date: 2020-02-28T14:35:06.258Z
|
|
|
+---
|
|
|
+在 Java8 以前,时间和日期的类库很难用,而且有线程安全等诸多问题。
|
|
|
+
|
|
|
+Joda time 弥补了 Java 在这方面的不足,但是在 Java8 时,增加了 `java.time` 包,对 Java 在日期 API 方面的进行了增强,这些代码实现了 JSR-310 的标准。Joda 的官方推荐迁移到 Java8 的时间类库上来。
|
|
|
+
|
|
|
+下面来详细对比对比一下两个类库,看看 Java8 的日期 API 是否能真正替代 Joda time。
|
|
|
+
|
|
|
+## 基础概念对比
|
|
|
+
|
|
|
+Joda Date 的核心概念,这些概念在 java time 中基本也能找到对应:
|
|
|
+
|
|
|
+### instant
|
|
|
+
|
|
|
+表示一个时刻,使用从 1970-01-01 00:00:00 至今的毫秒数表示
|
|
|
+
|
|
|
+Joda time:
|
|
|
+
|
|
|
+```
|
|
|
+DateTime dt = new DateTime();
|
|
|
+Instant instant = dt.toInstant();
|
|
|
+```
|
|
|
+
|
|
|
+Java time:
|
|
|
+
|
|
|
+```
|
|
|
+Clock clock = Clock.systemDefaultZone();
|
|
|
+Instant instant = clock.instant();
|
|
|
+```
|
|
|
+
|
|
|
+### interval
|
|
|
+
|
|
|
+表示两个 instant 之间的间隔,左闭右开。
|
|
|
+
|
|
|
+Joda time:
|
|
|
+
|
|
|
+```
|
|
|
+DateTime dt = new DateTime();
|
|
|
+DateTime dt1 = new DateTime();
|
|
|
+Interval interval = new Interval(dt.toInstant(), dt1.toInstant());
|
|
|
+```
|
|
|
+
|
|
|
+java time 中没有提供类似的 API,因为 JSR-310 标准中没有这个概念。
|
|
|
+
|
|
|
+### duration
|
|
|
+
|
|
|
+用毫秒表示的时间段,通常从 interval 获得 Joda time:
|
|
|
+
|
|
|
+```
|
|
|
+DateTime dt = new DateTime();
|
|
|
+DateTime dt1 = new DateTime();
|
|
|
+Interval interval = new Interval(dt.toInstant(), dt1.toInstant());
|
|
|
+Duration duration = interval.toInstant();
|
|
|
+```
|
|
|
+
|
|
|
+Java time:
|
|
|
+
|
|
|
+```
|
|
|
+LocalDateTime l1 = LocalDateTime.now();
|
|
|
+LocalDateTime l2 = LocalDateTime.now();
|
|
|
+Period period = Period.between(l1.toLocalDate(), l2.toLocalDate());
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+### period
|
|
|
+
|
|
|
+同样表示时间段,比如 3年,5个月,而 duration 使用毫秒表示
|
|
|
+
|
|
|
+Joda time:
|
|
|
+
|
|
|
+```
|
|
|
+DateTime dt1 = new DateTime();
|
|
|
+DateTime dt2 = new DateTime();
|
|
|
+Period period = Period.fieldDifference(dt1.toLocalDateTime(), dt2.toLocalDateTime());
|
|
|
+```
|
|
|
+
|
|
|
+Java time:
|
|
|
+
|
|
|
+```
|
|
|
+LocalDateTime l1 = LocalDateTime.now();
|
|
|
+LocalDateTime l2 = LocalDateTime.now();
|
|
|
+Period period = Period.between(l1.toLocalDate(), l2.toLocalDate());
|
|
|
+```
|
|
|
+
|
|
|
+年表,这是 joda-time 设计的基础
|
|
|
+
|
|
|
+Joda time:
|
|
|
+
|
|
|
+```
|
|
|
+DateTime dt = <span class="hljs-keyword">new</span> DateTime();
|
|
|
+Chronology chronology = dt.getChronology();
|
|
|
+<span class="copy-code-btn">复制代码</span>
|
|
|
+```
|
|
|
+
|
|
|
+Java time:
|
|
|
+
|
|
|
+```
|
|
|
+LocalDateTime localDateTime = LocalDateTime.now();
|
|
|
+Chronology ch = localDateTime.getChronology();
|
|
|
+<span class="copy-code-btn">复制代码</span>
|
|
|
+```
|
|
|
+
|
|
|
+表示时区。
|
|
|
+
|
|
|
+Joda time:
|
|
|
+
|
|
|
+```
|
|
|
+DateTime dt = <span class="hljs-keyword">new</span> DateTime();
|
|
|
+DateTimeZone dateTimeZone = dt.getZone();
|
|
|
+Set<string> zones = DateTimeZone.getAvailableIDs();
|
|
|
+<span class="copy-code-btn">复制代码</span></string>
|
|
|
+```
|
|
|
+
|
|
|
+Java time:
|
|
|
+
|
|
|
+```
|
|
|
+Clock clock = Clock.systemDefaultZone();
|
|
|
+ZoneId zoneId = clock.getZone();
|
|
|
+Set<string> zones = ZoneId.getAvailableZoneIds();
|
|
|
+<span class="copy-code-btn">复制代码</span></string>
|
|
|
+```
|
|
|
+
|
|
|
+从上面可以看到,除了 JSR-310 中没有的 Interval 的定义之外,这两个库在基础概念方面的实现相差不大。
|
|
|
+
|
|
|
+> 因为 Unix 系统从 1970-01-01 00:00:00 开始计时,这个时间也称之为 Epoch Time,后续使用 Unix 的这种计时方式。
|
|
|
+
|
|
|
+## 具体使用
|
|
|
+
|
|
|
+Joda time 依赖 JDK5 及后续版本,没有额外的依赖。
|
|
|
+
|
|
|
+为了起到对比的效果,挑几个比较常用的场景进行对比:
|
|
|
+
|
|
|
+* 获取 1970 至今的毫秒数
|
|
|
+* 获取当前时间
|
|
|
+* 获取年、月、日、星期几
|
|
|
+* 日期的增减
|
|
|
+* 日期的格式化
|
|
|
+
|
|
|
+在代码中,经常会使用这个功能来表示唯一性:
|
|
|
+
|
|
|
+Joda time:
|
|
|
+
|
|
|
+```
|
|
|
+DateTime dt = <span class="hljs-keyword">new</span> DateTime();
|
|
|
+<span class="hljs-keyword">long</span> mills = dt.getMillis();
|
|
|
+<span class="copy-code-btn">复制代码</span>
|
|
|
+```
|
|
|
+
|
|
|
+Java time:
|
|
|
+
|
|
|
+```
|
|
|
+Clock clock = Clock.systemDefaultZone();
|
|
|
+<span class="hljs-keyword">long</span> mills = clock.millis();
|
|
|
+<span class="copy-code-btn">复制代码</span>
|
|
|
+```
|
|
|
+
|
|
|
+这块两个库没有太大的差异:
|
|
|
+
|
|
|
+Joda time:
|
|
|
+
|
|
|
+```
|
|
|
+DateTime dt = <span class="hljs-keyword">new</span> DateTime();
|
|
|
+LocalDateTime localDateTime = dt.toLocalDateTime();
|
|
|
+<span class="copy-code-btn">复制代码</span>
|
|
|
+```
|
|
|
+
|
|
|
+Java time:
|
|
|
+
|
|
|
+```
|
|
|
+LocalDateTime localDateTime = LocalDateTime.now();
|
|
|
+<span class="copy-code-btn">复制代码</span>
|
|
|
+```
|
|
|
+
|
|
|
+Joda time:
|
|
|
+
|
|
|
+```
|
|
|
+DateTime dt = <span class="hljs-keyword">new</span> DateTime();
|
|
|
+<span class="hljs-keyword">int</span> dayOfYear = dt.getDayOfYear();
|
|
|
+<span class="hljs-keyword">int</span> dayOfMonth = dt.getDayOfMonth();
|
|
|
+<span class="hljs-keyword">int</span> dayOfWeek = dt.getDayOfWeek();
|
|
|
+<span class="hljs-keyword">int</span> hourOfDay = dt.getHourOfDay();
|
|
|
+<span class="copy-code-btn">复制代码</span>
|
|
|
+```
|
|
|
+
|
|
|
+Java time:
|
|
|
+
|
|
|
+```
|
|
|
+Clock clock = Clock.systemDefaultZone();
|
|
|
+LocalDateTime localDateTime = LocalDateTime.now(clock);
|
|
|
+<span class="hljs-keyword">int</span> dayOfYear = localDateTime.getDayOfYear();
|
|
|
+<span class="hljs-keyword">int</span> dayOfMonth = localDateTime.getDayOfMonth();
|
|
|
+<span class="hljs-keyword">int</span> dayOfWeek = localDateTime.getDayOfWeek().getValue();
|
|
|
+<span class="hljs-keyword">int</span> hourOfDay = localDateTime.getHour();
|
|
|
+<span class="copy-code-btn">复制代码</span>
|
|
|
+```
|
|
|
+
|
|
|
+获取这些值两个库也没有太大的差异,但是对于一些场景,比如我想获得 "星期四" 这个字符串。 在 Joda 库中,可以 `dt.dayOfWeek().getAsShortText(); // 星期四` 这样获得。在 Java 中, `localDateTime.getDayOfWeek().name(); //THURSDAY` 只能获取到英文。
|
|
|
+
|
|
|
+Joda time 在本地化方面比 Java time做的更好。
|
|
|
+
|
|
|
+Joda time:
|
|
|
+
|
|
|
+```
|
|
|
+DateTime dt = <span class="hljs-keyword">new</span> DateTime();
|
|
|
+dt = dt.plusDays(<span class="hljs-number">2</span>);
|
|
|
+dt = dt.plusHours(<span class="hljs-number">5</span>);
|
|
|
+dt = dt.minusDays(<span class="hljs-number">1</span>);
|
|
|
+dt = dt.minusHours(<span class="hljs-number">2</span>);
|
|
|
+<span class="copy-code-btn">复制代码</span>
|
|
|
+```
|
|
|
+
|
|
|
+Java time:
|
|
|
+
|
|
|
+```
|
|
|
+LocalDateTime localDateTime = LocalDateTime.now();
|
|
|
+localDateTime = localDateTime.plusDays(<span class="hljs-number">2</span>);
|
|
|
+localDateTime = localDateTime.plusHours(<span class="hljs-number">2</span>);
|
|
|
+localDateTime = localDateTime.minusDays(<span class="hljs-number">1</span>);
|
|
|
+localDateTime = localDateTime.minusHours(<span class="hljs-number">1</span>);
|
|
|
+<span class="copy-code-btn">复制代码</span>
|
|
|
+```
|
|
|
+
|
|
|
+日期格式化是日常使用最频繁的功能,下面来对比一下这两者的区别。
|
|
|
+
|
|
|
+Joda time:
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+DateTimeFormatter formatter = DateTimeFormat.forPattern(<span class="hljs-string">"yyyy-MM-dd HH:mm:ss"</span>);
|
|
|
+DateTime dt = <span class="hljs-keyword">new</span> DateTime();
|
|
|
+System.out.println(dt.toString(formatter));
|
|
|
+
|
|
|
+String dateFormat = <span class="hljs-string">"yyyy-MM-dd HH:mm:ss"</span>;
|
|
|
+System.out.println(dt.toString(dateFormat));
|
|
|
+<span class="copy-code-btn">复制代码</span>
|
|
|
+```
|
|
|
+
|
|
|
+Java time:
|
|
|
+
|
|
|
+```
|
|
|
+DateTimeFormatter formatter = DateTimeFormatter.ofPattern(<span class="hljs-string">"yyyy-MM-dd HH:mm:ss"</span>);
|
|
|
+LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(formatter.format(localDateTime));
|
|
|
+<span class="copy-code-btn">复制代码</span>
|
|
|
+```
|
|
|
+
|
|
|
+通过上面的对比,可以发现这两个类库都可以完成相同的功能。虽然在细节上是有一些细微的差别。
|
|
|
+
|
|
|
+java.util.Date 是 Java 中最早的日期类,后来就不推荐使用这个类了,java.util.Calendar 用来替代 Date。Calendar 有 Date 的所有功能,并且提供了更加丰富的获取年月日的 API。
|
|
|
+
|
|
|
+Calendar 是一个虚拟类,GregorianCalendar 则是 Calendar 的实现类。
|
|
|
+
|
|
|
+Java time 与 java.util 下的时间类相互转化,可以将 Date 或者 Calendar 转化成 Java time 中的 LocalDateTime.
|
|
|
+
|
|
|
+java.util.Date 转 java.time.LocalDateTime:
|
|
|
+
|
|
|
+```
|
|
|
+Date date = <span class="hljs-keyword">new</span> Date();
|
|
|
+LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
|
|
|
+<span class="copy-code-btn">复制代码</span>
|
|
|
+```
|
|
|
+
|
|
|
+java.util.Carlendar 转 java.time.LocalDateTime:
|
|
|
+
|
|
|
+```
|
|
|
+Calendar calendar = Calendar.getInstance();
|
|
|
+LocalDateTime localDateTime = LocalDateTime.ofInstant(calendar.toInstant(), ZoneId.systemDefault());
|
|
|
+<span class="copy-code-btn">复制代码</span>
|
|
|
+```
|
|
|
+
|
|
|
+Joda time 也可以与 java.util.Date 可以进行相互的转化:
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+DateTime dt = <span class="hljs-keyword">new</span> DateTime();
|
|
|
+Date jdkDate = dt.toDate();
|
|
|
+
|
|
|
+dt = <span class="hljs-keyword">new</span> DateTime(jdkDate);
|
|
|
+
|
|
|
+DateTime dt = <span class="hljs-keyword">new</span> DateTime();
|
|
|
+Calendar jdkCal = dt.toCalendar(Locale.CHINESE);
|
|
|
+
|
|
|
+dt = <span class="hljs-keyword">new</span> DateTime(jdkCal);
|
|
|
+
|
|
|
+DateTime dt = <span class="hljs-keyword">new</span> DateTime();
|
|
|
+GregorianCalendar jdkGCal = dt.toGregorianCalendar();
|
|
|
+
|
|
|
+dt = <span class="hljs-keyword">new</span> DateTime(jdkGCal);
|
|
|
+<span class="copy-code-btn">复制代码</span>
|
|
|
+```
|
|
|
+
|
|
|
+## 设计思想
|
|
|
+
|
|
|
+Joda time 与 Java time 在功能上已经相差不大,常用的功能这两个类库都可以完成,而且两个库都是线程安全的。
|
|
|
+
|
|
|
+但我认为 Joda time 的 API 更加简洁一些,Joda time 的使用可以直接从 DateTime 这个类开始。而 Java time 的使用则更加繁琐。
|
|
|
+
|
|
|
+从设计上来说 Java time 都不再使用 new 来创建实例,而是使用工厂方法来创建实例。这点上比 Joda time 的设计要更好,而且更加安全。
|
|
|
+
|
|
|
+既然 Joda time 都推荐迁移回 Java time 了,那么最终肯定是要迁移的。但是目前来说,我觉得 Joda time 用起来更加顺手一些,暂时还会继续使用这个。
|