话说Java一连设计了两套时间工具,分别是日期类型Date,以及日历类型Calendar,按理说用在编码开发中绰绰有余了。然而随着Java的日益普遍使用,人们仍是发现了它们的种种弊端。且不说先天不良的Date类型,单说后起之秀的Calendar类型,这个日历工具在实际开发中仍然存在如下毛病:
一、日历工具获取当前月份的时候,与Date同样都是从0开始计数,好比经过get方法得到的一月份数值为0;
二、日历工具获取当天是星期几的时候,星期日是排在最前面的,经过get方法得到的星期日数值为1,而星期一数值竟然是2!
三、日历工具可以表达的最小时间单位是毫秒,使得时间精度不够高,难以用在更加精密的科学运算场合。
四、日历工具没有提供闰年的判断方法。
五、日历工具缺少本身的格式化工具,竟然还得借助于Date类型那边的格式化工具SimpleDateFormat,方能将日期时间按照指定格式输出为字符串。
总而言之,不论是Date仍是Calendar,在解决复杂问题之时的编码都很别扭,故而每一个Java工程基本要从新编写一个日期处理工具DateUtil,在新工具内部封装常见的日期处理操做,这样才能知足实际业务的开发要求。因而Date和Calendar两个难兄难弟从JDK1.1开始并肩做战,一路走到Java五、Java6乃至Java7,后来估摸着无可救药了,干爹Oracle一不作、二不休,终于在Java8推出了全新的日期时间类型,意图经过新类型一劳永逸治好Date和Calendar的沉疴宿疾。
全新的日期时间类型不仅仅是一个类型,而是一个家族,它的成员主要有LocalDate、LocalTime、LocalDateTime等等,接下来分别介绍这几个日期时间类型:html
一、本地日期类型LocalDate
获取本地日期的实例很简单,调用该类型的now方法便可,而且顾名思义获得的是当前日期。经过本地日期获取年月日的数值,就是平常生活中的习惯数字,例如一月份对应的数值是1,十二月份对应的数值是12,星期一对应的数值是1,星期日对应的数值是7等等。此外,本地日期额外提供了几个经常使用的统计方法,包括:该日期所在的年份一共有多少天、该日期所在的月份一共有多少天、该日期所在的年份是否为闰年等。下面的代码便演示了如何从本地日期获取各类数值的例子:java
// 得到本地日期的实例 LocalDate date = LocalDate.now(); System.out.println("date=" + date.toString()); // 得到该日期所在的年份 int year = date.getYear(); System.out.println("year=" + year); // 得到该日期所在的月份。注意getMonthValue方法返回的是数字月份,而getMonth方法返回的是英文月份 int month = date.getMonthValue(); System.out.println("month=" + month + ", english month=" + date.getMonth()); // 得到该日期所在的日子 int dayOfMonth = date.getDayOfMonth(); System.out.println("dayOfMonth=" + dayOfMonth); // 得到该日期在一年当中的序号 int dayOfYear = date.getDayOfYear(); System.out.println("dayOfYear=" + dayOfYear); // 得到该日期是星期几。注意getDayOfWeek方法返回的是英文的星期几,后面跟着的getValue方法才返回数字的星期几 int dayOfWeek = date.getDayOfWeek().getValue(); System.out.println("dayOfWeek=" + dayOfWeek + ", english weekday=" + date.getDayOfWeek()); // 得到该日期所在的年份一共有多少天 int lengthOfYear = date.lengthOfYear(); System.out.println("lengthOfYear=" + lengthOfYear); // 得到该日期所在的月份一共有多少天 int lengthOfMonth = date.lengthOfMonth(); System.out.println("lengthOfMonth=" + lengthOfMonth); // 判断该日期所在的年份是否为闰年 boolean isLeapYear = date.isLeapYear(); System.out.println("isLeapYear=" + isLeapYear);
除了建立处于当前日期的本地实例,LocalDate还支持建立指定日期的本地实例,就像如下代码示范的那样:工具
// 构造一个指定年月日的日期实例 LocalDate dateManual = LocalDate.of(2018, 11, 22); System.out.println("dateManual=" + dateManual.toString());
至于针对某个单位的数值,LocalDate也提供了专门的修改方法,例如以plus打头的系列方法用来增长日期数值,以minus打头的系列方法用来减小日期数值,以with打头的系列方法用来设置日期数值,这些日期修改的具体用法示例以下:编码
dateManual = dateManual.plusYears(0); // 增长若干年份 dateManual = dateManual.plusMonths(0); // 增长若干月份 dateManual = dateManual.plusDays(0); // 增长若干日子 dateManual = dateManual.plusWeeks(0); // 增长若干星期 dateManual = dateManual.minusYears(0); // 减小若干年份 dateManual = dateManual.minusMonths(0); // 减小若干月份 dateManual = dateManual.minusDays(0); // 减小若干日子 dateManual = dateManual.minusWeeks(0); // 减小若干星期 dateManual = dateManual.withYear(2000); // 设置指定的年份 dateManual = dateManual.withMonth(12); // 设置指定的月份 dateManual = dateManual.withDayOfYear(1); // 设置当年的日子 dateManual = dateManual.withDayOfMonth(1); // 设置当月的日子
此外,做为一种日期类型,LocalDate一如既往地支持判断两个日期实例的迟早关系,好比equals方法用于判断两个日期是否相等,isBefore方法用于判断A日期是否在B日期以前,isAfter方法用于判断A日期是否在B日期以后等。具体的本地日期校验代码以下所示:spa
// 判断两个日期是否相等 boolean equalsDate = date.equals(dateManual); System.out.println("equalsDate=" + equalsDate); // 判断A日期是否在B日期以前 boolean isBeforeDate = date.isBefore(dateManual); System.out.println("isBeforeDate=" + isBeforeDate); // 判断A日期是否在B日期以后 boolean isAfterDate = date.isAfter(dateManual); System.out.println("isAfterDate=" + isAfterDate); // 判断A日期是否与B日期相等 boolean isEqualDate = date.isEqual(dateManual); System.out.println("isEqualDate=" + isEqualDate);
二、本地时间类型LocalTime
前面介绍的LocalDate只能操做年月日,若要操做时分秒则需经过本地时间类型LocalTime。获取本地时间的实例依然要调用该类型的now方法,接着就能经过该实例分别获取对应的时分秒乃至纳秒(一秒的十亿分之一),下面便演示了如何调用LocalTime的基本方法:设计
// 得到本地时间的实例 LocalTime time = LocalTime.now(); System.out.println("time=" + time.toString()); // 得到该时间所在的时钟 int hour = time.getHour(); System.out.println("hour=" + hour); // 得到该时间所在的分钟 int minute = time.getMinute(); System.out.println("minute=" + minute); // 得到该时间所在的秒钟 int second = time.getSecond(); System.out.println("second=" + second); // 得到该时间秒钟后面的纳秒单位。一秒等于一千毫秒,一毫秒等于一千微秒,一微秒等于一千纳秒,算下来一秒等于十亿纳秒 int nano = time.getNano(); System.out.println("nano=" + nano);
如同本地日期LocalDate那样,LocalTime也容许建立指定时分秒的时间实例,还支持单独修改时钟、分钟、秒钟和纳秒。固然修改时间的途径包括plus系列方法、minus系列方法、with系列方法等等,它们的调用方式示例以下:orm
// 构造一个指定时分秒的时间实例 LocalTime timeManual = LocalTime.of(14, 30, 25); System.out.println("timeManual=" + timeManual.toString()); timeManual = timeManual.plusHours(0); // 增长若干时钟 timeManual = timeManual.plusMinutes(0); // 增长若干分钟 timeManual = timeManual.plusSeconds(0); // 增长若干秒钟 timeManual = timeManual.plusNanos(0); // 增长若干纳秒 timeManual = timeManual.minusHours(0); // 减小若干时钟 timeManual = timeManual.minusMinutes(0); // 减小若干分钟 timeManual = timeManual.minusSeconds(0); // 减小若干秒钟 timeManual = timeManual.minusNanos(0); // 减小若干纳秒 timeManual = timeManual.withHour(0); // 设置指定的时钟 timeManual = timeManual.withMinute(0); // 设置指定的分钟 timeManual = timeManual.withSecond(0); // 设置指定的秒钟 timeManual = timeManual.withNano(0); // 设置指定的纳秒
另外,LocalTime依然提供了equals、isBefore、isAfter等方法用于判断两个时间的前后关系,具体的方法调用以下所示:htm
// 判断两个时间是否相等 boolean equalsTime = time.equals(timeManual); System.out.println("equalsTime=" + equalsTime); // 判断A时间是否在B时间以前 boolean isBeforeTime = time.isBefore(timeManual); System.out.println("isBeforeTime=" + isBeforeTime); // 判断A时间是否在B时间以后 boolean isAfterTime = time.isAfter(timeManual); System.out.println("isAfterTime=" + isAfterTime);
三、本地日期时间类型LocalDateTime
如今有了LocalDate专门处理年月日,又有了LocalTime专门处理时分秒,还须要一种类型可以同时处理年月日和时分秒,它就是本地日期时间类型LocalDateTime。LocalDateTime基本等价于LocalDateTime与LocalTime的合集,它同时拥有两者的绝大部分方法,故这里再也不赘述。下面是建立该类型实例的代码片断,读者可参考以前LocalDateTime与LocalTime的调用代码,尝试补齐LocalDateTime的方法调用过程。blog
// 演示LocalDateTime的各类方法 private static void showLocalDateTime() { // 得到本地日期时间的实例 LocalDateTime datetime = LocalDateTime.now(); System.out.println("datetime=" + datetime.toString()); // LocalDateTime的方法是LocalDate与LocalTime的合集, // 也就是说LocalDate与LocalTime的大部分方法能够直接拿来给LocalDateTime使用, // 于是下面再也不演示LocalDateTime的详细方法如何调用了。 // 注意LocalDateTime不提供lengthOfYear、lengthOfMonth、isLeapYear这三个方法。 }
更多Java技术文章参见《Java开发笔记(序)章节目录》开发