你的SimpleDateFormat安全吗

SimpleDateFormat在日常中,最常的处理就是时间格式的问题。各种奇葩的需求。例如:需要比较你的文章发布了多少个小时,服务器通常都是返回文章的发布时间,所以还得自行处理。

你是这么使用的么:

1
2
3
SimpleDateFormat formatter =new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");//注意时间匹配的格式文字
Date curDate = new Date(System.currentTimeMillis());//获取当前时间
String str = formatter.format(curDate);

详细的使用:java 时间 格式化(获取当前时间)

这样使用有什么问题呢?由于SimpleDateFormat线程不安全,在多线程的环境下,可以回导致时间处理得不对。那么需要怎么进行处理呢?

java 8情况下,如何确保SimpleDateFormat线程安全

代码如下:

1
2
3
4
5
6
7
8
9
10
11
/** http://my.oschina.net/liux
* 默认SimpleDateFormat线程不安全,所以需要进行封装
*/

private final static ThreadLocal<SimpleDateFormat> dateFormater = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
# 使用的时候
dateFormater.get().parse(sdate);

如果看了代码还不知道,为什么这么做可以确保线程安全的话,赶紧去脑补下ThreadLocal这个类的作用。

java8使用DateTimeFormatter来确保线程安全

参考文章:Java 8新的时间日期库的20个使用示例,介绍到:DateTimeFormatter这个线程安全的时间处理类来代替SimpleDateFormat

DateTimeFormatter类用于在Java中进行日期的格式化与解析。与SimpleDateFormat不同,它是不可变且线程安全的,如果需要的话,可以赋值给一个静态变量。DateTimeFormatter类提供了许多预定义的格式器,你也可以自定义自己想要的格式。当然了,根据约定,它还有一个parse()方法是用于将字符串转换成日期的,如果转换期间出现任何错误,它会抛出DateTimeParseException异常。类似的,DateFormatter类也有一个用于格式化日期的format()方法,它出错的话则会抛出DateTimeException异常。

时代在进步,赶紧加入java8的大阵容。

下面是java8的时间类的介绍

Date API

The new Date API is comparable with the Joda-Timelibrary, however it's not the same.

Clock

provides access to the current date and time.

  • Clocks are aware of a timezone and may be used instead of System.currentTimeMillis() to retrieve the current milliseconds. 即:代替System.currentTimeMillis()

  • Instants can be used to create legacy java.util.Date objects.

例子:

1
2
3
4
5
6
7
8
9
10
11

Clock clock = Clock.systemDefaultZone();

long millis = clock.millis();




Instant instant = clock.instant();

Date legacyDate = Date.from(instant); // legacy java.util.Date

Timezones

依靠ZoneId生成。

Timezones define the offsets which are important to convert between instants and local dates and times.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

System.out.println(ZoneId.getAvailableZoneIds());

// prints all available timezone ids




ZoneId zone1 = ZoneId.of("Europe/Berlin");

ZoneId zone2 = ZoneId.of("Brazil/East");

System.out.println(zone1.getRules());

System.out.println(zone2.getRules());




// ZoneRules[currentStandardOffset=+01:00]

// ZoneRules[currentStandardOffset=-03:00]

LocalTime

LocalTime represents a time without a timezone, e.g. 10pm or 17:30:15.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

LocalTime now1 = LocalTime.now(zone1);

LocalTime now2 = LocalTime.now(zone2);




System.out.println(now1.isBefore(now2)); // false




long hoursBetween = ChronoUnit.HOURS.between(now1, now2);

long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);




System.out.println(hoursBetween); // -3

System.out.println(minutesBetween); // -239

获取对应的时间已经对时间的操作:比较等。

解析格式化时间格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

LocalTime late = LocalTime.of(23, 59, 59);

System.out.println(late); // 23:59:59




DateTimeFormatter germanFormatter =

DateTimeFormatter

.ofLocalizedTime(FormatStyle.SHORT)

.withLocale(Locale.GERMAN);




LocalTime leetTime = LocalTime.parse("13:37", germanFormatter);

System.out.println(leetTime); // 13:37

LocalDate

LocalDate represents a distinct date, e.g. 2014-03-11.

Keep in mind that each manipulation returns a new instance.

对应的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

LocalDate today = LocalDate.now();

LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);

LocalDate yesterday = tomorrow.minusDays(2);




LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);

DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();

System.out.println(dayOfWeek); // FRIDAY

解析字符串更解析LocalTime一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

DateTimeFormatter germanFormatter =

DateTimeFormatter

.ofLocalizedDate(FormatStyle.MEDIUM)

.withLocale(Locale.GERMAN);




LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter);

System.out.println(xmas); // 2014-12-24

LocalDateTime

LocalDateTime is immutable and works similar to LocalTime and LocalDate. We can utilize methods for retrieving certain fields from a date-time

使用实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59);




DayOfWeek dayOfWeek = sylvester.getDayOfWeek();

System.out.println(dayOfWeek); // WEDNESDAY




Month month = sylvester.getMonth();

System.out.println(month); // DECEMBER




long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);

System.out.println(minuteOfDay); // 1439

转化:java.util.Date.

1
2
3
4
5
6
7
8
9
10
11
12
13

Instant instant = sylvester

.atZone(ZoneId.systemDefault())

.toInstant();




Date legacyDate = Date.from(instant);

System.out.println(legacyDate); // Wed Dec 31 23:59:59 CET 2014

自定义formatters进行匹配解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

DateTimeFormatter formatter =

DateTimeFormatter

.ofPattern("MMM dd, yyyy - HH:mm");




LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter);

String string = formatter.format(parsed);

System.out.println(string); // Nov 03, 2014 - 07:13

注意:跟java.text.NumberFormat不同,DateTimeFormatter是线程安全的。