新的API 格式化(字符串 -><- 字符串 互转)
public static void main(String[] args) { //字符串转化为日期对象 String dateStr= "2016年10月25日"; DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日"); LocalDate date= LocalDate.parse(dateStr, formatter); //日期转换为字符串 LocalDateTime now = LocalDateTime.now(); DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy年MM月dd日 hh:mm a"); String nowStr = now .format(format); System.out.println(nowStr); //2018年08月07日 12:15 上午 }
- DateTimeFormatter预定义了一些格式,可以直接调用format方法,方便调用者使用
//2017-01-01 DateTimeFormatter.ISO_LOCAL_DATE.format(LocalDate.of(2017, 1, 1)) //20170101 DateTimeFormatter.BASIC_ISO_DATE.format(LocalDate.of(2017, 1, 1)); //2017-01-01T09:10:00 DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.of(2017, 1, 1, 9, 10, 0));
根据当前操作系统语言环境,有SHORET MEDIUM LONG FULL 四种不同的风格来格式化。
可以通过DateTimeFormatter的静态方法ofLocalizedDate ofLocalizedTime ofLocalizedDateTime
- 使用自定义模式格式化
//2017-02-27 22:48:52 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now())
当然也可以这么搞
//使用的ISO_LOCAL_DATE格式解析 2017-01-01 LocalDate.parse("2017-01-01"); //使用自定义格式解析 2017-01-01T08:08:08 LocalDateTime.parse("2017-01-01 08:08:08", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
在DateTimeFormatter中还有很多定义好的格式,有兴趣的可以自己去看一下
SimpleDateFormat是线程不安全的,所以在高并发环境下,建议这么搞
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); } }; //和线程绑定 保证安全 public static String format(Date date) { return threadLocal.get().format(date);
注意:ofPattern(String pattern)和ofPattern(String pattern, Locale locale)还是有区别的。但绝大多数情况下,我们用ofPattern就够了,因为Locale对象根据用户的国家,地区文化差异格式化,不会改变系统时间,只是表达方式变而已,就是数值表示方法不同而已,也是一样的值,这个方法不常用,因为不能覆盖所有语言环境。并且和格式化模版有关,比如我们的最常用yyyy-MM-dd HH:mm:ss会没有效果。但是这种模版“GGGG yyyy/MMMM/dd HH:mm:ss EEE”,Local不同,展示方式是有很大不同的
Date类型和时间戳 转换成新的时间类型
Date在1.8之后提供了几个方法,可以很方便的转换成新的API
//时间戳转instant就很简单了 Instant instant = Instant.ofEpochMilli(System.currentTimeMillis()); System.out.println(instant); //2018-08-06T16:26:08.539Z(其实已经24点了,所以直接输出是有时区问题的 需要注意) //Date直接转Instant System.out.println(new Date().toInstant()); //2018-08-06T16:26:08.539Z //Instant --> Date Date.from(Instant.now()); //Calendar --> Instant(这个用得很少) Calendar.getInstance().toInstant();
理论知识就介绍到这了,接下来看一些有意思的案例实现,可以更好的了解应用场景
根据已经了解的策略模式,我们可以很好的猜到,LocalDate、LocalTime、LocalDateTime他们之前的互相转换,也是可以走from方法的,如下:
LocalDateTime localDateTime = LocalDateTime.now(); LocalDate localDate = LocalDate.from(localDateTime); LocalTime localTime = LocalTime.from(localDateTime); System.out.println(localDate); //2018-08-13 System.out.println(localTime); //16:04:48.356 ///下面的会报错哟/// //LocalTime localTime = LocalTime.now(); //LocalDate localDate = LocalDate.from(localTime); //这样转是会报错的 因为LocalTime不含有Date元素 Unable to obtain LocalDate from TemporalAccessor: 16:01:47.541 of type java.time.LocalTime //LocalDateTime localDateTime = LocalDateTime.from(localTime); //这样转也是会报错的 因为不含有date元素 //System.out.println(localTime); //System.out.println(localDateTime);
重要:常用:LocalDate和Date类、时间戳之间转换的坑
Date对象表示特定的日期和时间,而LocalDate(Java8)对象只包含没有任何时间信息的日期。 因此,如果我们只关心日期而不是时间信息,则可以在Date和LocalDate之间进行转换
在JDK8以前,我们经常遇到用Date类型来装载时间。有时候只表示日期,有时候是日期+时间,但是我们的选择都只能是Date类型。因此Date类型到LocalDate、LocalTime、Instant等类型的转换 显得尤为重要了。
这里面需要注意一个坑:他们转换的中间桥梁都是时间戳Instant对象,但是转换的时候如果没有考虑时区,就会报错的。
比如下面这个例子,看起来顺滑,其实异常了:
Date date = new Date(); Instant instant = date.toInstant(); //看起来非常顺滑 但其实 异常:Unable to obtain LocalDate from TemporalAccessor: 2018-08-31T02:41:28.076Z of type java.time.Instant LocalDate from = LocalDate.from(date.toInstant());
其实这个也好理解。人家Date是带有日期和时间的,然后突然来一个只需要日期的,LocalDate不知道咋处理(或者说JDK8没考虑到这一点,其实不是,因为时区没定,LocalDate自己不好自己做定论),所以不允许直接转换也可以理解。所以各位使用起一定要小心使用了
糗事Date和LocalDate、LocalTime等互相转化的的思想也很简单 借助LocalDateTime对象就万无一失了。
Date date = new Date(); Instant instant = date.toInstant(); //以ZoneId.systemDefault转换成LocalDateTime后,就可以随意转换了 LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); //方式一:使用LocalDate、LocalTime的from LocalDate fromLocalDate = LocalDate.from(localDateTime); LocalTime fromLocalTime = LocalTime.from(localDateTime); System.out.println(fromLocalDate); //2018-08-31 System.out.println(fromLocalTime); //11:03:19.716 //方式二:直接to的方式 LocalDate toLocalDate = localDateTime.toLocalDate(); LocalTime toLocalTime = localDateTime.toLocalTime(); System.out.println(toLocalDate); //2018-08-31 System.out.println(toLocalTime); //11:03:19.716
反向转换:借助的中间变量是Instant即可
public static void main(String[] args) { LocalDateTime localDateTime = LocalDateTime.now(); LocalDate localDate = LocalDate.now(); LocalTime localTime = LocalTime.now(); Instant instant = null; ZoneId zone = ZoneId.systemDefault(); //LocalDateTime转Instant转Date instant = localDateTime.atZone(zone).toInstant(); System.out.println(Date.from(instant)); //LocalDate转Instant转Date instant = localDate.atStartOfDay().atZone(zone).toInstant(); System.out.println(Date.from(instant)); //LocalTime转Instant转Date(很麻烦 一般杜绝这样使用吧) //必须先借助localDate转换成localDateTime 在转成instant 再转date LocalDateTime localDateTimeDate = LocalDateTime.of(localDate, localTime); instant = localDateTime.atZone(zone).toInstant(); System.out.println(Date.from(instant)); }
时间矫正器(TemporalAdjuster )
Java8推出了时间矫正器的概念。可以辅助我们更精准的定位到一些日期,比如写个周日,下个结婚纪念日等等。
TemporalAdjuster : 时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。不过它是个接口,并且是函数式接口
TemporalAdjusters : 该类通过静态方法提供了大量的常用TemporalAdjuster 的实现。
时间矫正,用的都是with语法。可以理解成和set差不多
public static void main(String[] args) { LocalDateTime ldt1 = LocalDateTime.now(); //本月第一天 LocalDateTime ldt2 = ldt1.with(TemporalAdjusters.firstDayOfMonth()); System.out.println(ldt2); //2018-08-01T17:34:42.039 //本月的第一个周五 LocalDateTime ldt3 = ldt1.with(TemporalAdjusters.firstInMonth(DayOfWeek.FRIDAY)); System.out.println(ldt3); //2018-08-03T17:41:07.619 }
接下来一个场景会比较有意思点:下一个信用卡还款日是什么时候。
public static void main(String[] args) { LocalDate localDate = LocalDate.now(); //下一个工作日(不考虑法定节假日的情况) 自己实现一个时间矫正器 LocalDate with = localDate.with(x -> { LocalDate date = LocalDate.class.cast(x); DayOfWeek dayOfWeek = date.getDayOfWeek(); if (dayOfWeek == DayOfWeek.FRIDAY) { return date.plusDays(3); } else if (dayOfWeek == DayOfWeek.SATURDAY) { return date.plusDays(2); } else { return date.plusDays(1); } }); System.out.println(with); //2018-08-10 }
时间矫正器,在很多场景下,还是非常有用的。所以希望读者能够大概掌握
Java中处理日期、时间的经典案例场景
检查两个日期是否相等
LocalDate重写了equals方法来进行日期的比较,如下所示:
在java8中如何检查重复事件,比如生日
这是相对比较常用的一个场景:判断今天是否是某个人的生日。
通过列子可以看到MonthDay只存储了月日,对比两个日期的月日即可知道是否重复,而且使用了equals方法,非常的方便快捷有木有