首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java中的时间和日期处理

Java中的时间和日期处理

作者头像
栋先生
发布2018-09-29 17:00:05
2.7K0
发布2018-09-29 17:00:05
举报
文章被收录于专栏:Java成长之路Java成长之路

本文主要讲解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类中也有许多缺点,许多设计缺陷问题并未彻底解决。缺点如下:

  1. 月份依旧是从0开始计算(不过,至少Calendar 类拿掉了由1900年开始计算年份这一设计)。
  2. Calendar类也是可变的,使用起来不安全。
  3. 同时存在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);//20140318

        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("20140318", 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());
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年07月22日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、日期处理
    • 旧版本
      • Date
      • Calendar
    • Java 8
      • 日期和时间:LocalDate和LocalTime
      • 合并日期与时间LocalDateTime
  • 二、机器时间处理
    • 旧版本:Timestamp
      • Java 8:Instant
      • 时间区间:Duration和Period
      • 三格式化日期
        • 旧版本:DateFormat
          • Java 8:DateTimeFormatter
          • 四、处理时区
            • 旧版本:TimeZone
              • java 8:ZoneId
                • ZoneDateTime
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档