前言 Java8发布,已有数年之久,但是发现很多人都还是坚持着用SimpleDateFormat和Date进行时间操作。SimpleDateFormat这个类不是线程安全的,在使用的时候稍不注意,就会产生致命的问题。Date这个类,是可以重新设置时间的,这对于一些类内部的属性来说,是非常不安全的。
在阿里巴巴规约手册里,强制规定SimpleDateFormat是线程不安全的类,当定义为静态变量时,必须加锁处理。忽略线程安全问题,正是大多数Java初学者在进行时间转化时容易踩坑的点。
Date属性可以重新设置时间 比如有User.java如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class User { private String username; private Date birthday; public User (String username, Date birthday) { this .username = username; this .birthday = birthday; } public String getUsername () { return username; } public void setUsername (String username) { this .username = username; } public Date getBirthday () { return birthday; } public void setBirthday (Date birthday) { this .birthday = birthday; } }
我们实例化该User
1 2 3 4 5 6 7 public class Main { public static void main (String[] args) { User user = new User("happyjava" , new Date()); } }
这当然没什么问题,但是我可以通过user.getBirthday()方法获取到birthday的引用,从而修改直接修改birthday的值。如下:
1 2 3 4 5 6 7 public static void main (String[] args) { User user = new User("happyjava" , new Date()); System.out.println(user.getBirthday()); Date birthday = user.getBirthday(); birthday.setTime(11111111L ); System.out.println(user.getBirthday()); }
输出结果如下:
这里可以看到,user对象的birthday属性被修改掉了。这也是Date对象的弊端所在,我们可以通过改写getter方法,使它返回一个新的Date对象即可解决,如下:
1 2 3 4 public Date getBirthday () { return new Date(birthday.getTime()); }
切记这里是不可以用clone方法来生成返回一个新的Date对象的,因为Date类可以被继承,你不能确定调用者是否给birthday设置了一个Date的子类。
Java8提供的新的时间类库LocalDateTime Java8提供了LocalDateTime来替代传统的Date来处理时间,下面,我们就来探讨下这个类库的使用方法吧。
1.获取当前时间
可以通过 LocalDateTime localDateTime = LocalDateTime.now();方法来获取当前时间,测试如下:
1 2 3 4 5 @Test public void testNow () { LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(localDateTime); }
输出结果
2.根据时间戳初始化时间
1 2 3 4 5 6 @Test public void testNewFromTimestamp () { Instant instant = Instant.ofEpochMilli(System.currentTimeMillis()); LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.of("+8" )); System.out.println(dateTime); }
这里的+8意思是东八区,下同。
输出结果:
3.根据字符串获取时间
可以使用LocalDateTime.parse方法对字符串进行转化成时间,如果不传pattern,默认是2019-05-06T11:16:12.361格式。
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void testNewFromString () { String dateStr = "2019-05-06T11:16:12.361" ; LocalDateTime localDateTime = LocalDateTime.parse(dateStr); System.out.println(localDateTime); String pattern = "yyyy-MM-dd HH:mm:ss" ; dateStr = "2019-01-01 12:12:12" ; localDateTime = LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern(pattern)); System.out.println(localDateTime); }
输出结果:
1 2 2019-05-06T11:16:12.361 2019-01-01T12:12:12
4.时间转化成字符串
可以通过DateTimeFormatter的format方法,将LocalDateTime转化成字符串。
1 2 3 4 5 6 7 8 @Test public void testToString () { LocalDateTime now = LocalDateTime.now(ZoneId.of("+8" )); String pattern = "yyyy-MM-dd HH:mm:ss" ; DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); String format = formatter.format(now); System.out.println(format); }
输出结果:
5.LocalDateTime转时间戳
1 2 3 4 5 6 @Test public void testDateToTimeMillis () { LocalDateTime dateTime = LocalDateTime.now(); long epochMilli = dateTime.toInstant(ZoneOffset.of("+8" )).toEpochMilli(); System.out.println(epochMilli); }
输出结果:
总结 因为DateTimeFormatter是线程安全的,所以在实际使用LocalDateTime的时候,可以把DateTimeFormatter定义成静态常量的方式进行使用。以上列举了比较常用的时间操作,LocalDateTime还可以做很多事情,这个就让读者自行去挖掘吧。我自己封装了个LocalDateTime工具类,只做过简单的自测,大家可以参考一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 package happy.localdatetime;import java.time.Instant;import java.time.LocalDateTime;import java.time.ZoneOffset;import java.time.format.DateTimeFormatter;public class DateTimeUtils { private DateTimeUtils () { } private final static String COMMON_PATTERN = "yyyy-MM-dd HH:mm:ss" ; private final static DateTimeFormatter COMMON_FORMATTER = DateTimeFormatter.ofPattern(COMMON_PATTERN); private final static ZoneOffset DEFAULT_ZONE_OFFSET = ZoneOffset.of("+8" ); public static String dateToString (LocalDateTime dateTime) { assert dateTime != null ; return COMMON_FORMATTER.format(dateTime); } public static LocalDateTime stringToDate (String dateStr) { assert dateStr != null ; return LocalDateTime.parse(dateStr, COMMON_FORMATTER); } public static String dateToString (LocalDateTime dateTime, DateTimeFormatter formatter) { assert dateTime != null ; return formatter.format(dateTime); } public static LocalDateTime stringToDate (String dateStr, DateTimeFormatter formatter) { assert dateStr != null ; return LocalDateTime.parse(dateStr, formatter); } public static long dateToTimeMillis (LocalDateTime dateTime) { assert dateTime != null ; return dateTime.toInstant(DEFAULT_ZONE_OFFSET).toEpochMilli(); } public static LocalDateTime timeMillisToDate (long timeMillis) { Instant instant = Instant.ofEpochMilli(timeMillis); return LocalDateTime.ofInstant(instant, DEFAULT_ZONE_OFFSET); } public static void main (String[] args) { String s = dateToString(LocalDateTime.now()); System.out.println(s); System.out.println(); String dateStr = "2019-01-01 12:12:12" ; LocalDateTime localDateTime = stringToDate(dateStr); System.out.println(localDateTime); System.out.println(); System.out.println(dateToTimeMillis(localDateTime)); System.out.println(); System.out.println(timeMillisToDate(System.currentTimeMillis())); } }