文章目录
本文主要讲解Java 8的时间处理方式和Java8之前版本的时间处理方式的区别。笔者将Java8之前的jdk版本统称为旧版本。
一、日期处理
旧版本
Date
在Java 1.0中,对日期和时间的支持只能依赖java.util.Date类。它在易用性上许多问题,下面就谈谈这个类的缺点。
缺点一:易用性较差。
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类在任何方面支持时区。
缺点二:Date类是可变的,能把2014年3月18日修改成4月18日意味着什么呢?这种设计会将你拖入维护的噩梦。
Calendar
java.util.Calendar
类是为了替代Date类而出现的。很不幸的是,Calendar类中也有许多缺点,许多设计缺陷问题并未彻底解决。缺点如下:
- 月份依旧是从0开始计算(不过,至少Calendar 类拿掉了由1900年开始计算年份这一设计)。
- Calendar类也是可变的,使用起来不安全。
- 同时存在Date和Calendar这两个类,容易使程序员产生困惑。到底该使用哪一个类呢?此外,有的特性只在某一个类有提供,比如用 于以语言无关方式格式化和解析日期或时间的DateFormat方法就只在Date类里有。
Java 8
日期和时间:LocalDate和LocalTime
LocalDate
类的实例是一个不 可变对象,它只提供了简单的日期,并不含当天的时间信息。另外,它也不附带任何与时区相关的信息。
LocalTime
用来表示一天中的时间,比如13:45:20。
创建LocalDate和LocalTime的两种方式
//1.1 通过of重载的工厂方法创建 LocalDate ofDate = LocalDate.of(2014, 3, 18);//2014-03-18 LocalTime ofTime = LocalTime.of(13, 45, 20); // 13:45:20 //1.2 使用静态方法parse来创建 LocalDate parseDate = LocalDate.parse("2014-03-18");//2014-03-18 LocalTime parseTime = LocalTime.parse("13:45:20");// 13:45:20
读取LocalDate和LocalTime常用值的两种方式
//2. 读取LocalDate和LocalTime常用值的两种方式 //2.1 LocalDate 和 LocalTime 类提供了多种方法来 读取常用的值,比如年份、月份、星期几等 int hour = ofTime.getHour(); // 13 int minute = ofTime.getMinute(); // 45 int second = ofTime.getSecond(); // 20 System.out.println(ofTime); int year = ofDate.getYear(); // 2014 Month month = ofDate.getMonth(); // MARCH int day = ofDate.getDayOfMonth(); // 18 DayOfWeek dow = ofDate.getDayOfWeek(); // TUESDAY int len = ofDate.lengthOfMonth(); // 31 (days in March) boolean leap = ofDate.isLeapYear(); // false (判断是否为为闰年) System.out.println(ofDate); //2.1 通过传递一个TemporalField参数给get方法拿到同样的信息。 int y = ofDate.get(ChronoField.YEAR);//2014 int m = ofDate.get(ChronoField.MONTH_OF_YEAR);//3 int d = ofDate.get(ChronoField.DAY_OF_MONTH);//18 int dow2 = ofDate.get(ChronoField.DAY_OF_WEEK);//2 int hour2 = ofTime.get(ChronoField.HOUR_OF_DAY);//13 int minute2 = ofTime.get(ChronoField.MINUTE_OF_HOUR);//45 int second2 = ofTime.get(ChronoField.SECOND_OF_MINUTE);//20
合并日期与时间LocalDateTime
LocalDateTime,是LocalDate和LocalTime的合体。它同时表示了日期和时间,但不带有时区信息。
//3. 创建LocalDateTime的两种方式 //3.1 通过重载的of工厂方法创建 LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20); LocalDateTime dt2 = LocalDateTime.of(ofDate, ofTime); //3.2 通过合并日期和时间的方式创建 LocalDateTime dt3 = ofDate.atTime(13, 45, 20); LocalDateTime dt4 = ofDate.atTime(ofTime); LocalDateTime dt5 = ofTime.atDate(ofDate); //4. 从LocalDateTime中提取LocalDate或者LocalTime 组件 LocalDate date1 = dt1.toLocalDate();//2014-03-18 LocalTime time1 = dt1.toLocalTime();//13:45:20
二、机器时间处理
作为人,我们习惯于以星期几、几号、几点、几分这样的方式理解日期和时间。毫无疑问, 这种方式对于计算机而言并不容易理解。从计算机的角度来看,建模时间最自然的格式是表示一 个持续时间段上某个点的单一大整型数。
旧版本:Timestamp
Java 8:Instant
java.time.Instant
类对时间建模的方式,基本上它是以Unix年时间(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的 秒数进行计算。
//1. 通过向静态工厂方法ofEpochSecond传递一个代表秒数的值创建一个该类的实例。 Instant instant1 = Instant.ofEpochSecond(3); //2. ofEpochSecond的重载增强版本,它接收第二个以纳秒为单位的参数值,对传入作为秒数的参数进行调整。 // 2秒之后再加上 100万纳秒(1秒) Instant instant3 = Instant.ofEpochSecond(2, 1_000_000_000); //4秒之前的100万纳秒(1秒) Instant instant4 = Instant.ofEpochSecond(4, -1_000_000_000);
时间区间:Duration和Period
Duration类主要用于以秒和纳秒衡量时间的长短。
Period类以年、月或者日的方式对多个时间单位建模。
private static void userDurationAndPeriod(){
LocalTime time1 = LocalTime.of(13, 45, 20); // 13:45:20 LocalTime time2 = LocalTime.of(20, 45, 20); // 13:45:20 LocalDateTime dateTime1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20); // 2014-03-18T13:45 LocalDateTime dateTime2 = LocalDateTime.of(2015, Month.MARCH, 18, 13, 45, 20); // 2015-03-18T13:45 Instant instant1 = Instant.ofEpochSecond(3); Instant instant2 = Instant.ofEpochSecond(4); LocalDate localDate1 = LocalDate.of(2014, 3, 8); LocalDate localDate2 = LocalDate.of(2014, 3, 18); //Duration的创建方式 //1. 通过两个LocalTimes对象、两个LocalDateTimes对象、或者两个Instant对象创建duration Duration d1 = Duration.between(time1,time2); Duration d2 = Duration.between(dateTime1, dateTime2); Duration d3 = Duration.between(instant1, instant2); //2. 通过工厂类,直接创建对应的实例; Duration threeMinutes1 = Duration.ofMinutes(3); Duration threeMinutes2 = Duration.of(3, ChronoUnit.MINUTES); //Duration的创建方式 //1. 通过两个LocalDate对象创建duration Period tenDays = Period.between(localDate1,localDate2); //2. 通过工厂类,直接创建对应的实例; Period tenDays2 = Period.ofDays(10); Period threeWeeks = Period.ofWeeks(3); Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1); System.out.println(d1.getSeconds()); System.out.println(d2.getSeconds()); }
注意:由于Duration类主要用于以秒和纳 秒衡量时间的长短,你不能仅向between方法传递一个LocalDate对象做参数。同样,Period以年、月或者日的方式对多个时间单位建模,所以只能传递LocalDate对象作为参数。
三格式化日期
旧版本:DateFormat
Java 8:DateTimeFormatter
新的 java.time.format 包就是格式化以及解析日期、时间对象的。这个包中最重要的是DateTimeFormatter。
private static void useDateFormatter() {
LocalDate date = LocalDate.of(2014, 3, 18); //1. 从时间生成字符串 //1.1 使用特定不同的格式器生成字符串 String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);// String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);//2014-03-18 //1.2 DateTimeFormatter类还支持静态工厂方法,它可以按 照某个特定的模式创建格式器 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); DateTimeFormatter chinaFormatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL); String s3 = date.format(formatter);//18/03/2014 String s5 = date.format(chinaFormatter2);//2014年3月18日 星期二 //2. 从字符串生成时间 //2.1 通过解析代表日期或时间的字符串重新创建该日期对象。 LocalDate date1 = LocalDate.parse("", DateTimeFormatter.BASIC_ISO_DATE); LocalDate date2 = LocalDate.parse("2014-03-18", DateTimeFormatter.ISO_LOCAL_DATE); LocalDate date3 = LocalDate.parse("18/03/2014", formatter); LocalDate date5 = LocalDate.parse("2014年3月18日 星期二", chinaFormatter2); //3. 自定义DateTimeFormatter DateTimeFormatter complexFormatter = new DateTimeFormatterBuilder() .appendText(ChronoField.DAY_OF_MONTH) .appendLiteral(". ") .appendText(ChronoField.MONTH_OF_YEAR) .appendLiteral(" ") .appendText(ChronoField.YEAR) .parseCaseInsensitive() .toFormatter(Locale.ITALIAN); }
文档地址:DateTimeFormatter
四、处理时区
旧版本:TimeZone
java 8:ZoneId
之前你看到的Java8中的日期和时间的种类都不包含时区信息。时区的处理是新版日期和时间API新增 加的重要功能,使用新版日期和时间API时区的处理被极大地简化了。跟其他日期和时间类一 样,ZoneId类也是无法修改的。
时区是按照一定的规则将区域划分成的标准时间相同的区间。每个特定 的ZoneId对象都由一个地区ID标识,比如:
ZoneId romeZone = ZoneId.of("Europe/Rome");
地区ID都为“{区域}/{城市}”的格式,这些地区集合的设定都由英特网编号分配机构(IANA) 的时区数据库提供。java 8中支持的所有地区集合可以通过以下语句打印出来:
Set<String> zoneIds= ZoneId.getAvailableZoneIds(); for (String zone : zoneIds) {
//共计599个 System.out.println(zone); }
ZoneDateTime
一旦得到一个ZoneId对象,你就可以将它与LocalDate、LocalDateTime或者是Instant对象整合起来,构造为一个ZonedDateTime实例,它代表了相对于指定时区的时间点。代码如下所示:
private static void useZoneId(){
ZoneId romeZone = ZoneId.of("Europe/Rome"); LocalDate date = LocalDate.of(2014, Month.MARCH, 18); ZonedDateTime zdt1 = date.atStartOfDay(romeZone); LocalDateTime dateTime = LocalDateTime.now(); ZonedDateTime zdt2 = dateTime.atZone(romeZone); Instant instant = Instant.now(); ZonedDateTime zdt3 = instant.atZone(romeZone); System.out.println(zdt1); System.out.println(zdt2); System.out.println(zdt3); }
下图对ZonedDateTime的组成部分进行了说明,相信能够帮助你理解LocaleDate、 LocalTime、LocalDateTime以及ZoneId之间的差异。
通过ZoneId,你还可以将LocalDateTime转换为Instant:
LocalDateTime dateTime2 = LocalDateTime.of(2018,7,21,18,46,0); ZoneId romeZone2 = ZoneId.systemDefault(); Instant instantFromDateTime = dateTime2.atZone(romeZone2).toInstant(); System.out.println(instantFromDateTime.getEpochSecond());
今天的文章
Java中的时间和日期处理分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/88652.html