在Java中输出RFC 3339时间戳

47

我想输出一个带有PST偏移量的时间戳(例如2008-11-13T13:23:30-08:00)。java.util.SimpleDateFormat似乎不会以小时:分钟格式输出时区偏移量,它会省略冒号。在Java中,是否有简单的方法可以获得该时间戳?

// I want 2008-11-13T12:23:30-08:00
String timestamp = new SimpleDateFormat("yyyy-MM-dd'T'h:m:ssZ").format(new Date());
System.out.println(timestamp); 
// prints "2008-11-13T12:23:30-0800" See the difference?

此外,SimpleDateFormat 无法正确解析上面的示例。它会抛出 ParseException 异常。
// Throws a ParseException
new SimpleDateFormat("yyyy-MM-dd'T'h:m:ssZ").parse("2008-11-13T13:23:30-08:00")

1
对于这个问题的新读者,我建议不要使用SimpleDateFormatDate。那些类设计得很差,而且早已过时,前者尤其麻烦。使用java.time中的OffsetDateTimeZonedDateTime,这是现代Java日期和时间API。请参见Arvind Kumar Avinash的答案 - Ole V.V.
13个回答

59

从Java 7开始,有一个X模式字符串用于ISO8601时区。对于您描述的格式,使用XXX。请参见文档

示例:

System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX")
        .format(new Date()));

结果:

2014-03-31T14:11:29+02:00

我在解析从Google Cloud APIs获取的“2017-12-12T02:01:43.924-08:00”时遇到了ParseException。 - pujan jain
1
@pujanjain 这个有毫秒,而这个模式没有包括。 - Daniel Beck
我认为,任何声称符合RFC3339标准的答案都必须按照https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3339.html#anchor14中的要求处理可选的1位毫秒。 - Marcel Stör

20

请查看Joda Time包。它可以使RFC 3339日期格式化变得更加容易。

Joda示例:

DateTime dt = new DateTime(2011,1,2,12,45,0,0, DateTimeZone.UTC);
DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
String outRfc = fmt.print(dt);

2
这个答案如果有一个例子会更有用。 - noah
2
@Biff,你可以简化你的代码示例。不需要使用格式化程序。Joda-Time自动默认为ISO 8601/RFC 3339格式。只需调用toString方法,无论是显式还是隐式都可以。像这样:String output = dt.toString(); - Basil Bourque

16
我花了很多时间寻找答案来解决相同的问题,最终在这里找到了有用的信息:http://developer.android.com/reference/java/text/SimpleDateFormat.html 建议的解决方案如下: String timestamp = new SimpleDateFormat("yyyy-MM-dd'T'h:m:ssZZZZZ").format(new Date()); 如果你注意到,我用了5个'Z'而不是1个。这会输出带有偏移量的冒号,例如:"2008-11-13T12:23:30-08:00"。希望能帮到你。

谢谢 - 您的答案被低估了 - 这最终让我能够输出RFC 3339。使用X会导致Caused by: java.lang.IllegalArgumentException: Illegal pattern component: XXX at org.apache.commons.lang.time.FastDateFormat.parsePattern(FastDateFormat.java:691) at org.apache.commons.lang.time.FastDateFormat.init(FastDateFormat.java:558) - Zasz
很高兴能帮助 :) - Rahul Ravindran

14

来自“完成部门”的一个解决方案是,在SimpleDateFormat完成后使用正则表达式修复字符串。像Perl中的s/(\d{2})(\d{2})$/$1:$2/这样。

如果您对此感兴趣,我将编辑此响应并提供可用的Java代码。

但是,是的。我也遇到了这个问题。RFC3339,我在看着你!

编辑:

这对我有效。

// As a private class member
private SimpleDateFormat rfc3339 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");

String toRFC3339(Date d)
{
   return rfc3339.format(d).replaceAll("(\\d\\d)(\\d\\d)$", "$1:$2");
}

4
问题在于Z产生的时区偏移没有冒号(:)作为分隔符。

完全正确,但没有告诉我们如何修复它。 jjohn的解决方案为我解决了问题。 - scaganoff

3
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'h:m:ss.SZ");

这不正是你所需要的吗?


不,如果您尝试解析上面给出的时间戳,它会抛出ParseException异常。 - Cristian

2
我们可以简单地使用 ZonedDateTime 类DateTimeFormatter 类 来实现这个。
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx");
ZonedDateTime z2 = ZonedDateTime.now(ZoneOffset.UTC).truncatedTo(ChronoUnit.SECONDS);
System.out.println("format =======> " + z2.format(format));

输出: 格式 =======> 2020年03月30日05时57分37秒+00:00

2

我尝试了这种格式,对我来说有效:yyyy-MM-dd'T'HH:mm:ss'Z'


1

我为RFC3339创建了一个InternetDateFormat类。

但源代码注释是日语。

PS:我创建了英文版并进行了一些重构。


1

我找到了一个有帮助的 PasteBin,解决了我的问题:http://pastebin.com/y3TCAikc

以防它的内容随后被删除:

// I want 2008-11-13T12:23:30-08:00
String timestamp = new SimpleDateFormat("yyyy-MM-dd'T'h:m:ssZ").format(new Date());
System.out.println(timestamp); 
// prints "2008-11-13T12:23:30-0800" See the difference?

// Throws a ParseException
new SimpleDateFormat("yyyy-MM-dd'T'h:m:ssZ").parse("2008-11-13T13:23:30-08:00")

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'h:m:ss.SZ");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接