首页 > 编程语言 >Dating Java8系列之新的日期和时间

Dating Java8系列之新的日期和时间

时间:2024-01-13 00:00:00浏览次数:30  
标签:System 日期 之新 LocalDateTime println Dating LocalDate Java8 out

给我馍馍/文

 

图片

 

图片

旧的日期时间

 

在Java 1.0中,对日期和时间的支持只能依赖java.util.Date类。正如类名所表达的,这个类无法表示日期,只能以毫秒的精度表示时间。更糟糕的是它的易用性,由于某些原因和未知的设计决策,这个类的易用性被深深地损害了,比如:年份的起始选择是1900年,月份的起始从0开始。这意味着,如果你想要用Date表示Java 8的发布日期,即2014年3月18日,需要创建下面这样的Date实例:

Date date = new Date(114, 2, 18);

它的打印输出效果为:

Tue Mar 18 00:00:00 CET 2014

看起来不那么直观,不是吗?此外,甚至Date类的toString方法返回的字符串也容易误导人。以我们的例子而言,它的返回值中甚至还包含了JVM的默认时区CET,即中欧时间(Central Europe Time)。但这并不表示Date类是一个支持时区的日期时间API。

随着Java 1.0退出历史舞台,Date类的种种问题和限制几乎一扫而光,但很明显,这些历史旧账如果不牺牲前向兼容性是无法解决的。所以,在Java1.1中,Date类中的很多方法被废弃了,取而代之的是java.util.Calendar类。很不幸,Calendar类也有类似的问题和设计缺陷,导致使用这些方法写出的代码非常容易出错。比如,月份依旧是从0开始计算(不过,至少Calendar类拿掉了由1900年开始计算年份这一设计)。更糟的是,同时存在Date和Calendar这两个类,也增加了程序员的在选择上的困难。到底该使用哪一个类呢?此外,有的特性只在某一个类有提供,比如用于以语言无关方式格式化和解析日期或时间的DateFormat方法就只在Date类里有。

DateFormat方法也有它自己的问题。比如,它不是线程安全的。这意味着两个线程如果使用同一个formatter解析日期,你可能会得到无法预期的结果。

 

图片

新的日期和时间

 

LocalDate

LocalDate类的实例是一个不可变对象,它只提供了简单的日期,并不含当天的时间信息。另外,它也不携带任何与时区相关的信息。

LocalDate localDate1 = LocalDate.now();System.out.println(localDate1);LocalDate localDate2 = LocalDate.of(2019, 06, 06);LocalDate localDate3 = LocalDate.of(2019, Month.JUNE, 06);System.out.println(localDate2 + " " + localDate3);System.out.println(localDate2.getDayOfYear());System.out.println(localDate2.getDayOfWeek());System.out.println(localDate2.getDayOfMonth());System.out.println(localDate2.getMonth());System.out.println(localDate2.get(ChronoField.DAY_OF_WEEK));System.out.println(localDate2.get(ChronoField.DAY_OF_YEAR));

LocalTime

类似地,一天中的时间,比如13:45:20,可以使用LocalTime类表示。你可以使用of重载的两个工厂方法创建LocalTime的实例。第一个重载函数接收小时和分 ,第二个重载函数同时还接收秒。同LocalDate一样,LocalTime类也提供了一些getter方法访问这些变量的值。

LocalTime localTime1 = LocalTime.now();System.out.println(localTime1);LocalTime localTime2 = LocalTime.of(12, 02);LocalTime localTime3 = LocalTime.of(12, 02, 03);LocalTime localTime4 = LocalTime.of(12, 02, 03, 900_000_000);System.out.println(localTime2 + " " + localTime3 + " " + localTime4);System.out.println(localTime4.getHour());System.out.println(localTime4.getMinute());System.out.println(localTime4.getSecond());

LocalDateTime

这个复合类名叫LocalDateTime,是LocalDate和LocalTime的合体。它同时表示了日期和时间,但不带有时区信息,你可以直接创建,也可以通过合并日期和时间对象进行构造。

LocalDateTime localDateTime1 = LocalDateTime.now();System.out.println(localDateTime1);LocalDateTime localDateTime2 = LocalDateTime.of(2000, Month.AUGUST, 22, 04, 05, 06);System.out.println(localDateTime2);System.out.println(LocalDateTime.of(localDate1, localTime1));System.out.println(localDate2.atTime(localTime2));System.out.println(localTime3.atDate(localDate3));System.out.println(localTime3.get(ChronoField.HOUR_OF_DAY));System.out.println(localTime3.get(ChronoField.MINUTE_OF_HOUR));

Instant

作为人,我们习惯于以星期几、几号、几点、几分这样的方式理解日期和时间。毫无疑问,这种方式对于计算机而言并不容易理解。从计算机的角度来看,建模时间最自然的格式是表示一个持续时间段上某个点的单一大整型数。这也是新的java.time.Instant类对时间建模的方式,基本上它是以Unix元年时间(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的秒数进行计算。

// InstantInstant instant = Instant.now();System.out.println(instant);// 时间戳,秒,毫秒System.out.println(instant.toEpochMilli());System.out.println(instant.getEpochSecond());// Date、Calendar、SystemDate date = new Date();System.out.println(date.getTime());System.out.println(Calendar.getInstance().getTimeInMillis());System.out.println(System.currentTimeMillis());System.out.println(instant.toEpochMilli() + " " + instant.getNano());

Duration和Period

Duration类的静态工厂方法between就是为获取两个时间的间隔。可以创建两个LocalTime对象、两个LocalDateTime。

由于LocalDateTime和Instant是为不同的目的而设计的,一个是为了便于人阅读使用, 另一个是为了便于机器处理,所以你不能将二者混用。如果试图在这两类对象之间创建duration,会触发一个DateTimeException异常。此外,由于Duration类主要用于以秒和纳秒衡量时间的长短,你不能仅向between方法传递一个LocalDate对象做参数。

如果你需要以年、月或者日的方式对多个时间单位建模,可以使用Period类。使用该类的工厂方法between,你可以使用得到两个LocalDate之间的时长。

图片

// Duration 用在LocalTime、LocalDateTimeDuration duration1 = Duration.between(localTime1, localTime2);System.out.println(duration1);System.out.println(Duration.between(localDateTime1, localDateTime2));System.out.println(Duration.ofDays(2));System.out.println(Duration.ofHours(5));System.out.println(Duration.ofMinutes(100));System.out.println(Duration.of(2, ChronoUnit.MINUTES));System.out.println(Duration.of(2, ChronoUnit.SECONDS));
// Period用在LocalDate上面Period period = Period.between(localDate1, localDate2);System.out.println(period);System.out.println(Period.ofYears(2));

 

图片

操纵日期和时间

 

简单的操纵对象

如果你已经有一个LocalDate对象,想要创建它的一个修改版,最直接也最简单的方法是使用withAttribute方法。withAttribute方法会创建对象的一个副本,并按照需要修改它的属性。注意,下面的这段代码中所有的方法都返回一个修改了属性的对象。它们都不会修改原来的对象!

LocalDate date1 = LocalDate.of(2014, 3, 18);LocalDate date2 = date1.withYear(2011);LocalDate date3 = date2.withDayOfMonth(25);LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 9);

像LocalDate、LocalTime、LocalDateTime以及Instant这样表示时间点的日期和时间类提供了大量通用的方法

图片

使用TemporalAdjuster

截至目前,你所看到的所有日期操作都是相对比较直接的。有的时候,你需要进行一些更加复杂的操作,比如,将日期调整到下个周日、下个工作日,或者是本月的最后一天。这时,你可以使用重载版本的with方法,向其传递一个提供了更多定制化选择的TemporalAdjuster对象,更加灵活地处理日期。对于最常见的用例,日期和时间API已经提供了大量预定义的TemporalAdjuster。你可以通过TemporalAdjuster类的静态工厂方法访问它们。

import static java.time.temporal.TemporalAdjusters.*;LocalDate date1 = LocalDate.of(2014, 3, 18);LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY));LocalDate date3 = date2.with(lastDayOfMonth());

使用TemporalAdjuster我们可以进行更加复杂的日期操作,而且这些方法的名称也非常直观,方法名基本就是问题陈述。此外,即使你没有找到符合你要求的预定义TemporalAdjuster,创建你自己的TemporalAdjuster也并非难事。

实际上,TemporalAdjuster接口只声明了单一的一个方法这使得它成为了一个函数式接口),定义如下。

图片

// 使用TemporalAdjuster中预定义的方法快速操纵和修改日期System.out.println(localDate.with(TemporalAdjusters.lastDayOfYear()));System.out.println(localDate.with(TemporalAdjusters.firstDayOfNextMonth()));System.out.println(localDate.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)));System.out.println(localDate.with(TemporalAdjusters.firstInMonth(DayOfWeek.TUESDAY)));// 函数式接口LocalDate firstMonthOfYear = localDate.with((temporal) -> localDate.withMonth(1));LocalDate lastMonthOfYear = localDate.with((temporal) -> localDate.withMonth(12));System.out.println(firstMonthOfYear + " " + lastMonthOfYear);

 

图片

解析日期对象

 

解析日期时间

处理日期和时间对象时,格式化以及解析日期和时间对象是另一个非常重要的功能。新的java.time.format包就是特别为这个目的而设计的。这个包中,最重要的类是DateTimeFormatter。创建格式器最简单的方法是通过它的静态工厂方法以及常量。像BASIC_ISO_DATE和ISO_LOCAL_DATE这样的常量是DateTimeFormatter类的预定义实例。所有的DateTimeFormatter实例都能用于以一定的格式创建代表特定日期或时间的字符串。比如,下面的这个例子中,我们使用了两个不同的格式器生成了字符串:

LocalDate ld4 = LocalDate.of(2019, 06, 06);System.out.println(ld1.format(DateTimeFormatter.BASIC_ISO_DATE));LocalTime lt3 = LocalTime.now();System.out.println(lt3.format(DateTimeFormatter.ISO_LOCAL_TIME));System.out.println(LocalDate.parse("20190606", DateTimeFormatter.BASIC_ISO_DATE));System.out.println(LocalTime.parse(“12:02:03”, DateTimeFormatter.ISO_LOCAL_TIME));

和老的java.util.DateFormat相比较,所有的DateTimeFormatter实例都是线程安全的。所以,你能够以单例模式创建格式器实例,就像DateTimeFormatter所定义的那些常量,并能在多个线程间共享这些实例。DateTimeFormatter类还支持一个静态工厂方法,它可以按照某个特定的模式创建格式器,代码清单如下。

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");String ld4Str = ld4.format(dateTimeFormatter);System.out.println(ld4Str);System.out.println(LocalDate.parse(ld4Str, dateTimeFormatter));

 时区

时区是按照一定的规则将区域划分成的标准时间相同的区间。在ZoneRules这个类中包含了40个这样的实例。你可以简单地通过调用ZoneId的getRules()得到指定时区的规则。每个特定的ZoneId对象都由一个地区ID标识,比如:

ZoneId zoneId = ZoneId.of("Asia/Tokyo");

地区ID都为“{区域}/{城市}”的格式,这些地区集合的设定都由因特网编号分配机构(IANA)的时区数据库提供。你可以通过Java8的新方法toZoneId将一个老的时区对象转换为ZoneId:

ZoneId zoneId = TimeZone.getDefault().toZoneId();

一旦得到一个ZoneId对象你就可以将它与LocalDate、LocalDateTime或者是Instant对象整合起来,构造为一个ZonedDateTime实例,它代表了相对于指定时区的时间点。

 

图片

// 不同时区ZoneId zoneId = ZoneId.of("Asia/Tokyo");ZoneId defaultZoneId = TimeZone.getDefault().toZoneId();LocalDateTime localDateTime2 = LocalDateTime.now();// 标记这个是哪个时区的时间System.out.println(localDateTime2);System.out.println(" !! " + localDateTime2.atZone(zoneId));// 把时间戳转成指定时区的时间System.out.println(Instant.now().atZone(zoneId));

 通过ZoneId,你还可以将LocalDateTime转换为Instant:

LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45); Instant instantFromDateTime = dateTime.toInstant(romeZone);

你也可以通过反向的方式得到LocalDateTime对象: 

Instant instant = Instant.now();LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone);

另一种比较通用的表达时区的方式是利用当前时区和UTC/格林尼治时间的固定偏差计算时区。比如,基于这个理论,你可以说“北京早于伦敦8小时”。这种情况下,你可以使用ZoneOffset类,它是ZoneId的一个子类,表示的是当前时间和伦敦格林尼治子午线时间的差异:

ZoneOffset beijing = ZoneOffset.of("+08:00");OffsetDateTime offsetDateTime = OffsetDateTime.of(LocalDateTime.now(), beijing);System.out.println(offsetDateTime);

 

图片

总结

 

  • 新版的日期和时间API中,日期-时间对象是不可变的。

  • 新的API提供了两种不同的时间表示方式,有效地区分了运行时人和机器的不同需求。

  • 可以用绝对或者相对的方式操作日期和时间,操作的结果总是返回一个新的实例,老的日期时间对象不会发生变化。

  • TemporalAdjuster能够用更精细的方式操纵日期,不再局限于一次只能改变它的一个值,并且你还可按照需求定义自己的日期转换器。

  • 现在可以按照特定的格式需求,定义自己的格式器,打印输出或者解析日期时间对象。这些格式器可以通过模板创建,也可以自己编程创建,并且它们都是线程安全的。

  • 可以用相对于某个地区/位置的方式,或者以与UTC/格林尼治时间的绝对偏差的方式表示时区,并将其应用到日期-时间对象上,对其进行本地化。

 

作者:给我馍馍
博客:https://www.cnblogs.com/givemomo/

 

本篇文章如有帮助到您,请给「给我馍馍」点个赞,感谢您的支持。

标签:System,日期,之新,LocalDateTime,println,Dating,LocalDate,Java8,out
From: https://www.cnblogs.com/givemomo/p/17961788

相关文章

  • Dating Java8系列之用流收集数据
    给我馍馍/文  收集器简介 1.收集器介绍Java8中流支持两种类型的操作:中间操作(如filter或map)和终端操作(如count、findFirst、forEach和reduce)。中间操作可以链接起来,将一个流转换为另一个流。这些操作不会消耗流,其目的是建立一个流水线。与此相反,终端操作会消耗流......
  • Dating Java8系列之Lambda表达式和函数式接口(下)
    给我馍馍/文  使用函数式接口  函数式接口定义且只定义了一个抽象方法。函数式接口很有用,因为抽象方法的签名可以描述Lambda表达式的签名。为了应用不同的Lambda表达式,你需要一套能够描述常见函数描述符的函数式接口。Java8的库设计师帮我们在java.util.......
  • Dating Java8系列之Lambda表达式和函数式接口(上)
    给我馍馍/文  Lambda简介 我们可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式。它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。匿名:我们说匿名,是因为它不像普通的方法那样有一个明确的名称。函数:我们说它是函数,......
  • Dating Java8系列之通过行为参数化传递代码
    给我馍馍/文 引言 作为技术开发人员,我们无法保证我们写出来的代码的可用时间和保质期,也许今天刚上线的一个功能,等到明天就因为需求变动不得不进行更改。 为什么会不断的变化作为技术人员,我们当然希望需求能循序渐进地平稳过度,但往往事与愿违,对于其原因我简单总结为以......
  • Dating Java8系列之default默认方法
    给我馍馍/文  引言 传统上,Java程序的接口是将相关方法按照约定组合到一起。实现接口的类必须为接口中定义的每个方法提供一个实现,或者从父类中继承它的实现。 不断迭代的API默认方法的引入就是为了,以兼容的方式,解决像JavaAPI这样的类库,演进迭代问题。理解演进迭......
  • Dating Java8系列之Java8中的流操作
    给我馍馍/文  本次我们会使用到很多的流操作,如筛选、切片、映射、查找、匹配和归约,这些操作可以让我们能快速完成复杂的数据查询。 筛选和切片 用谓词筛选Streams接口支持filter方法。该操作会接受一个谓词(一个返回boolean的函数)作为参数,并返回一个包括所有符合......
  • Dating Java8系列之Java8中的‘流’
    给我馍馍/文 流的概念 1.流是什么流是JavaAPI的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,我们可以把它们看成遍历数据集的高级迭代器。 代码:按价格排序后得到手机名称列表 使用新的流式方法有几个显而易见......
  • Dating Java8系列之巧用Optional之优雅规避NPE问题
    给我馍馍/文  避之不及的NullPointerException NPE:NullPointerException空指针异常是最常见的Java异常之一,抛出NPE错误不是用户操作的错误,而是开发人员的错误,应该被避免,那么只能在每个方法中加入非空检查,阅读性和维护性都比较差。以下是一个常见的嵌套对象:一个用......
  • Dating Java8系列之并行数据处理
    翎野君/文  分支合并框架 分支合并框架介绍分支/合并框架的目的是以递归的方式将可以并行的任务拆分成更小的任务,然后将每个子任务的结果合并起来生成整体结果。它是ExecutorService接口的一个实现,它把子任务分配给线程池(称为ForkJoinPool)中的工作线程。把任务提交......
  • java8日期时间格式化DateTimeFormatter多个格式
    原文地址:datetimeformatter.ofpatternmultipleformats-掘金DateTimeFormatter 是一个用于日期时间格式化和解析的类。使用 ofPattern 方法可以创建一个格式化器,该方法接受一个日期时间格式的字符串作为参数。如果您需要在同一个 DateTimeFormatter 对象中支持多种不同的......