Java8中的时间和日期(下)

在上篇文章Java中的时间和日期(上)里面,简单介绍了Java中的Date类,Calendar类以及用于格式化的SimpleDateFormater类。使用这些的时候我们会明显地感受到其中的不便之处,比如,Calendar类的月份是从0开始计数的;日期格式输出不够友好,很多情况下都需要使用SimpleDateFormater类来格式化;一些简单得日期计算也比较麻烦等等。所以就有了joda-time这种第三方库来简化java对于时间和日期的操作。为了改变这种情况,java 8中对时间和日期对处理就吸收了joda-time库的特性。那么新的时间日期处理会带来怎样的便捷呢?这便是本篇文章所要聊的内容。

  • Instant——它代表的是时间戳
  • LocalDate——不包含具体时间的日期,比如2014-01-14。它可以用来存储生日,周年纪念日,入职日期等。
  • LocalTime——它代表的是不含日期的时间
  • LocalDateTime——它包含了日期及时间,不过还是没有偏移信息或者说时区。
  • ZonedDateTime——这是一个包含时区的完整的日期时间,偏移量是以UTC/格林威治时间为基准的。

1. 月份的枚举类 Enum Month

java.time.Month

在以前使用Java的时候,你一定痛恨了月份的表示和计算,最主要的原因就是因为一月份是从0开始计数的。

在Java 8中为了改变这一现状,增加了一个Month枚举类来表示月份。使用这个枚举类甚至还可以直接进行月份的加减运算

  • of(int month) 
    这是一个静态方法,用于创建一个Month对象。传入的参数当然是从1开始计数啦,1表示一月,12表示十二月。当传入的参数小于1或者大于12时,就会抛出异常。

  • getValue() 
    返回该Month对象当前的值。一月份返回1,二月份返回2,依次类推。

  • minus(long months) 
    这个是用来做月份的减法计算的。传入的参数表示你想在该Month对象的基础上减去几个月。如果是1月份减去2个月,返回的当然是11月份。

  • plus(long months) 
    用来计算月份的加法。传入的参数表示你想在该Month对象的基础上增加几个月。比如12月加2个月就变成了二月份。

  • maxLength(), minLength()和length(boolean leapYear) 
    用来获取Month对象表示的该月的日期数。其中,length(boolean leapYear)中的参数表示是否为闰年。其实这三个方法返回的结果在很多情况下都是一样的,返回的都是当月的日期数,30或者31。只有二月份除外,当Month对象表示二月份时,maxLength()length(true)返回29,minLength()length(false)返回28。

下面用代码来说明上述方法的使用:

public static void main(String[] args) {
    System.out.println(Month.DECEMBER);         // DECEMBER
    System.out.println(Month.of(2));            // FEBRUARY

    Month month = Month.FEBRUARY;        
    System.out.println(month.getValue());       // 2
    System.out.println(month.minus(3));         // NOVEMBER
    System.out.println(month.plus(2));          // APRIL

    System.out.println(month.length(false));    // 28
    System.out.println(month.length(true));     // 29
    System.out.println(month.maxLength());	//29
    System.out.println(month.minLength());	//28
}

有时候希望返回月份是中文,而不是英文。毕竟程序员大多都比较懒,能少转化一次自然是很好的。又或者你需要显示的是月份的英文缩写?Java 8都为你想到了。

只要调用 month.getDisplayName(TextStyle, Locale)方法就行:

第一个参数是文本类型,也就是说你想显示完整的名称还是缩写;

第二个参数表示地区,如果没有特殊要求,传入Locale.getDefault()就行。就像下面的代码演示的那样:

2. 星期的枚举类Enum DayOfWeek

java.time.DayOfWeek

DayOfWeek枚举类用来表示一个周的七天。常用的方法和 Month枚举类的几乎一致,包括

of(int dayOfWeek)静态方法用于创建DayOfWeek对象;

getValue()方法用来获取该对象的值;

plus(long days)minus(long days)方法用来进行加减法计算。

getDisplayName(TextStyle style, Locale locale)来格式化输出。代码演示如下:

public static void main(String[] args) {
    System.out.println(DayOfWeek.FRIDAY);           // FRIDAY
    System.out.println(DayOfWeek.of(7));            // SUNDAY

    DayOfWeek dayOfWeek = DayOfWeek.TUESDAY;
    System.out.println(dayOfWeek.getValue());       // 2
    System.out.println(dayOfWeek.plus(3));          // FRIDAY
    System.out.println(dayOfWeek.minus(2));         // SUNDAY

    Locale defaultLocal = Locale.getDefault();
    System.out.println(dayOfWeek.getDisplayName(TextStyle.FULL, defaultLocal));     // 星期二
    System.out.println(dayOfWeek.getDisplayName(TextStyle.SHORT, defaultLocal));    // 星期二
    System.out.println(dayOfWeek.getDisplayName(TextStyle.NARROW, defaultLocal));   // 二

    Locale locale = Locale.ENGLISH;
    System.out.println(dayOfWeek.getDisplayName(TextStyle.FULL, locale));           // Tuesday
    System.out.println(dayOfWeek.getDisplayName(TextStyle.SHORT, locale));          // Tue
    System.out.println(dayOfWeek.getDisplayName(TextStyle.NARROW, locale));         // T
}

但是,在DayOfWeek枚举类中,是没有maxLength(), minLength()和length(boolean leapYear)这三个方法的,相信你们也知道是为什么。

最后说一句,由于MonthDayOfWeek只是枚举类,它们并不持有当前时间信息,所以就别妄想使用这两个枚举类来解决”今天是星期几”,”明天是几号”等问题了。

源码中的加减法计算

刚开始学Java的时候,计算月份/星期几乎是必备作业,不过当时用的是Date/Calendar类来计算,相当麻烦,

Java 8使用枚举来表示月份和星期之后,进行相应的加减法计算就变的相对简单了。

由于月份的计算和星期的计算原理是一样的,我们就只看Month的加减法计算。

private static final Month[] ENUMS = Month.values();

public Month plus(long months) {
    int amount = (int) (months % 12);
    return ENUMS[(ordinal() + (amount + 12)) % 12];
}

 public Month minus(long months) {
    return plus(-(months % 12));
}

这里的处理方法很巧妙,减法直接调用加法的处理逻辑,当年我就没想到过,哈哈,值得学习。

3. LocalDate和LocalTime类

java.time.LocalDate
java.time.LocalTime

重头戏来了,现在开始隆重介绍Java 8的常用的时间日期类:LocalDateLocalTime

使用 LocalDate可以获取当前日期(注意只是日期,不包含时间),并可以进行相应处理。

使用 LocalTime可以获取当前时间(注意只是时间,不包含日期)并进行相应处理。这样就更好的符合“单一职责原则”。

构造方法

根据不同的需求,提供了不同的创建方式,主要包括两个静态方法now()of()方法。其实,在后面我们会看到,在Java 8中,创建时间和日期几乎都会用的这两个方法。

public static void main(String[] args) {
    LocalDate date1 = LocalDate.now();
    LocalDate date2 = LocalDate.of(1998, 2, 4);
    LocalDate date3 = LocalDate.ofEpochDay(180);

    System.out.println(date1);      // 2016-07-11
    System.out.println(date2);      // 1998-02-04
    System.out.println(date3);      // 1970-06-30

    LocalTime time1 = LocalTime.now();
    LocalTime time2 = LocalTime.now().withNano(0);
    LocalTime time3 = LocalTime.of(12, 30);
    LocalTime time4 = LocalTime.ofSecondOfDay(60 * 60 * 2);

    System.out.println(time1);      // 10:56:04.772
    System.out.println(time2);      // 10:56:04
    System.out.println(time3);      // 12:30
    System.out.println(time4);      // 02:00
}

withNano()方法会在后面提及,主要是修改当前对象表示的纳秒数的值。在上面的代码中,有几点需要注意的地方:

  • ofEpochDay(long epochDay)方法中的参数,指的是距1970年1月1日那天的时间间隔。
  • 在Java 8中,时间和日期的格式是按照ISO-8061的时间和日期标准来显示的。年份为4位数,月日时分秒都是2位数,不足两位用0补齐。

常用方法

LocalDate

还记得之前说过的,DayOfWeek枚举类不持有当前时间信息,所以你无法单独使用它来得到今天是星期几这种信息。然而如果获取到了当前日期的LocalDate对象后,问题就迎刃而解了。

LocalDate提供了大量的方法来进行日期信息的获取和计算。有了这一个LocalDate对象,你不仅可以知道这个对象是哪年几月几日星期几,还能够对于年月日进行加减法计算,甚至你可以以周为单位进行日期的加减法计算,比如,你可以轻松解决两个周前的今天是几月几日这类问题。

下面,是常用的方法,请自行查阅官方API文档

方法名 返回值类型 对该方法的解释
getYear() int 获取当前日期的年份
getMonth() Month 获取当前日期的月份对象
getMonthValue() int 获取当前日期是第几月
getDayOfWeek() DayOfWeek 表示该对象表示的日期是星期几
getDayOfMonth() int 表示该对象表示的日期是这个月第几天
getDayOfYear() int 表示该对象表示的日期是今年第几天
withYear(int year) LocalDate 修改当前对象的年份
withMonth(int month) LocalDate 修改当前对象的月份
withDayOfMonth(int dayOfMonth) LocalDate 修改当前对象在当月的日期
isLeapYear() boolean 是否是闰年
lengthOfMonth() int 这个月有多少天
lengthOfYear() int 该对象表示的年份有多少天(365或者366)
plusYears(long yearsToAdd) LocalDate 当前对象增加指定的年份数
plusMonths(long monthsToAdd) LocalDate 当前对象增加指定的月份数
plusWeeks(long weeksToAdd) LocalDate 当前对象增加指定的周数
plusDays(long daysToAdd) LocalDate 当前对象增加指定的天数
minusYears(long yearsToSubtract) LocalDate 当前对象减去指定的年数
minusMonths(long monthsToSubtract) LocalDate 当前对象减去注定的月数
minusWeeks(long weeksToSubtract) LocalDate 当前对象减去指定的周数
minusDays(long daysToSubtract) LocalDate 当前对象减去指定的天数
compareTo(ChronoLocalDate other) int 比较当前对象和other对象在时间上的大小,返回值如果为正,则当前对象时间较晚,
isBefore(ChronoLocalDate other) boolean 比较当前对象日期是否在other对象日期之前
isAfter(ChronoLocalDate other) boolean 比较当前对象日期是否在other对象日期之后
isEqual(ChronoLocalDate other) boolean 比较两个日期对象是否相等

列出这么多方法,不是要你死记硬背记住它们,而是要在脑海有个印象,知道有哪些常用方法,可以做什么。概括起来,LocalDate类中常用的方法有四种:获取日期信息,修改日期信息,加减法运算和日期对象间的比较。记住了这些,以后在工作中就可以查阅使用,而不用自己在造一遍轮子。

有几点需要注意的地方:

  • 上面列表里面有一个ChronoLocalDate,它是一个接口,LocalDate类实现了这个接口,所以直接传一个LocalDate类对象即可。
  • isEqual(ChronoLocalDate other)这个方法,如果两个对象是同一个对象,或者这两个对象的值相等(同年同月同日),则返回true,否则返回false。
  • 当一个方法返回的是LocalDate对象时,便可以使用链式调用。举个例子,获取昨天的日期,我们可以直接这样写:LocalDate.now().minusDays(1)

下面用代码演示几个方法:

public static void main(String[] args) {
    LocalDate now = LocalDate.now();
    System.out.println(now.getYear());                  // 2016
    System.out.println(now.getDayOfWeek());             // MONDAY
    System.out.println(now.getDayOfMonth());            // 11
    System.out.println(now.withMonth(3));               // 2016-03-11
    System.out.println(now.minusWeeks(2));              // 2016-06-27
    System.out.println(now.plusDays(10));               // 2016-07-21

    LocalDate firstDayOfYear = LocalDate.of(2016,1,1);
    System.out.println(now.compareTo(firstDayOfYear));  // 6
    System.out.println(now.isAfter(firstDayOfYear));    // true
    System.out.println(now.isEqual(firstDayOfYear));    // false
}

LocalTime

LocalDate类中的方法和LocalDate中的类似,同样可以分为:获取时间信息,修改时间信息,加减法运算和时间对象间的比较。方法的具体描述我就不写了。根据LocalDate类中列举的常用方法,你也能猜得出在LocalTime类中有哪些对应的常用方法。下面还是用代码演示几个方法:

public static void main(String[] args) {
    LocalTime now = LocalTime.now();
    System.out.println(now.getHour());              // 14
    System.out.println(now.getMinute());            // 15
    System.out.println(now.getSecond());            // 22
    System.out.println(now.getNano());              // 881000000
    System.out.println(now.withNano(0));            // 14:15:22
    System.out.println(now.minusHours(3));          // 11:15:22.881
    System.out.println(now.minusMinutes(15));       // 14:00:22.881
    System.out.println(now.minusSeconds(20));       // 14:15:02.881

    LocalTime halfOfDay = LocalTime.of(12 ,0);
    System.out.println(now.compareTo(halfOfDay));   // 1
    System.out.println(now.isAfter(halfOfDay));     // true
}

不过有几点需要说明:

  • LocalTime中没有isEqual()方法。
  • getNano()中,nano指的是纳秒(毫微秒),1秒等于1亿纳秒。

 

4. LocalDateTime日期时间类

java.time.LocalDateTime

或许有人觉得,将日期和时间分开处理有些不方便。我想将时间和日期一起处理怎么办?当然可以,Java 8中还提供了LocalDateTime来满足你的这个需求。

构造方法

和前面的类似,可以使用静态方法now()和静态方法of()来创建一个LocalDateTime对象。比如:

public static void main(String[] args) {
    LocalDateTime now = LocalDateTime.now();
    System.out.println(now);        // 2016-07-11T14:27:20.169
    LocalDateTime dateTime1 = LocalDateTime.of(1990, 1, 1, 12, 3);
    LocalDateTime dateTime2 = LocalDateTime.of(2000, 2, 4, 8, 4, 20);
    System.out.println(dateTime1);  // 1990-01-01T12:03
    System.out.println(dateTime2);  // 2000-02-04T08:04:20
}

通常,你需要在of()方法中传入6个参数,表示年月日时分秒。关于月份,既可以传入Month对象,也可以传入int值(当然1表示一月份)。也可以将秒这个参数省略了,传入5个参数。也可以增加一个纳秒参数,变为7个参数。

常用方法

这个不想再说了,和LocalDateLocalTime类似。

5. LocalDateTime和LocalDate、LocalTime之间的转化

LocalDateTime既然是“集LocalDate和LocalTime的大成者”,自然能将LocalDateTime转化位LocalDate或者LocalTime,而且方法很简单,只需要调用toLocalDate()或者toLocalTime()方法,就像下面演示的那样:

public static void main(String[] args) {
    LocalDateTime dateTime = LocalDateTime.now();
    System.out.println(dateTime);   // 2016-07-11T16:57:41.217

    LocalDate date = dateTime.toLocalDate();
    LocalTime time = dateTime.toLocalTime();
    System.out.println(date);       // 2016-07-11
    System.out.println(time);       // 16:57:41.217
}

6. 解析和格式化

在Date和Calendar统治的时代,想要格式化一个日期,只能用Date来格式化,并且SimpleDateFormat还有线程安全隐患,无疑很麻烦。而现在,在Java 8中,这些问题都不复存在了。

有一点需要再次强调,再Java 8中,时间日期的格式是按照ISO-8061的时间和日期标准来显示的。年份为4位数,月日时分秒都是2位数,不足两位用0补齐,日期之间需要用短横线连接,时间之间要用:连接必须按照此规则来进行解析,比如:

public static void main(String[] args) {
    LocalDate date = LocalDate.parse("2016-09-08");
    LocalTime time = LocalTime.parse("12:24:43");
    System.out.println(date);   // 2016-09-08
    System.out.println(time);   // 12:24:43
}

当然,Java是宽容的,如果你不按照ISO-8061的格式传入,也有解决办法,

可以使用parse(CharSequence text, DateTimeFormatter formatter)这个方法,

第一个参数传入你要解析的文本。第二个参数传入你所想要的格式类型。

解析一个日期字符串  String--->LocalDateTime 

public static void main(String[] args) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
    LocalDate date = LocalDate.parse("20160708", formatter);
    System.out.println(date);        // 2016-07-08

    DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");  
    LocalDateTime datetime = LocalDateTime.parse("2013/12/31 23:59:59", formatter2);  
    System.out.println(datetime); 		// LocalDateTime 2013-12-31T23:59:59
    System.out.println(formatter2.format(datetime)); //String  2013/12/31 23:59:59
}

格式化一个时间日期对象   LocalDateTime  ---> String 也很简单,就想下面一样:

public static void main(String[] args) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
		 
    LocalDateTime date = LocalDateTime.now();
    String dateStr = date.format(formatter);
    System.out.println(dateStr);    		// 2013/12/31 23:59:59
    System.out.println(formatter.format(date)); // 2013/12/31 23:59:59
}

7. Clock时钟

java.time.Clock

时钟,类似于钟表的概念,提供了如系统时钟、固定时钟、特定时区的时钟

//时钟提供给我们用于访问某个特定 时区的 瞬时时间、日期 和 时间的。  
Clock c1 = Clock.systemUTC(); //系统默认UTC时钟(当前瞬时时间 System.currentTimeMillis())  
System.out.println(c1.millis()); //每次调用将返回当前瞬时时间(UTC)  1532179537366 
		  
Clock c2 = Clock.systemDefaultZone(); //系统默认时区时钟(当前瞬时时间)  
System.out.println(c2.millis()); //每次调用将返回当前瞬时时间(UTC)  1532179594406
		  
Clock c3 = Clock.system(ZoneId.of("Asia/Shanghai"));//上海时区  
System.out.println(c3.millis());//每次调用将返回当前瞬时时间(UTC)  1532179594406

8.Instant瞬时时间

java.time.Instant

瞬时时间,等价于以前的System.currentTimeMillis()

//瞬时时间 相当于以前的System.currentTimeMillis()  
Instant instant1 = Instant.now();  
System.out.println(instant1.getEpochSecond());//精确到秒 得到相对于1970-01-01 00:00:00 UTC的时间 1532179778  
System.out.println(instant1.toEpochMilli()); //精确到毫秒   1532179778266
		  
Clock clock1 = Clock.systemUTC(); //获取系统UTC默认时钟  
Instant instant2 = Instant.now(clock1);//得到时钟的瞬时时间  
System.out.println(instant2.toEpochMilli());  
		  
Clock clock2 = Clock.fixed(instant1, ZoneId.systemDefault()); //固定瞬时时间时钟  
Instant instant3 = Instant.now(clock2);//得到时钟的瞬时时间  
System.out.println(instant3.toEpochMilli());//equals instant1 

参考文章: https://blog.csdn.net/wl9739/article/details/51882913

猜你喜欢

转载自blog.csdn.net/qq_42402854/article/details/81146232
今日推荐