虽然java 10已经发布,11 已经在路上,虽然 java EE 已经更名为 jakarta EE,但是大多数人连jdk1.8的新特性都不太了解,本人也是如此,所以在学习Java 8 API 添加的 Data-Time API 的时候 做一些个人笔记,希望帮助自己的同时也能帮到大家。
分析1.8之前的日期类:
1、线程不安全:java.util.Date 这个类线程不安全,而且所有日期类都是可变的。
2、时间处理麻烦:默认的开始日期从1900年,不支持国际化,不提供时区支持,所以经常算出来的时间不是中国时间。
3、设计不好:java初学者接触到导包的时候,总会导错包,比如java.util和java.sql包中都有日期类,类名却是一样的。
那我们一起看看time包下的常用类吧
写在前面:在连接数据库的时候,使用 Java 8 新特性 TimeDateAPI,字段对应不上,或者存入的不是时间,或者直接抛异常,请更新对应数据库的 pom 依赖,添加以下支持
Spring Data API
org.hibernate hibernate-java8 5.0.12.Final MyBatis
org.mybatis mybatis-typehandlers-jsr310 1.0.1
接下来我们针对每一个类写一点简单的例子,方便快速上手。
这个类是不可变的,线程安全。
public class InstantDemo {
public static void main(String[] args) {
//===============================================================================
// 1、获取当前时间戳
//===============================================================================
System.out.println(Instant.now()); // 2018-07-23T07:57:00.821Z
//===============================================================================
// 2、传入CharSequence类型,转换为instant对象
//===============================================================================
CharSequence text = "2010-10-31T07:57:00.821Z";
Instant instant = Instant.parse(text);
System.out.println(instant); // 2010-10-31T07:57:00.821Z
// 注意:格式不正确或者不存在该日期时,会抛出DateTimeParseException异常
//===============================================================================
// 3、比较时间(大于小于排序)
//===============================================================================
System.out.println(instant.isAfter(Instant.parse("2010-11-30T07:57:00.821Z")));
// false 返回布尔值
System.out.println(instant.isBefore(Instant.parse("2010-11-30T07:57:00.821Z")));
// true 返回布尔值
System.out.println(instant.compareTo(Instant.parse("2010-11-30T07:57:00.821Z")));
// -1 compareTo 返回值int类型 等于返回 0 小于返回 -1 大于返回 1
System.out.println(instant.equals(Instant.parse("2010-10-31T07:57:00.821Z")));
// true 返回布尔值 比较两个时间是否相等
//===============================================================================
// 4、获取从1970-01-01T00:00:00Z到现在的秒数
//===============================================================================
System.out.println(instant.getEpochSecond()); // 1288511820
// 返回值long,注意返回的是秒!不是毫秒!
//===============================================================================
// 5、设置时区偏移
//===============================================================================
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime); // 2010-10-31T15:57:00.821+08:00
// 返回值类型OffsetDateTime 我们在东八区,所以加上八小时,就是本地时间戳
}
}
LocalDate是一个不可变的类,它表示默认格式(yyyy-MM-dd)的日期,线程安全。
public class LocalDateDemo {
public static void main(String[] args) {
//===============================================================================
// 1、获取当前日期
//===============================================================================
LocalDate localDate = LocalDate.now();
System.out.println(localDate); // 2018-07-23
//===============================================================================
// 2、构造LocalDate
//===============================================================================
CharSequence text = "2010-10-10";
System.out.println(LocalDate.parse(text)); // 2010-10-10
CharSequence date = "20101010";
System.out.println(LocalDate.parse(date,DateTimeFormatter.BASIC_ISO_DATE));// 2010-10-10
// 自定义对应格式的转换,JDK1.8给出了预定义的格式化 DateTimeFormatter.BASIC_ISO_DATE 就是其中之一
CharSequence myDate = "2010年10月10日";
System.out.println(LocalDate.parse(myDate,DateTimeFormatter.ofPattern("yyyy年MM月dd日"))); // 2010-10-10
// 预定义格式没有的情况,也可以自定义
System.out.println(LocalDate.of(2010, 10, 10));// 2010-10-10
// 给出int类型对应的 年月日,亦可构造出LocalDate对象
System.out.println(LocalDate.of(2010, Month.JULY, 10));// 2010-07-10
// 月份亦可给出对应 Mouth 枚举
System.out.println(LocalDate.ofEpochDay(123)); // 1970-05-04
// 给出日子,计算出 从 1970-01-01 对应日子后的日期
System.out.println(LocalDate.ofYearDay(2010, 163)); //2010-06-12
// 传入 年,日数,计算出 对应该年份 对应日子后的日期
//===============================================================================
// 3、获取,年,月,日
//===============================================================================
System.out.println(localDate.getMonth()); // JULY 返回英文月份,对应 Mouth 枚举
System.out.println(localDate.getMonthValue()); // 7 返回数字月份
System.out.println(localDate.getYear()); // 2018 返回对应年
System.out.println(localDate.getDayOfMonth()); // 23 返回对应月份中的日子
System.out.println(localDate.getDayOfWeek()); // MONDAY 返回对应星期,对应 DayOfWeek 枚举
System.out.println(localDate.getDayOfYear()); // 204 返回对应年份中的日子
System.out.println(localDate.lengthOfMonth()); // 31 对应该月份有多少天
System.out.println(LocalDate.parse("2004-02-10").lengthOfMonth());
// 29 证明可以自动判断是否为闰年!!!
System.out.println(localDate.lengthOfYear()); // 365 对应年份有多少天
System.out.println(LocalDate.parse("2004-02-10").lengthOfYear());
// 366 证明可以自动判断是否为闰年!!!
//===============================================================================
// 4、判断大于小于,排序,判断是否闰年
//===============================================================================
System.out.println(localDate.compareTo(LocalDate.parse("2000-01-01"))); // 18
// 这里的 compareTo 方法为什么返回 18 请看下面详解!!!
System.out.println(localDate.isAfter(LocalDate.parse("2000-01-01"))); // true
System.out.println(localDate.isBefore(LocalDate.parse("2000-01-01"))); // false
System.out.println(localDate.isEqual(LocalDate.parse("2000-01-01"))); // false
System.out.println(localDate.isLeapYear());// false 返回布尔值,该年是否为闰年
//===============================================================================
// 5、日期的加减
//===============================================================================
System.out.println(localDate.plusMonths(1)); // 2018-08-23 加一个月
System.out.println(localDate.plusMonths(-1)); // 2018-06-23 减去一个月,传入负数即可,下面的也是
System.out.println(localDate.plusDays(1)); // 2018-07-24 加上一天
System.out.println(localDate.plusMonths(1)); // 2018-08-23 加上一个月
System.out.println(localDate.plusWeeks(1)); // 2018-07-30 加上一个星期
//===============================================================================
// 6、其他计算
//===============================================================================
System.out.println(localDate.withDayOfYear(123)); // 2018-05-03 计算当前年第123天的日期
}
}
针对上面 compareTo 方法为什么返回 18 ,我们来看一下源码
源码所以也就不难理解为什么返回18了,2018年到2000年差的不就是18年,所以这里返回的是相差的年数,如果年数一样,返回的则是相差的月数,同理相同的月份,则返回相差的天数。
LocalTime是一个不可变的日期时间对象,代表一个时间,通常被看作是小时 - 秒,这个类不可变,线程安全。
public class LocalTimeDemo {
public static void main(String[] args) {
//===============================================================================
// 1、获取当前时间
//===============================================================================
LocalTime time1 = LocalTime.now();
System.out.println(time1); // 09:39:47.275
//===============================================================================
// 2、构造LocalTime
//===============================================================================
// 根据传入文本构造LocalTIme时间对象,格式不正确会抛出 java.time.format.DateTimeParseException 异常
CharSequence text1 = "22:22:22";
LocalTime localTime1 = LocalTime.parse(text1);
System.out.println(localTime1); // 22:22:22
// 传入自定义文本,自定义格式化 构造 LocalTime 对象
CharSequence text2 = "11-11-11";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH-mm-ss");
LocalTime localTime2 = LocalTime.parse(text2, dateTimeFormatter);
System.out.println(localTime2); // 11:11:11
// 根据入参 时,分,秒,纳秒构造出LocalTime对象,24小时制
// 如果入参不符合时间要求,例如传入25小时,61分钟,会抛出 java.time.DateTimeException 异常
LocalTime time2 = LocalTime.of(13, 22);
System.out.println(time2); // 13:22
LocalTime time3 = LocalTime.of(14, 22, 31);
System.out.println(time3); // 14:22:31
LocalTime time4 = LocalTime.of(22, 43, 33, 422);
System.out.println(time4); // 22:43:33.000000422
// 根据分钟 构造 LocalTime 时间对象 超过最大分钟,会抛出 java.time.DateTimeException 异常
LocalTime time5 = LocalTime.ofSecondOfDay(22324);
System.out.println(time5); // 06:12:04
// 根据纳秒构造LocalTime对象
LocalTime time6 = LocalTime.ofNanoOfDay(1231);
System.out.println(time6); // 00:00:00.000001231
//===============================================================================
// 3、获时,分,秒等
//===============================================================================
int hour = time1.getHour();
int minute = time1.getMinute();
int second = time1.getSecond();
System.out.println("小时:" + hour + ",分钟:"
+ minute + ",秒数:" + second); // 小时:10,分钟:5,秒数:53
//===============================================================================
// 4、判断大于小于,排序,相差多少
//===============================================================================
// 判断时间1是否在时间2后
boolean after = time1.isAfter(time2);
System.out.println(after); // false
// 判断时间1是否在时间2前
boolean before = time1.isBefore(time2);
System.out.println(before); // true
// 排序比较,前者大于后者返回 1,否则 -1,相等 0
int i = time1.compareTo(time2);
System.out.println(i); // -1
// 时间1和时间2相差多少小时,分钟,秒,大于为正数,小于为负数(坏处就是分跟分加减,秒跟秒加减)
long betweenHours = ChronoUnit.HOURS.between(time1, time2);
long betweenSeconds = ChronoUnit.SECONDS.between(time1, time2);
long betweenMinutes = ChronoUnit.MINUTES.between(time1, time2);
System.out.println("相差小时:" + betweenHours + " 分钟: " +
betweenMinutes + " 秒数:" + betweenSeconds); // 相差小时:2 分钟: 178 秒数:10707
//===============================================================================
// 5、时间的加减,修改
//===============================================================================
// 加两小时,减去传入负数即可
LocalTime time7 = time3.plusHours(2);
System.out.println(time7); // 16:22:31
// 减去10分钟
LocalTime time8 = time3.plusMinutes(-10);
System.out.println(time8); // 14:12:31
// 加上30秒
LocalTime time9 = time3.plusSeconds(30);
System.out.println(time9); // 14:23:01
// 修改小时
LocalTime time10 = time3.withHour(23);
System.out.println(time10); // 23:22:31
// 修改分钟
LocalTime time11 = time3.withMinute(11);
System.out.println(time11); // 14:11:31
// 修改秒数
LocalTime time12 = time3.withSecond(50);
System.out.println(time12); // 14:22:50
//===============================================================================
// 6、两个时间之间的相差秒数,小时
//===============================================================================
Duration duration = Duration.between(time3, time12);
long days = duration.toDays();
long hours = duration.toHours();
long millis = duration.toMillis()/1000;
}
}
是一个不可变的日期时间对象,代表日期时间,年-月-日-时-分-秒,线程安全。
public class LocalDateTimeDemo {
public static void main(String[] args) {
//===============================================================================
// 1、获取当前日期时间
//===============================================================================
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime); // 2018-07-26T14:47:44.144
//===============================================================================
// 2、构造日期时间
//===============================================================================
CharSequence text = "2007-12-03T10:15:30";
// 从一个文本字符串 获得 LocalDateTime实例
LocalDateTime dateTime1 = LocalDateTime.parse(text);
System.out.println(dateTime1); // 2007-12-03T10:15:30
//===============================================================================
// 3、获取,年,月,日,时等等
//===============================================================================
int dayOfMonth = localDateTime.getDayOfMonth();
DayOfWeek dayOfWeek = localDateTime.getDayOfWeek();
int dayOfYear = localDateTime.getDayOfYear();
int hour = localDateTime.getHour();
Month month = localDateTime.getMonth();
int year = localDateTime.getYear();
System.out.println(year + "年" + month.toString() + "月" + dayOfMonth + "日" +
hour + "时" + "|||" + dayOfWeek + "|||" + dayOfYear);
// 2018年JULY月31日9时|||TUESDAY|||212
}
}
是一个不变的日期时间对象,表示一年和一个月的组合。 可以获得可以从年和月派生的任何字段,例如四分之一年份。类线程安全
public class YearMonthDemo {
public static void main(String[] args) {
//===============================================================================
// 1、获取当前年月
//===============================================================================
YearMonth yearMonth = YearMonth.now();
System.out.println(yearMonth); // 2018-07
//===============================================================================
// 2、构造年月
//===============================================================================
CharSequence text = "2018-01";
YearMonth yearMonth1 = YearMonth.parse(text);
System.out.println(yearMonth1); // 2018-01
CharSequence text2 = "2018年02月";
YearMonth yearMonth2 = YearMonth.parse(text2, DateTimeFormatter.ofPattern("yyyy年MM月"));
System.out.println(yearMonth2); // 2018-02
//===============================================================================
// 3、获取,年,月
//===============================================================================
int year = yearMonth.getYear();
Month month = yearMonth.getMonth();
int monthValue = yearMonth.getMonthValue();
System.out.println(year + "年" + monthValue + "月" + "mouth对象-》" + month);
// 2018年7月mouth对象-》JULY
//===============================================================================
// 4、判断大于小于,排序,判断是否闰年
//===============================================================================
System.out.println(yearMonth.isAfter(yearMonth1)); // true
System.out.println(yearMonth.isBefore(yearMonth1)); // false
System.out.println(yearMonth.isLeapYear()); // 是否闰年 false
System.out.println(yearMonth.compareTo(yearMonth1)); // 6 (这里为什么是6 上面有详解)
//===============================================================================
// 5、年月的加减,设置年月
//===============================================================================
YearMonth yearMonth3 = yearMonth.plusMonths(1);
System.out.println(yearMonth3); // 2018-08
YearMonth yearMonth4 = yearMonth.plusYears(-1);
System.out.println(yearMonth4); // 2017-07
YearMonth yearMonth5 = yearMonth.withMonth(5);
System.out.println(yearMonth5); // 2018-05
YearMonth yearMonth6 = yearMonth.withYear(2000);
System.out.println(yearMonth6); // 2000-07
}
}
是一个不变的日期时间对象,代表一年和一个月的组合。 可以获得可以从月和日派生的任何字段,线程安全。
public class MonthDayDemo {
public static void main(String[] args) {
//===============================================================================
// 1、获取当前月日
//===============================================================================
MonthDay monthDay = MonthDay.now();
System.out.println(monthDay); // --07-31
//===============================================================================
// 2、构造月日
//===============================================================================
CharSequence text1 = "--03-01";
MonthDay monthDay1 = MonthDay.parse(text1);
System.out.println(monthDay1); // --03-01
CharSequence text2 = "11月23日";
MonthDay monthDay2 = MonthDay.parse(text2, DateTimeFormatter.ofPattern("MM月dd日"));
System.out.println(monthDay2); // --11-23
MonthDay monthDay3 = MonthDay.of(2, 3);
System.out.println(monthDay3); // --02-03
MonthDay monthDay4 = MonthDay.of(Month.AUGUST, 22);
System.out.println(monthDay4); // --08-22
//===============================================================================
// 3、获取 月,日
//===============================================================================
int monthValue = monthDay.getMonthValue();
Month month = monthDay.getMonth();
int dayOfMonth = monthDay.getDayOfMonth();
System.out.println(monthValue + "月" + dayOfMonth + "日" + "月份-》" + month);
// 7月31日月份-》JULY
//===============================================================================
// 4、判断大于小于,排序
//===============================================================================
System.out.println(monthDay.isAfter(monthDay1)); // true
System.out.println(monthDay.isBefore(monthDay1)); // false
System.out.println(monthDay.compareTo(monthDay1)); // 4 (这里为什么是4,上面有详解)
}
}
针对业务上常见的需求,日期处理,这里给出了示范,根据示范举一反三,我想可以解决99%以上的日期操作。
public class NewTimeDemo {
public static void main(String args[]) {
// 输入 20180101
CharSequence str = "20040201";
// BASIC_ISO_DATE -》ISO日期格式化,格式或解析无偏移的日期,如“20111203”。
LocalDate localDate = LocalDate.parse(str, BASIC_ISO_DATE);
//===============================================================================
// 1、 这个月月末
//===============================================================================
System.out.println("本月月末是:" + localDate.with(TemporalAdjusters.lastDayOfMonth()));
// 本月月末是:2004-02-29
//===============================================================================
// 2、 下个月月初
//===============================================================================
System.out.println("下月月初是:" + localDate.with(TemporalAdjusters.firstDayOfNextMonth()));
//下月月初是:2004-03-01
//===============================================================================
// 3、 下个月月末
//===============================================================================
System.out.println("下月月末是:" + localDate.with(TemporalAdjusters.firstDayOfNextMonth())
.with(TemporalAdjusters.lastDayOfMonth()));
// 下月月末是:2004-03-31
//===============================================================================
// 4、 给年月,计算当月天数
//===============================================================================
CharSequence str2 = "200102";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMM");
YearMonth ym = YearMonth.parse(str2, dateTimeFormatter);
System.out.println(ym.lengthOfMonth()); // 28
//===============================================================================
// 5、 日期加减天数
//===============================================================================
System.out.println(localDate.plusDays(-1)); // 2004-01-31
//===============================================================================
// 6、 日期加减月份
//===============================================================================
System.out.println(localDate.plusMonths(-1)); // 2004-01-01
System.out.println(localDate.plusMonths(1)); // 2004-03-01
//===============================================================================
// 7、 传参月份,日子,输出修改后的
//===============================================================================
try {
System.out.println(localDate.withMonth(2).withDayOfMonth(2));
// 2004-02-02
} catch (DateTimeException exception) {
System.out.println("输入日期异常!");
}
System.out.println(localDate.withDayOfMonth(1));
// 2004-02-01
//===============================================================================
// 8、 小于15日,输出该月15日,否则输出下月1日
//===============================================================================
System.out.println("--------------------");
if (localDate.getDayOfMonth() < 15) {
System.out.println(localDate.withDayOfMonth(15));
} else {
System.out.println(localDate.with(TemporalAdjusters.firstDayOfNextMonth()));
}
// 2004-02-15
}
}
总结
1、构造对应时间对象,使用 .now() .parse() ;
2、获取相关时间单位,使用 .get() .lengthOf..() ;
3、判断、比较,使用 .is...() .compareTo() ;
4、日期加减,使用 .plus() ;
5、其他计算,使用 .with() .with(TemporalAdjusters.......()) ;