如何将UTC日期/时间(字符串)解析为可读性更强的内容



我有一个日期和时间的字符串,如下所示:2011-04-15T20:08:18Z。我对日期/时间格式不太了解,但我认为,如果我错了,请纠正我,这就是UTC格式。

我的问题是:在Java中,将其解析为更正常的格式最简单的方法是什么?

tl;dr

String output = 
    Instant.parse ( "2011-04-15T20:08:18Z" )
           .atZone ( ZoneId.of ( "America/Montreal" ) )
           .format ( 
               DateTimeFormatter.ofLocalizedDateTime ( FormatStyle.FULL )
                                .withLocale ( Locale.CANADA_FRENCH ) 
           )
;

vendredi 2011年4月15日美国东部时间8月16日

详细信息

乔什·平特的答案是正确的,但可能更简单。

java.time

在Java8及更高版本中,捆绑的Java.util.Date/Calendar类被JSR310定义的Java.time框架所取代。这些类的灵感来自Joda Time,但完全是重新构建的。

java.time框架是JodaTime的正式继承者。Joda Time的创建者建议我们应该在方便的时候尽快迁移到java.Time。Joda Time继续更新和调整,但进一步的创新将只在java.Time及其在ThreeTen Extra项目中的扩展中进行。

大部分java.time功能已经被后移植到Java6&7在ThreeTen Backport项目中,并在ThreeTenABP项目中进一步适应了Android。

上面Joda Time代码的等效代码非常相似。概念是相似的。与JodaTime一样,java.Time类在解析/生成日期-时间值的文本表示时默认使用ISO8601格式。

Instant是UTC时间线上的一个时刻,分辨率为纳秒(而Joda Time&java.util.Date使用的是毫秒)。

Instant instant = Instant.parse( "2011-04-15T20:08:18Z" );

应用时区(ZoneId)可获得ZonedDateTime

ZoneId zoneId = ZoneId.of( "Asia/Kolkata" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

调整到另一个时区。

ZoneId zoneId_NewYork = ZoneId.of( "America/New_York" );
ZonedDateTime zdt_NewYork = zdt.withZoneSameInstant( zoneId_NewYork );

要创建toString方法之外的其他格式的字符串,请使用java.time.format类。您可以指定自己的格式化模式,也可以让java.time自动本地化。为(a)用于翻译月/周名称的人类语言,以及(b)句号与逗号、部分顺序等的文化规范指定Locale

DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL );
formatter = formatter.withLocale( Locale.US );
String output = zdt_NewYork.format( formatter );

2011年4月15日,星期五,美国东部时间下午4:08:18


关于java.time

java.time框架构建在Java8及更高版本中。这些类取代了诸如java.util.DateCalendar和&SimpleDateFormat

现在处于维护模式的JodaTime项目建议迁移到java.Time类。

要了解更多信息,请参阅Oracle教程。并在Stack Overflow中搜索许多示例和解释。规范是JSR310。

您可以直接与数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC驱动程序。不需要字符串,也不需要java.sql.*类。

从哪里获得java.time类?

  • Java SE 8Java SE 9和更高版本
    • 内置
    • 标准Java API的一部分,带有捆绑实现
    • Java 9添加了一些小功能和修复程序
  • Java SE 6Java SE 7
    • 大部分java.time功能都是向后移植到Java6&7英寸ThreeTen背包
  • 安卓
    • java.time类的Android捆绑包实现的后续版本
    • 对于早期的Android(<26),ThreeTenABP项目适用于ThreeTen Backport(如上所述)。请参阅如何使用ThreeTenABP…

ThreeTen Extra项目通过附加类扩展java.time。这个项目是将来可能添加到java.time的试验场。您可以在这里找到一些有用的类,如IntervalYearWeekYearQuarter等等。


Joda时间

更新:Joda Time项目现在处于维护模式,团队建议迁移到java.Time类。这一部分原封不动地保留了下来。

将字符串传递给构造函数

Joda Time可以直接获取字符串。只需传递给DateTime类上的构造函数。

Joda Time了解日期时间的标准ISO 8601格式,并使用该格式作为默认格式。

示例代码

以下是JodaTime2.3中在Mac上运行Java7的示例代码。

我展示了如何通过两种方式将字符串传递给DateTime构造函数:带时区和不带时区。指定时区可以解决人们在进行日期-时间工作时遇到的许多问题。如果未指定,您将获得默认时区,该时区在投入生产时可能会带来惊喜。

我还展示了如何使用内置常量DateTimeZone.UTC指定无时区偏移(UTC/GMT)。这就是末尾的ZZulu时间的缩写)的意思:没有时区偏移(00:00)。

// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
// import org.joda.time.*;
// import org.joda.time.format.*;
// Default time zone.
DateTime dateTime = new DateTime( "2011-04-15T20:08:18Z" );
// Specified time zone.
DateTime dateTimeInKolkata = new DateTime( "2011-04-15T20:08:18Z", DateTimeZone.forID( "Asia/Kolkata" ) );
DateTime dateTimeInNewYork = new DateTime( "2011-04-15T20:08:18Z", DateTimeZone.forID( "America/New_York" ) );
// In UTC/GMT (no time zone offset).
DateTime dateTimeUtc = dateTimeInKolkata.toDateTime( DateTimeZone.UTC );
// Output in localized format.
DateTimeFormatter formatter = DateTimeFormat.shortDateTime().withLocale( Locale.US );
String output_US = formatter.print( dateTimeInNewYork );

转储到控制台…

System.out.println("dateTime: " + dateTime );
System.out.println("dateTimeInKolkata: " + dateTimeInKolkata );
System.out.println("dateTimeInNewYork: " + dateTimeInNewYork );
System.out.println("dateTimeUtc: " + dateTimeUtc );
System.out.println("dateTime in US format: " + output_US );

运行时…

dateTime: 2011-04-15T13:08:18.000-07:00
dateTimeInKolkata: 2011-04-16T01:38:18.000+05:30
dateTimeInNewYork: 2011-04-15T16:08:18.000-04:00
dateTimeUtc: 2011-04-15T20:08:18.000Z
dateTime in US format: 4/15/11 4:08 PM

使用JodaTime

我一直在使用其他解决方案,在格式末尾使用Z来获取解析错误。

相反,我选择利用JodaTime出色的解析功能,并能够非常轻松地完成以下操作:

String timestamp = "2011-04-15T20:08:18Z";
DateTime dateTime = ISODateTimeFormat.dateTimeParser().parseDateTime(timestamp);

这可以正确识别UTC时区,并允许您使用JodaTime的广泛操作方法从中获得您想要的内容。

希望这能帮助其他人。

已经有很多答案,但只想用java 8更新,以防任何人在分析字符串日期时遇到问题。

一般来说,我们在日期方面面临两个问题

  1. 将字符串分析为最新
  2. 以所需字符串格式显示日期

Java 8中的DateTimeFormatter类可以用于这两个目的。以下方法试图为这些问题提供解决方案。

方法1:将UTC字符串转换为即时。使用Instant,您可以通过提供时区字符串为任何时区创建日期,并根据需要使用DateTimeFormatter格式化显示日期。

String dateString = "2016-07-13T18:08:50.118Z";
String tz = "America/Mexico_City";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MMM d yyyy hh:mm a");
ZoneId zoneId = ZoneId.of(tz);
Instant instant = Instant.parse(dateString);
ZonedDateTime dateTimeInTz =ZonedDateTime.ofInstant(instant, zoneId);
System.out.println(dateTimeInTz.format(dtf));

方法2:

使用内置的DateTimeFormatter常量(例如ISO_INSTANT)将字符串解析为LocalDateISO_INSTANT可以解析模式的日期

yyyy-MM-dd''HH:MM:ssX例如'2011-12-03T10:15:30Z'

LocalDate parsedDate
  = LocalDate.parse(dateString, DateTimeFormatter.ISO_INSTANT);
DateTimeFormatter displayFormatter = DateTimeFormatter.ofPattern("yyyy MM dd");
System.out.println(parsedDate.format(displayFormatter));

方法3:

如果您的日期字符串具有很高的时间精度,例如,它捕获的是几秒的分数,在这种情况下2016-07-13T18:08:50.118Z,那么方法1可以工作,但方法2不工作。如果您尝试解析它,它将抛出DateTimeException,因为ISO_INSTANT格式化程序将无法解析几秒钟,正如您从其模式中看到的那样。在这种情况下,您必须通过提供如下日期模式来创建自定义DateTimeFormatter

LocalDate localDate 
= LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX"));

取自我写的博客链接。

Java 7版本的SimpleDateFormat支持使用大写字母X的ISO-8601时区。

String string = "2011-04-15T20:08:18Z";
DateFormat iso8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
Date date = iso8601.parse(string);

如果你一直使用Java 6或更早版本,那么推荐JodaTime的答案是安全的。

你必须给出以下格式:

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date parse = simpleDateFormat.parse( "2011-04-15T20:08:18Z" );

我在Andrew White解决方案中出现解析错误。在Z周围添加单引号解决了问题

DateFormat m_ISO8601Local = new SimpleDateFormat ("yyyy-MM-dd'T'HH:mm:ss'Z'");

@khmarbaise答案中的模式对我有效,这是我提取的实用方法(注意,模式字符串中省略了Z):

/**
 * Converts an ISO-8601 formatted UTC timestamp.
 *
 * @return The parsed {@link Date}, or null.
 */
@Nullable
public static Date fromIsoUtcString(String isoUtcString) {
    DateFormat isoUtcFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
    isoUtcFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
    try {
        return isoUtcFormat.parse(isoUtcString);
    } catch (ParseException e) {
        e.printStackTrace();
        return null;
    }
}

对于JDK的所有旧版本(6个版本),它可能很有用。

去掉尾随的"Z",用"UTC"时区显示名称替换它,然后使用合适的简单日期格式器解析整个字符串。

String timeZuluVal = "2011-04-15T20:08:18Z";
timeZuluVal = timeZuluVal.substring( 0, timeZuluVal.length() - 2 ); // strip 'Z';
timeZuluVal += " " + TimeZone.getTimeZone( "UTC" ).getDisplayName();
DateFormat simpleDateFormat = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss zzzz" );
Date dateVal = simpleDateFormat.parse( timeZuluVal );

Joda时间

public static final String SERVER_TIME_FORMAT = "yyyy-MM-dd  HH:mm:ss";
public static DateTime getDateTimeFromUTC(String time) {
    try {
        DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(SERVER_TIME_FORMAT).withZoneUTC();
        Calendar localTime = Calendar.getInstance();
        DateTimeZone currentTimeZone = DateTimeZone.forTimeZone(localTime.getTimeZone());
        return dateTimeFormatter.parseDateTime(time).toDateTime().withZone(currentTimeZone);
    } catch (Exception e) {
        return DateTime.now();
    }
}

最新更新