new SimpleDateFormat()
吗?先说结论:
尽量少用SimpleDateFormat,原因有以下两点
如果使用static将SImpleDateFormat设置成共享变量,这会造成线程安全问题。如果一定要使用共享变量,那需要加锁! 或者可以使用JDK8+,使用DateTimeFormatter代替 SimpleDateFormat,这是一个线程安全的格式化工具类
两者最大的区别是,Java 8 新增的 DateTimeFormatter
是线程安全的,而 SimpleDateFormat
是线程不安全的。
DateTimeFormatter
是在Java 8
中引入的一个格式化器,用于打印和解析日期时间对象。
DateTimeFormatter
是不可变的,并且是线程安全的。
DateTimeFormatter使用用户定义的格式(如 "yyyy-MMM-dd hh:mm:ss"
)或使用预定义的常数(如ISO_LOCAL_DATE_TIME
)来格式化日期时间。
一个DateTimeFormatter
可以用所需的Locale
、Chronology
、ZoneId
和DecimalStyle
创建。
DateTimeFormatter
通过使用其ofPattern
方法被实例化为一个日期时间格式字符串。
实例化DateTimeFormatter
:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // yyyy-MMM-dd HH:mm:ss
代码,用给定的格式打印日期时间(date-time
)对象的代码:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String dateTime = formatter.format(LocalDateTime.now());
System.out.println(dateTime); //
用给定的格式解析一个日期时间(date-time
)对象:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime ldt = LocalDateTime.parse("2018-Dec-20 08:25:30", formatter);
System.out.println(ldt); //2018-12-20T08:25:30
下面,我们将讨论DateTimeFormatter
的方法,并举例说明LocalDate
、LocalDateTime
和LocalTime
实例的格式。
DateTimeFormatter
有以下静态方法来实例化DateTimeFormatter
。
FormatStyle
是一个枚举,其值可以是FULL
, LONG
, MEDIUM
, SHORT
。date-time
)格式的格式化器。date-time
)格式的格式化器。我们需要为日期和时间分别传递FormatStyle
。例如,日期可以是LONG
,时间可以是SHORT
。import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;
public class DateTimeFormatterDemo {
public static void main(String[] args) {
LocalDate localDate = LocalDate.now();
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("MMM dd, yyyy");
String formattedDate1 = formatter1.format(localDate);
System.out.println(formattedDate1); //Dec 17, 2018
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("MMM dd, yyyy", Locale.CANADA);
String formattedDate2 = formatter2.format(localDate);
System.out.println(formattedDate2); //Dec. 17, 2018
DateTimeFormatter formatter3 = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL);
String formattedDate3 = formatter3.format(localDate);
System.out.println(formattedDate3); //Monday, December 17, 2018
LocalDateTime localDateTime = LocalDateTime.now();
DateTimeFormatter formatter4 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
String formattedDate4 = formatter4.format(localDateTime);
System.out.println(formattedDate4); //Dec 17, 2018, 9:14:39 PM
DateTimeFormatter formatter5 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.SHORT);
String formattedDate5 = formatter5.format(localDateTime);
System.out.println(formattedDate5); //December 17, 2018, 9:14 PM
LocalTime localTime = LocalTime.now();
DateTimeFormatter formatter6 = DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM);
String formattedDate6 = formatter6.format(localTime);
System.out.println(formattedDate6); //9:14:39 PM
}
}
输出
Dec 17, 2018
Dec. 17, 2018
Monday, December 17, 2018
Dec 17, 2018, 9:14:39 PM
December 17, 2018, 9:14 PM
9:14:39 PM
它是一个关于本地化日期、时间或日期时间(date-time
)格式化风格的枚举。
它有以下常数。
1.FULL:例如 'Tuesday, April 11, 2015 AD' or '5:30:45pm PST'
.
'January 10, 2018'
.'Jan 10, 2018'
'11.15.50' or '6:30pm'
.为了格式化一个日期、时间或日期时间(date-time
),DateTimeFormatter
提供了以下方法。
date-time
)对象进行格式化,并以字符串形式返回。date-time
)对象进行格式化,并将结果附加到给定的Appendable
对象中。Appendable
对象可以是StringBuffer
、StringBuilder
等的实例。import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateTimeFormatterDemo {
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MMM-dd hh:mm:ss");
LocalDateTime ldt = LocalDateTime.now();
System.out.println(dtf.format(ldt)); //2018-Dec-20 03:50:45
StringBuffer sb = new StringBuffer("Date ");
dtf.formatTo(ldt, sb);
System.out.println(sb); //Date 2018-Dec-20 03:50:45
}
}
输出
2018-Dec-20 03:50:45
Date 2018-Dec-20 03:50:45
LocalDate
是一个在ISO-8601
日历系统中没有时区的日期。
找到使用DateTimeFormatter
格式化LocalDate
的例子。
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class DateTimeFormatterDemo {
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MMM-dd");
LocalDate ld = LocalDate.now();
System.out.println(dtf.format(ld)); //2018-Dec-20
dtf = DateTimeFormatter.ofPattern("yyyy-MMM-dd(E)");
ld = LocalDate.now();
System.out.println(dtf.format(ld)); //2018-Dec-20(Thu)
dtf = DateTimeFormatter.ofPattern("MMM dd, YYYY");
ld = LocalDate.now();
System.out.println(dtf.format(ld)); //Dec 20, 2018
}
}
输出
2018-Dec-20
2018-Dec-20(Thu)
Dec 20, 2018 java
DateTimeFormatter
也被用来解析一个本地日期。
示例代码:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MMM dd, yyyy");
LocalDate ld = LocalDate.parse("Dec 20, 2018", dtf);
System.out.println(ld);
输出
2018-12-20
LocalDateTime
是ISO-8601
日历系统中没有时区的日期时间。
查找使用DateTimeFormatter
格式化LocalDateTime
的示例。
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateTimeFormatterDemo {
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MMM-dd hh:mm:ss");
LocalDateTime ldt = LocalDateTime.now();
System.out.println(dtf.format(ldt)); //2018-Dec-20 07:40:03
dtf = DateTimeFormatter.ofPattern("yyyy-MMM-dd(E) hh:mm:ss a");
ldt = LocalDateTime.now();
System.out.println(dtf.format(ldt)); //2018-Dec-20(Thu) 07:40:03 PM
dtf = DateTimeFormatter.ofPattern("yy-MM-dd HH:mm:ss");
ldt = LocalDateTime.now();
System.out.println(dtf.format(ldt)); //18-12-20 19:40:03
}
}
输出
2018-Dec-20 07:40:03
2018-Dec-20(Thu) 07:40:03 PM
18-12-20 19:40:03
DateTimeFormatter
也被用来解析本地日期时间。
示例代码。
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss");
LocalDateTime ldt = LocalDateTime.parse("2018-Dec-20 08:25:30", dtf);
System.out.println(ldt);
输出
2018-12-20T08:25:30
LocalTime
是ISO-8601
日历系统中没有时区的时间。
查找使用DateTimeFormatter
格式化LocalTime
的例子。
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
public class DateTimeFormatterDemo {
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("hh:mm:ss");
LocalTime lt = LocalTime.now();
System.out.println(dtf.format(lt)); //08:03:32
dtf = DateTimeFormatter.ofPattern("hh:mm:ss a");
lt = LocalTime.now();
System.out.println(dtf.format(lt)); //08:03:32 PM
dtf = DateTimeFormatter.ofPattern("HH:mm");
lt = LocalTime.now();
System.out.println(dtf.format(lt)); //20:03
}
}
输出
08:03:32
08:03:32 PM
20:03
DateTimeFormatter
也被用来解析本地时间。
示例代码。
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("HH:mm:ss");
LocalTime lt = LocalTime.parse("08:25:30", dtf);
System.out.println(lt);
输出
08:25:30
DateTimeFormatter
提供以下方法来解析文本。
1.
TemporalAccessor parse(CharSequence text)
解析一个日期、时间或日期时间(date-time
)的文本,并返回时间性(temporal
)对象。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy hh:mm:ss");
TemporalAccessor ta = formatter.parse("18-Dec-2017 02:46:41");
System.out.println(ta.get(ChronoField.YEAR));
System.out.println(ta.get(ChronoField.HOUR_OF_AMPM));
输出
2017
2
2.
TemporalAccessor parse(CharSequence text, ParsePosition position)
我们可以通过ParsePosition
来转义给定文本中的一些字符。
我们用给定的初始索引启动一个ParsePosition
,parse
方法将从那里开始解析给定的文本。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy hh:mm:ss");
TemporalAccessor ta = formatter.parse("Date 18-Dec-2017 02:46:41", new ParsePosition(5));
System.out.println(ta.get(ChronoField.YEAR));
System.out.println(ta.get(ChronoField.HOUR_OF_AMPM));
输出
2017
2
3.
<T> T parse(CharSequence text, TemporalQuery<T> query)
解析给定的文本并返回TemporalQuery
指定的对象。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy hh:mm:ss");
LocalDate localDate = formatter.parse("18-Dec-2017 02:46:41", TemporalQueries.localDate());
System.out.println(localDate);
输出
2017-12-18
4.
TemporalAccessor parseBest(CharSequence text, TemporalQuery<?>... queries)
解析给定文本并返回其中一个指定类型。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy hh:mm:ss");
TemporalAccessor ta = formatter.parseBest("18-Dec-2017 02:46:41",
TemporalQueries.localDate(), TemporalQueries.localTime());
System.out.println(ta);
输出
2017-12-18
5.
TemporalAccessor parseUnresolved(CharSequence text, ParsePosition position)
用给定的ParsePosition
解析给定的文本,但不解决它。
这意味着即使月日是38
,它也不会产生错误。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy hh:mm:ss");
TemporalAccessor ta = formatter.parseUnresolved("Date 38-Dec-2017 02:46:41", new ParsePosition(5));
System.out.println(ta);
输出
{DayOfMonth=38, ClockHourOfAmPm=2, MinuteOfHour=46, YearOfEra=2017, SecondOfMinute=41, MonthOfYear=12},null
6.
static TemporalQuery<Period> parsedExcessDays()
提供一个查询,以访问作为已解析的Period
的多余天数。
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
public class DateTimeFormatterDemo {
public static void main(String[] args) {
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("HH:mm");
TemporalAccessor parsed1 = formatter1.parse("24:00");
LocalTime lt1 = parsed1.query(LocalTime::from);
Period excessDays1 = parsed1.query(DateTimeFormatter.parsedExcessDays());
System.out.println(lt1 + " , " + excessDays1); //00:00 , P1D
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
TemporalAccessor parsed2 = formatter2.parse("2018-12-03 24:00");
LocalDateTime lt2 = parsed2.query(LocalDateTime::from);
Period excessDays2 = parsed2.query(DateTimeFormatter.parsedExcessDays());
System.out.println(lt2 + " , " + excessDays2); //2018-12-04T00:00 , P0D
}
}
输出
00:00 , P1D
2018-12-04T00:00 , P0D
我们可以看到,当我们只有时间时,24:00
(一天的结束),我们得到的时间是00
和超过1
天(P1D
意味着有1
天的时间)。
但是当我们同时提供日期和时间时,在这种情况下,多余的天数就会加到日期部分。在我们的例子中,我们可以看到第3
天已经变成了第4
天,多余的天数为0
。
7.
static TemporalQuery<Boolean> parsedLeapSecond()
提供一个查询,用于访问是否解析了闰秒。
如果解析到闰秒,该查询返回真,否则返回假。
在UTC
时区,闰秒发生在'23:59:60'
。
在其他时区,时间可能不同。
查找示例。
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
public class DateTimeFormatterDemo {
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
TemporalAccessor parsed = formatter.parse("2017-12-31T23:59:60Z");
Instant instant = parsed.query(Instant::from);
System.out.println(instant);
System.out.println("leap second parsed="
+ parsed.query(DateTimeFormatter.parsedLeapSecond()));
}
}
输出
2017-12-31T23:59:59Z
leap second parsed=true
DateTimeFormatter.ISO_INSTANT
用UTC
格式化一个瞬间(instant
)。
下列方法返回DateTimeFormatter
实例。
我们可以在使用DateTimeFormatterBuilder
实例化DateTimeFormatter
时使用上述方法。
查找示例代码。
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.chrono.IsoChronology;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DecimalStyle;
import java.time.format.ResolverStyle;
import java.time.format.TextStyle;
import java.time.temporal.ChronoField;
import java.util.Locale;
public class DateTimeFormatterDemo {
public static void main(String[] args) {
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
DateTimeFormatter formatter = builder.appendLiteral("Day is:")
.appendValue(ChronoField.DAY_OF_MONTH)
.appendLiteral(", month is:")
.appendValue(ChronoField.MONTH_OF_YEAR)
.appendLiteral(", and year:")
.appendPattern("u")
.appendLiteral(" with the time:")
.appendValue(ChronoField.HOUR_OF_DAY)
.appendLiteral(":")
.appendText(ChronoField.MINUTE_OF_HOUR, TextStyle.NARROW_STANDALONE)
.toFormatter()
.withDecimalStyle(DecimalStyle.STANDARD)
.withChronology(IsoChronology.INSTANCE)
.withLocale(Locale.CANADA)
.withResolverStyle(ResolverStyle.LENIENT)
.withZone(ZoneId.systemDefault());
LocalDateTime dateTime = LocalDateTime.now();
String str = dateTime.format(formatter);
System.out.println(str);
}
}
输出
Day is:20, month is:12, and year:2018 with the time:11:36
我们可以使用以下方法获取DateTimeFormatter
对象信息。
getChronology():获取年表。
getDecimalStyle():获取十进制风格。
getLocale():获取区域设置。
getResolverFields():获取解析器字段。
getResolverStyle():获取解析器样式。
getZone():获取区域。
示例
import java.time.format.DateTimeFormatter;
public class DateTimeFormatterDemo {
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
System.out.println("Chronology: " + dtf.getChronology());
System.out.println("DecimalStyle: " + dtf.getDecimalStyle());
System.out.println("Locale: "+ dtf.getLocale());
System.out.println("ResolverFields: "+ dtf.getResolverFields());
System.out.println("ResolverStyle: "+ dtf.getResolverStyle());
System.out.println("Zone: "+ dtf.getZone());
}
}
输出
Chronology: ISO
DecimalStyle: DecimalStyle[0+-.]
Locale: en_US
ResolverFields: null
ResolverStyle: STRICT
Zone: null
DateTimeFormatter
提供以下方法将DateTimeFormatter
转换为java.text.Format
。
示例
import java.text.Format;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateTimeFormatterDemo {
public static void main(String[] args) {
DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("MMM dd, yyyy");
Format format1 = dtf1.toFormat();
String ld = format1.format(LocalDate.parse("2017-12-20"));
System.out.println(ld); //Dec 20, 2017
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("HH:mm:ss");
Format format2 = dtf2.toFormat();
String time = format2.format(LocalDateTime.now());
System.out.println(time); //12:34:23
}
}
输出
Dec 20, 2017
12:34:23
DateTimeFormatter
有以下预定义的格式。
Formatter | Example |
---|---|
BASIC_ISO_DATE | ‘20181203’ |
ISO_LOCAL_DATE | ‘2018-12-03’ |
ISO_OFFSET_DATE | ‘2018-12-03+01:00’ |
ISO_DATE | ‘2018-12-03+01:00’; ‘2018-12-03’ |
ISO_LOCAL_TIME | ‘11:15:30’ |
ISO_OFFSET_TIME | ‘11:15:30+01:00’ |
ISO_TIME | ‘11:15:30+01:00’; ‘11:15:30’ |
ISO_LOCAL_DATE_TIME | ‘2018-12-03T11:15:30’ |
ISO_OFFSET_DATE_TIME | ‘2018-12-03T11:15:30+01:00’ |
ISO_ZONED_DATE_TIME | ‘2018-12-03T11:15:30+01:00[Europe/Paris]’ |
ISO_DATE_TIME | ‘2018-12-03T11:15:30+01:00[Europe/Paris]’ |
ISO_ORDINAL_DATE | ‘2018-337’ |
ISO_WEEK_DATE | ‘2018-W48-6’ |
ISO_INSTANT | ‘2018-12-03T11:15:30Z’ |
RFC_1123_DATE_TIME | ‘Tue, 3 Jun 2018 11:05:30 GMT’ |
例如,我们正在提供一个示例,以便使用预定义格式化器ISO_WEEK_DATE
来打印和解析本地日期。
找到代码。
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class DateTimeFormatterDemo {
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ISO_WEEK_DATE;
String dateTime = formatter.format(LocalDate.now());
System.out.println(dateTime); //2018-W51-5
LocalDate ld = LocalDate.parse("2018-W40-4", formatter);
System.out.println(ld); //2018-10-04
}
}
输出
2018-W51-5
2018-10-04
找到格式化日期时间的模式字母(letters
)和符号(symbols
)。
Symbol | Description | Example |
---|---|---|
G | era | AD; Anno Domini; A |
u | year | 2018; 18 |
y | year-of-era | 2018; 18 |
D | day-of-year | 180 |
M/L | month-of-year | 7; 07; Jul; July; J |
d | day-of-month | 11 |
g | modified-julian-day | 2451334 |
Q/q | quarter-of-year | 3; 03; Q3; 3rd quarter |
Y | week-based-year | 1999; 99 |
w | week-of-week-based-year | 25 |
W | week-of-month | 3 |
E | day-of-week | Tue; Tuesday; T |
e/c | localized day-of-week | 2; 02; Tue; Tuesday; T |
F | day-of-week-in-month | 2 |
a | am-pm-of-day | AM |
h | clock-hour-of-am-pm (1-12) | 12 |
K | hour-of-am-pm (0-11) | 0 |
k | clock-hour-of-day (1-24) | 24 |
H | hour-of-day (0-23) | 0 |
m | minute-of-hour | 35 |
s | second-of-minute | 50 |
S | fraction-of-second | 970 |
A | milli-of-day | 1234 |
n | nano-of-second | 987654321 |
N | nano-of-day | 1234000000 |
V | time-zone ID | America/Los_Angeles; Z; -08:30 |
v | generic time-zone name | Pacific Time; PT |
z | time-zone name | Pacific Standard Time; PST |
O | localized zone-offset | GMT+8; GMT+08:00; UTC-08:00 |
X | zone-offset ‘Z’ for zero | Z; -08; -0830; -08:30; -083015; -08:30:15 |
x | zone-offset | +0000; -08; -0830; -08:30; -083015; -08:30:15 |
Z | zone-offset | +0000; -0800; -08:00 |
p | pad next | 1 |
' | escape for text | |
'' | single quote | ' |
[ | optional section start | |
] | optional section end |