自Java 几乎 开始以来,Java开发人员就通过java.util.Date类(自JDK 1.0起)和java.util.Calendar类(自JDK 1.1起 )来处理日期和时间。 在这段时间内,成千上万(甚至可能数百万)的Java开发人员已使用java.text.DateFormat和java.text.SimpleDateFormat格式化并解析了Java日期和时间。 鉴于多年来这样做的频率,不足为奇的是,有很多关于这些类的日期和时间的解析和格式设置的 在线 示例和教程 。 经典的Java教程在“ 格式化”课程 ( Dates and Times )中介绍了这些java.util和java.text类。 Java教程中新的Date Time轨迹涵盖了Java 8中有关日期和时间的新类,以及它们的格式和解析 。 这篇文章提供了一些实际的例子。
在通过示例演示Java 8样式的日期/时间解析/格式化之前,先比较一下DateFormat / SimpleDateFormat和DateTimeFormatter的Javadoc描述。 下表包含可从Javadoc的比较中直接或间接搜集到的每种区分格式信息的区分信息。 从此表中可能最重要的观察结果是,新的DateTimeFormatter
是线程安全的且不可变的,并且DateTimeFormatter
提供了用于解析和格式化日期和时间的API的概述。
本文的其余部分将使用示例来演示如何使用java.time构造在Java 8中格式化和解析日期。 这些示例将使用以下字符串模式和实例。
/** Pattern to use for String representation of Dates/Times. */
private final String dateTimeFormatPattern = "yyyy/MM/dd HH:mm:ss z";
/**
* java.util.Date instance representing now that can
* be formatted using SimpleDateFormat based on my
* dateTimeFormatPattern field.
*/
private final Date now = new Date();
/**
* java.time.ZonedDateTime instance representing now that can
* be formatted using DateTimeFormatter based on my
* dateTimeFormatPattern field.
*
* Note that ZonedDateTime needed to be used in this example
* instead of java.time.LocalDateTime or java.time.OffsetDateTime
* because there is zone information in the format provided by
* my dateTimeFormatPattern field and attempting to have
* DateTimeFormatter.format(TemporalAccessor) instantiated
* with a format pattern that includes time zone details
* will lead to DateTimeException for instances of
* TemporalAccessor that do not have time zone information
* (such as LocalDateTime and OffsetDateTime).
*/
private final ZonedDateTime now8 = ZonedDateTime.now();
/**
* String that can be used by both SimpleDateFormat and
* DateTimeFormatter to parse respective date/time instances
* from this String.
*/
private final String dateTimeString = "2014/09/03 13:59:50 MDT";
在Java 8之前,用于日期和时间的标准Java方法是通过Date和Calendar类,而用于解析和格式化日期的标准方法是通过DateFormat和SimpleDateFormat 。 下一个代码清单演示了这些经典方法。
使用SimpleDateFormat格式化和解析Java日期
/**
* Demonstrate presenting java.util.Date as String matching
* provided pattern via use of SimpleDateFormat.
*/
public void demonstrateSimpleDateFormatFormatting()
{
final DateFormat format = new SimpleDateFormat(dateTimeFormatPattern);
final String nowString = format.format(now);
out.println(
"Date '" + now + "' formatted with SimpleDateFormat and '"
+ dateTimeFormatPattern + "': " + nowString);
}
/**
* Demonstrate parsing a java.util.Date from a String
* via SimpleDateFormat.
*/
public void demonstrateSimpleDateFormatParsing()
{
final DateFormat format = new SimpleDateFormat(dateTimeFormatPattern);
try
{
final Date parsedDate = format.parse(dateTimeString);
out.println("'" + dateTimeString + "' is parsed with SimpleDateFormat as " + parsedDate);
}
// DateFormat.parse(String) throws a checked exception
catch (ParseException parseException)
{
out.println(
"ERROR: Unable to parse date/time String '"
+ dateTimeString + "' with pattern '"
+ dateTimeFormatPattern + "'.");
}
}
在Java 8中,首选日期/时间类不再位于java.util包中,并且首选日期/时间处理类现在位于java.time包中。 类似地,首选的日期/时间格式/解析类不再位于java.text包中,而是来自java.time.format包。
java.time
包提供了许多用于对日期和/或时间进行建模的类。 这些包括仅对日期建模(没有时间信息)的类,仅对时间建模(没有日期信息)的类,对日期和时间信息进行建模的类,使用时区信息的类以及不包含时区信息的类。 尽管类的特性(例如,是否支持日期或时间或时区信息)会影响可以应用的模式,但用于格式化和解析它们的方法通常是相似的。 在本文中,我将ZonedDateTime类用作示例。 选择该选项的原因是它包含日期,时间和时区信息,并允许我使用一个匹配模式,该模式涉及所有三个特征,例如Date或Calendar实例。 这样可以更轻松地比较新旧方法。
DateTimeFormatter类提供了ofPattern方法,用于基于所提供的日期/时间模式String提供DateTimeFormatter
的实例。 然后可以在该DateTimeFormatter
实例上调用一种格式方法,以获取日期和/或时间信息,格式为与提供的模式匹配的String。 下一个代码清单说明了基于提供的模式从ZonedDateTime
格式化String
这种方法。
将ZonedDateTime格式化为字符串
/**
* Demonstrate presenting ZonedDateTime as a String matching
* provided pattern via DateTimeFormatter and its
* ofPattern(String) method.
*/
public void demonstrateDateTimeFormatFormatting()
{
final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern(dateTimeFormatPattern);
final String nowString = formatter.format(now8);
out.println(
now8 + " formatted with DateTimeFormatter and '"
+ dateTimeFormatPattern + "': " + nowString);
}
根据模式从字符串中解析日期/时间类很容易完成。 有两种方法可以实现。 一种方法是将DateTimeFormatter
的实例传递给静态ZonedDateTime.parse(CharSequence,DateTimeFormatter)方法,该方法返回从提供的字符序列派生并基于提供的模式的ZonedDateTime
实例。 下一个代码清单对此进行了说明。
使用静态ZonedDateTime.parse方法从字符串解析ZonedDateTime
/**
* Demonstrate parsing ZonedDateTime from provided String
* via ZonedDateTime's parse(String, DateTimeFormatter) method.
*/
public void demonstrateDateTimeFormatParsingTemporalStaticMethod()
{
final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern(dateTimeFormatPattern);
final ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateTimeString, formatter);
out.println(
"'" + dateTimeString
+ "' is parsed with DateTimeFormatter and ZonedDateTime.parse as "
+ zonedDateTime);
}
从字符串解析ZonedDateTime
第二种方法是通过DateTimeFormatter
的parse(CharSequence,TemporalQuery <T>)方法。 在下面的代码清单中对此进行了说明,该清单也提供了演示Java 8 方法引用的用法的机会(请参见ZonedDateTime::from
)。
使用DateTimeFormatter.parse方法从字符串解析ZonedDateTime
/**
* Demonstrate parsing ZonedDateTime from String
* via DateTimeFormatter.parse(String, TemporaryQuery)
* with the Temple Query in this case being ZonedDateTime's
* from(TemporalAccessor) used as a Java 8 method reference.
*/
public void demonstrateDateTimeFormatParsingMethodReference()
{
final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern(dateTimeFormatPattern);
final ZonedDateTime zonedDateTime = formatter.parse(dateTimeString, ZonedDateTime::from);
out.println(
"'" + dateTimeString
+ "' is parsed with DateTimeFormatter and ZoneDateTime::from as "
+ zonedDateTime);
}
很少有项目能成为可以从Java 8开始的未开发项目。因此,将JDK 8之前的日期/时间类与JDK 8中引入的新日期/时间类联系起来的类很有帮助。这方面的一个示例是JDK 8的DateTimeFormatter
通过DateTimeFormatter.toFormat()方法提供JDK 8之前的抽象Format类的降序实例的功能。 下一个代码清单对此进行了演示。
从JDK 8的DateTimeFormatter访问JDK 8之前的格式
/**
* Demonstrate formatting ZonedDateTime via DateTimeFormatter,
* but using implementation of Format.
*/
public void demonstrateDateTimeFormatAndFormatFormatting()
{
final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern(dateTimeFormatPattern);
final Format format = formatter.toFormat();
final String nowString = format.format(now8);
out.println(
"ZonedDateTime " + now + " formatted with DateTimeFormatter/Format (and "
+ format.getClass().getCanonicalName() + ") and '"
+ dateTimeFormatPattern + "': " + nowString);
}
该即时与两个前JDK 8工作时类是特别重要的Date
和Calendar
结合类与JDK 8之所以引入新的日期和时间类Instant
是如此重要的是, java.util.Date
有方法从(即时)和toInstant()分别从Instant
获取Date
和从Date
获取Instant
。 因为Instant
在将Java 8之前的日期/时间处理迁移到Java 8基线非常重要,所以下一个代码清单演示了Instant
实例的格式和解析。
格式化和解析Instant实例
/**
* Demonstrate formatting and parsing an instance of Instant.
*/
public void demonstrateDateTimeFormatFormattingAndParsingInstant()
{
// Instant instances don't have timezone information
final Instant instant = now.toInstant();
final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern(
dateTimeFormatPattern).withZone(ZoneId.systemDefault());
final String formattedInstance = formatter.format(instant);
out.println(
"Instant " + instant + " formatted with DateTimeFormatter and '"
+ dateTimeFormatPattern + "' to '" + formattedInstance + "'");
final Instant instant2 =
formatter.parse(formattedInstance, ZonedDateTime::from).toInstant();
out.println(formattedInstance + " parsed back to " + instant2);
}
为了完整起见,所有上述示例均来自下一个代码清单中显示的示例类。
DateFormatDemo.java
package dustin.examples.numberformatdemo;
import static java.lang.System.out;
import java.text.DateFormat;
import java.text.Format;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
/**
* Demonstrates formatting dates as strings and parsing strings
* into dates and times using pre-Java 8 (java.text.SimpleDateFormat)
* and Java 8 (java.time.format.DateTimeFormatter) mechanisms.
*/
public class DateFormatDemo
{
/** Pattern to use for String representation of Dates/Times. */
private final String dateTimeFormatPattern = "yyyy/MM/dd HH:mm:ss z";
/**
* java.util.Date instance representing now that can
* be formatted using SimpleDateFormat based on my
* dateTimeFormatPattern field.
*/
private final Date now = new Date();
/**
* java.time.ZonedDateTime instance representing now that can
* be formatted using DateTimeFormatter based on my
* dateTimeFormatPattern field.
*
* Note that ZonedDateTime needed to be used in this example
* instead of java.time.LocalDateTime or java.time.OffsetDateTime
* because there is zone information in the format provided by
* my dateTimeFormatPattern field and attempting to have
* DateTimeFormatter.format(TemporalAccessor) instantiated
* with a format pattern that includes time zone details
* will lead to DateTimeException for instances of
* TemporalAccessor that do not have time zone information
* (such as LocalDateTime and OffsetDateTime).
*/
private final ZonedDateTime now8 = ZonedDateTime.now();
/**
* String that can be used by both SimpleDateFormat and
* DateTimeFormatter to parse respective date/time instances
* from this String.
*/
private final String dateTimeString = "2014/09/03 13:59:50 MDT";
/**
* Demonstrate presenting java.util.Date as String matching
* provided pattern via use of SimpleDateFormat.
*/
public void demonstrateSimpleDateFormatFormatting()
{
final DateFormat format = new SimpleDateFormat(dateTimeFormatPattern);
final String nowString = format.format(now);
out.println(
"Date '" + now + "' formatted with SimpleDateFormat and '"
+ dateTimeFormatPattern + "': " + nowString);
}
/**
* Demonstrate parsing a java.util.Date from a String
* via SimpleDateFormat.
*/
public void demonstrateSimpleDateFormatParsing()
{
final DateFormat format = new SimpleDateFormat(dateTimeFormatPattern);
try
{
final Date parsedDate = format.parse(dateTimeString);
out.println("'" + dateTimeString + "' is parsed with SimpleDateFormat as " + parsedDate);
}
// DateFormat.parse(String) throws a checked exception
catch (ParseException parseException)
{
out.println(
"ERROR: Unable to parse date/time String '"
+ dateTimeString + "' with pattern '"
+ dateTimeFormatPattern + "'.");
}
}
/**
* Demonstrate presenting ZonedDateTime as a String matching
* provided pattern via DateTimeFormatter and its
* ofPattern(String) method.
*/
public void demonstrateDateTimeFormatFormatting()
{
final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern(dateTimeFormatPattern);
final String nowString = formatter.format(now8);
out.println(
now8 + " formatted with DateTimeFormatter and '"
+ dateTimeFormatPattern + "': " + nowString);
}
/**
* Demonstrate parsing ZonedDateTime from provided String
* via ZonedDateTime's parse(String, DateTimeFormatter) method.
*/
public void demonstrateDateTimeFormatParsingTemporalStaticMethod()
{
final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern(dateTimeFormatPattern);
final ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateTimeString, formatter);
out.println(
"'" + dateTimeString
+ "' is parsed with DateTimeFormatter and ZonedDateTime.parse as "
+ zonedDateTime);
}
/**
* Demonstrate parsing ZonedDateTime from String
* via DateTimeFormatter.parse(String, TemporaryQuery)
* with the Temple Query in this case being ZonedDateTime's
* from(TemporalAccessor) used as a Java 8 method reference.
*/
public void demonstrateDateTimeFormatParsingMethodReference()
{
final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern(dateTimeFormatPattern);
final ZonedDateTime zonedDateTime = formatter.parse(dateTimeString, ZonedDateTime::from);
out.println(
"'" + dateTimeString
+ "' is parsed with DateTimeFormatter and ZoneDateTime::from as "
+ zonedDateTime);
}
/**
* Demonstrate formatting ZonedDateTime via DateTimeFormatter,
* but using implementation of Format.
*/
public void demonstrateDateTimeFormatAndFormatFormatting()
{
final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern(dateTimeFormatPattern);
final Format format = formatter.toFormat();
final String nowString = format.format(now8);
out.println(
"ZonedDateTime " + now + " formatted with DateTimeFormatter/Format (and "
+ format.getClass().getCanonicalName() + ") and '"
+ dateTimeFormatPattern + "': " + nowString);
}
/**
* Demonstrate formatting and parsing an instance of Instant.
*/
public void demonstrateDateTimeFormatFormattingAndParsingInstant()
{
// Instant instances don't have timezone information
final Instant instant = now.toInstant();
final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern(
dateTimeFormatPattern).withZone(ZoneId.systemDefault());
final String formattedInstance = formatter.format(instant);
out.println(
"Instant " + instant + " formatted with DateTimeFormatter and '"
+ dateTimeFormatPattern + "' to '" + formattedInstance + "'");
final Instant instant2 =
formatter.parse(formattedInstance, ZonedDateTime::from).toInstant();
out.println(formattedInstance + " parsed back to " + instant2);
}
/**
* Demonstrate java.text.SimpleDateFormat and
* java.time.format.DateTimeFormatter.
*
* @param arguments Command-line arguments; none anticipated.
*/
public static void main(final String[] arguments)
{
final DateFormatDemo demo = new DateFormatDemo();
out.print("\n1: ");
demo.demonstrateSimpleDateFormatFormatting();
out.print("\n2: ");
demo.demonstrateSimpleDateFormatParsing();
out.print("\n3: ");
demo.demonstrateDateTimeFormatFormatting();
out.print("\n4: ");
demo.demonstrateDateTimeFormatParsingTemporalStaticMethod();
out.print("\n5: ");
demo.demonstrateDateTimeFormatParsingMethodReference();
out.print("\n6: ");
demo.demonstrateDateTimeFormatAndFormatFormatting();
out.print("\n7: ");
demo.demonstrateDateTimeFormatFormattingAndParsingInstant();
}
}
下一个屏幕快照中显示了运行上述演示的输出。
结论
与JDK 8之前的版本相比,JDK 8日期/时间类以及相关的格式和解析类更易于使用。 这篇文章试图演示如何应用这些新类并利用它们的某些优点。
翻译自: https://www.javacodegeeks.com/2014/09/datetime-formattingparsing-java-8-style.html