Java 8新增了一个名为java.time的API,用于处理日期和时间(JSR 310)。
我有一个字符串类型的日期和时间(例如"2014-04-08 12:30"
),如何从给定的字符串中获取一个LocalDateTime
实例?
当我完成操作LocalDateTime
对象之后:如何将LocalDateTime
实例转换回与上述相同的字符串格式的字符串?
解析日期和时间
要从字符串创建一个LocalDateTime
对象,您可以使用静态的LocalDateTime.parse()
方法。它需要一个字符串和一个DateTimeFormatter
作为参数。 DateTimeFormatter
用于指定日期/时间模式。
String str = "1986-04-08 12:30";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.parse(str, formatter);
格式化日期和时间
要从一个LocalDateTime
对象创建格式化字符串,可以使用format()
方法。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.of(1986, Month.APRIL, 8, 12, 30);
String formattedDateTime = dateTime.format(formatter); // "1986-04-08 12:30"
请注意,在DateTimeFormatter
中预定义了一些常用的日期/时间格式。例如:使用DateTimeFormatter.ISO_DATE_TIME
对上述示例中的LocalDateTime
实例进行格式化,将生成字符串"1986-04-08T12:30:00"
。LocalDate
或ZonedDateTime
)都可以使用parse()
和format()
方法。DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX")
ÒÇé - Ray Hulhaformat()
而不是在实例上调用?至少,这就是我所犯的错误:在上面的示例中,我混淆了 DateTime
和 dateTime
。 - glaedDate
到LocalDate
及其相关类的位重新编写了一遍。 - DragasLocalDate.parse()
或LocalDateTime.parse()
来解析一个ISO 8601 format格式的String
。String strDate = "2015-08-04";
LocalDate aLD = LocalDate.parse(strDate);
System.out.println("Date: " + aLD);
String strDatewithTime = "2015-08-04T10:11:30";
LocalDateTime aLDT = LocalDateTime.parse(strDatewithTime);
System.out.println("Date with Time: " + aLDT);
输出,
Date: 2015-08-04
Date with Time: 2015-08-04T10:11:30
只有在需要处理其他日期格式时,才使用DateTimeFormatter
。
例如,在以下示例中,dd MMM uuuu表示月份的日期(两位数字),月份名称的三个字母(Jan、Feb、Mar等)和四位数的年份:
DateTimeFormatter dTF = DateTimeFormatter.ofPattern("dd MMM uuuu");
String anotherDate = "04 Aug 2015";
LocalDate lds = LocalDate.parse(anotherDate, dTF);
System.out.println(anotherDate + " parses to " + lds);
输出
04 Aug 2015 parses to 2015-08-04
DateTimeFormatter
对象是双向的;它既可以解析输入,也可以格式化输出。String strDate = "2015-08-04";
LocalDate aLD = LocalDate.parse(strDate);
DateTimeFormatter dTF = DateTimeFormatter.ofPattern("dd MMM uuuu");
System.out.println(aLD + " formats as " + dTF.format(aLD));
输出
2015-08-04 formats as 04 Aug 2015
Symbol Meaning Presentation Examples
------ ------- ------------ -------
G era text AD; Anno Domini; A
u year year 2004; 04
y year-of-era year 2004; 04
D day-of-year number 189
M/L month-of-year number/text 7; 07; Jul; July; J
d day-of-month number 10
Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter
Y week-based-year year 1996; 96
w week-of-week-based-year number 27
W week-of-month number 4
E day-of-week text Tue; Tuesday; T
e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
F week-of-month number 3
a am-pm-of-day text PM
h clock-hour-of-am-pm (1-12) number 12
K hour-of-am-pm (0-11) number 0
k clock-hour-of-am-pm (1-24) number 0
H hour-of-day (0-23) number 0
m minute-of-hour number 30
s second-of-minute number 55
S fraction-of-second fraction 978
A milli-of-day number 1234
n nano-of-second number 987654321
N nano-of-day number 1234000000
V time-zone ID zone-id America/Los_Angeles; Z; -08:30
z time-zone name zone-name Pacific Standard Time; PST
O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00;
X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15;
Z zone-offset offset-Z +0000; -0800; -08:00;
p pad next pad modifier 1
' escape for text delimiter
'' single quote literal '
[ optional section start
] optional section end
# reserved for future use
{ reserved for future use
} reserved for future use
2018-08-09 12:00:08
,但是当我解析时,会添加一个我不需要的 T
。有没有什么方法可以解决这个问题? - RaghuveerSufiyan Ghori的和micha的答案都很好地解释了关于字符串模式的问题。然而,如果你正在使用ISO 8601,则无需应用DateTimeFormatter
,因为LocalDateTime已经为此做好了准备:
将LocalDateTime转换为时区ISO 8601字符串
LocalDateTime ldt = LocalDateTime.now();
ZonedDateTime zdt = ldt.atZone(ZoneOffset.UTC); // You might use a different zone
String iso8601 = zdt.toString();
String iso8601 = "2016-02-14T18:32:04.150Z";
ZonedDateTime zdt = ZonedDateTime.parse(iso8601);
LocalDateTime ldt = zdt.toLocalDateTime();
Instant
”)相当复杂。 Java已经在多个迭代中解决了这个问题。最新的迭代中,java.time
和java.time.chrono
几乎涵盖了所有需求(除了time dilation :))。LocalDateTime
、ZonedDateTime
等如此复杂?java.util.Date
,它采用了有点天真的方法,假定只有年、月、日和时间。这很快就不够用了。java.sql.Date
,具有自己的限制。Calendar
API。java.time
中处理它当您使用时间戳字符串时,需要知道它包含的信息。 这是关键点。 如果您没有正确理解,就会出现加密异常,例如“无法创建Instant”、“Zone offset missing”、“unknown zone id”等。
它是否包含日期和时间?
它是否有时间偏移量?
时间偏移量是+hh:mm部分。有时,+00:00可能会被替换为'Zulu time',UTC
作为协调世界时,或GMT作为格林威治平均时间。这些也设置了时区。
对于这些时间戳,您可以使用OffsetDateTime
。
它是否有时区?
对于这些时间戳,您可以使用ZonedDateTime
。
时区可以通过以下方式指定
时区列表由"TZ database"编制,由ICAAN支持。
根据ZoneId
的javadoc,时区id也可以以某种方式指定为Z和偏移量。我不确定这如何映射到实际区域。
如果只有TZ的时间戳落在时间偏移量更改的闰秒中,则存在歧义,并且解释取决于ResolverStyle
,请参见下文。
如果两者都没有,则假定或忽略缺失的上下文。消费者必须决定。因此,需要将其解析为LocalDateTime
并通过添加缺失的信息将其转换为OffsetDateTime
:
Duration
)或当您不知道它并且并不真正重要时(例如,当地公交车时间表)非常有用。部分时间信息
LocalDate
、LocalTime
、OffsetTime
、MonthDay
、Year
或YearMonth
。如果您拥有完整的信息,则可以获得一个java.time.Instant
。这也在内部用于在OffsetDateTime
和ZonedDateTime
之间进行转换。
有一个广泛的文档DateTimeFormatter
,它既可以解析时间戳字符串,也可以格式化为字符串。
预创建的DateTimeFormatter
应该涵盖几乎所有标准的时间戳格式。例如,ISO_INSTANT
可以解析2011-12-03T10:15:30.123457Z
。
如果您有一些特殊格式,那么您可以创建自己的DateTimeFormatter(也是解析器)。
private static final DateTimeFormatter TIMESTAMP_PARSER = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SX"))
.toFormatter();
// No need for this!
TemporalAccessor ta = TIMESTAMP_PARSER.parse("2011-... etc");
但是,有了前面部分的知识,您可以方便地解析成所需的类型:
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z", TIMESTAMP_PARSER);
你实际上不需要使用 DateTimeFormatter
。你想要解析的类型有 parse(String)
方法。
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z");
TemporalAccessor
,如果您对字符串中的信息有模糊的了解,并且想在运行时决定使用它。需要注意的另一件事是,LocalDateTime.parse
不能与只包含日期格式字符(如uuuuMMdd
)的自定义格式化程序一起使用。在这种情况下,应改用LocalDate.parse
。例如:
String s = "20210223";
// ok
LocalDate.parse(s, DateTimeFormatter.ofPattern("uuuuMMdd"));
// java.time.format.DateTimeParseException
LocalDateTime.parse(s, DateTimeFormatter.ofPattern("uuuuMMdd"));
获取当前UTC时间并以所需格式显示
// Current the UTC time
OffsetDateTime utc = OffsetDateTime.now(ZoneOffset.UTC);
// Get LocalDateTime
LocalDateTime localDateTime = utc.toLocalDateTime();
System.out.println("*************" + localDateTime);
// Formatted UTC time
DateTimeFormatter dTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
System.out.println(" formats as " + dTF.format(localDateTime));
// Get the UTC time for the current date
Date now = new Date();
LocalDateTime utcDateTimeForCurrentDateTime = Instant.ofEpochMilli(now.getTime()).atZone(ZoneId.of("UTC")).toLocalDateTime();
DateTimeFormatter dTF2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
System.out.println(" formats as " + dTF2.format(utcDateTimeForCurrentDateTime));
所有答案都不错。Java 8及以上版本有以下模式用于解析和格式化时区:V
、z
、O
、X
、x
、Z
。
以下是它们的解析规则,根据文档中的规定:
Symbol Meaning Presentation Examples
------ ------- ------------ -------
V time-zone ID zone-id America/Los_Angeles; Z; -08:30
z time-zone name zone-name Pacific Standard Time; PST
O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00;
X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15;
Z zone-offset offset-Z +0000; -0800; -08:00;
但是关于格式化呢?
以下是针对日期(假设使用ZonedDateTime
)的样例,展示不同格式化模式下的行为:
// The helper function:
static void printInPattern(ZonedDateTime dt, String pattern) {
System.out.println(pattern + ": " + dt.format(DateTimeFormatter.ofPattern(pattern)));
}
// The date:
String strDate = "2020-11-03 16:40:44 America/Los_Angeles";
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss zzzz");
ZonedDateTime dt = ZonedDateTime.parse(strDate, format);
// 2020-11-03T16:40:44-08:00[America/Los_Angeles]
// Rules:
// printInPattern(dt, "V"); // exception!
printInPattern(dt, "VV"); // America/Los_Angeles
// printInPattern(dt, "VVV"); // exception!
// printInPattern(dt, "VVVV"); // exception!
printInPattern(dt, "z"); // PST
printInPattern(dt, "zz"); // PST
printInPattern(dt, "zzz"); // PST
printInPattern(dt, "zzzz"); // Pacific Standard Time
printInPattern(dt, "O"); // GMT-8
// printInPattern(dt, "OO"); // exception!
// printInPattern(dt, "OO0"); // exception!
printInPattern(dt, "OOOO"); // GMT-08:00
printInPattern(dt, "X"); // -08
printInPattern(dt, "XX"); // -0800
printInPattern(dt, "XXX"); // -08:00
printInPattern(dt, "XXXX"); // -0800
printInPattern(dt, "XXXXX"); // -08:00
printInPattern(dt, "x"); // -08
printInPattern(dt, "xx"); // -0800
printInPattern(dt, "xxx"); // -08:00
printInPattern(dt, "xxxx"); // -0800
printInPattern(dt, "xxxxx"); // -08:00
printInPattern(dt, "Z"); // -0800
printInPattern(dt, "ZZ"); // -0800
printInPattern(dt, "ZZZ"); // -0800
printInPattern(dt, "ZZZZ"); // GMT-08:00
printInPattern(dt, "ZZZZZ"); // -08:00
+
符号字符用于所有位置(现在的位置使用 -
),并且永远不被省略。java.time
类型。如果您要为java.util.Date
或java.util.Calendar
使用这些类型,则并非所有类型都能正常工作,因为这些类型已经损坏(因此被标记为过时,请不要使用它们)。让我们来看两个问题,示例字符串"2014-04-08 12:30"
如何从给定的字符串中获取LocalDateTime实例?
import java.time.format.DateTimeFormatter
import java.time.LocalDateTime
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
// Parsing or conversion
final LocalDateTime dt = LocalDateTime.parse("2014-04-08 12:30", formatter)
dt
应该允许您进行所有日期时间相关的操作。
那么我如何将 LocalDateTime 实例转换回具有相同格式的字符串?
final String date = dt.format(formatter)
yyyy-MM-dd HH:mm:ss.SSS
yyyy-MM-dd HH:mm:ss.S
yyyy-MM-dd HH:mm:ss
yyyy-MM-dd HH:mm
yyyy-MM-dd HH
yyyy-MM-dd
public static final String DATE_FORMAT_YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd HH:mm:ss.SSS";
public LocalDateTime stringToLocalDateTime(String s){
return LocalDateTime.parse(s, DateTimeFormatter.ofPattern(DATE_FORMAT_YYYY_MM_DD_HH_MM_SS_SSS.substring(0, s.length())));
}
这个问题已经有很多好的答案了。这个答案展示了如何使用预定义的DateTimeFormatter
来构建一个可以解析给定日期时间字符串的DateTimeFormatter
。
然而,使用这个DateTimeFormatter
格式化得到的LocalDateTime
将返回一个带有HH:mm:ss
格式的时间字符串。为了将时间字符串限制为HH:mm
格式,我们仍然需要像其他答案一样使用模式uuuu-MM-dd HH:mm
。
演示:
class Main {
public static void main(String[] args) {
DateTimeFormatter dtf = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral(' ')
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.toFormatter(Locale.ENGLISH);
String strDateTime = "2014-04-08 12:30";
LocalDateTime ldt = LocalDateTime.parse(strDateTime, dtf);
System.out.println(ldt);
// However, formatting the obtained LocalDateTime using this DateTimeFormatter
// will return a string with time in HH:mm:ss format. To restrict the time
// string to HH:mm format, we still have to use the pattern, uuuu-MM-dd HH:mm as
// other answers have done.
String strDateTimeFormatted = ldt.format(dtf);
System.out.println(strDateTimeFormatted);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm", Locale.ENGLISH);
strDateTimeFormatted = ldt.format(formatter);
System.out.println(strDateTimeFormatted);
}
}
输出:
2014-04-08T12:30
2014-04-08 12:30:00
2014-04-08 12:30
注意:在这里,您可以使用y
代替u
,但是我更喜欢使用u
而不是y
。
从Trail: Date Time了解有关现代日期时间API的更多信息。
ZonedDateTime
而不是LocalDateTime
,尽管名称似乎有些反直觉;Local
一词表示一般的“任何”地方,而不是特定的时区。 因此,LocalDateTime
对象与时间轴没有关联。 要具有意义并获得时间轴上的指定时刻,必须应用一个时区。 - Basil BourqueLocalDateTime
、ZonedDateTime
、OffsetDateTime
、Instant
、LocalDate
和LocalTime
之间的区别,以及如何保持冷静并在第一次尝试时正确地处理它们。 - Ondra ŽižkaLocalDateTime
可能被命名为ZonelessOffsetlessDateTime
。 - Ondra Žižka