在Java中验证时间戳格式yyyy-MM-dd'T'HH:mm:ssZ?

6

我正在尝试使用 joda time-1.6.2 进行 时间戳 验证。请指出我的错误并帮助我解决。

代码
String timestamp = "2014-09-23T23:03:11Z";
String datePattern = "yyyy-MM-dd'T'HH:mm:ssZ";

try {
             DateTimeFormatter dateFormatter = DateTimeFormat.forPattern(datePattern);
             dateFormatter.parseDateTime(timestamp);

        } catch (Exception e) {
            LOG.info("Timestamp is invalid format" + e);
        }

异常

INFO: Timestamp is invalid formatjava.lang.IllegalArgumentException: Invalid format: "2014-09-23T23:03:11Z" is malformed at "Z"

1
Z在时间戳中应该是什么?它是一个常量吗? - Jens
我不能使用这种格式或模式与Z吗? - Amila Iddamalgoda
1
@AmilaIddamalgoda 为什么 T 就像 'T'?但在你的时间戳中是 T? - Kick Buttowski
1
@KickButtowski 这就是在模式中指定字面字符 T,而不是指示格式化程序字段类型的方法。 - William Price
1
您可以直接将符合ISO 8601标准的字符串传递给DateTime构造函数或DateTime.parse,无需使用格式化程序。 - Basil Bourque
中间有个T,结尾是Z? - Pete Alvin
3个回答

10
我很怀疑将Z视为文字的做法。字符Z具有意义,即零偏移量。Joda-Time版本1.6的文档对此代码的解释如下:
String timestamp = "2014-09-23T23:03:11Z";
DateTime dt = 
  ISODateTimeFormat.dateTimeNoMillis().parseDateTime(timestamp).withZone(DateTimeZone.UTC);
System.out.println(dt); // 2014-09-23T23:03:11.000Z

返回一个格式化程序,将完整的日期和时间组合在一起,不包含毫秒,由'T'分隔 (yyyy-MM-dd'T'HH:mm:ssZZ)。时区偏移量是'Z'代表零时区,非零时使用'±HH:mm'的形式表示。 现在让我们详细查看以下四种替代方案(明确测试版本为1.6.2):
String timestamp = "2014-09-23T23:03:11Z";
DateTimeZone utc = DateTimeZone.UTC;

DateTime dt1 = ISODateTimeFormat.dateTimeNoMillis().parseDateTime(timestamp).withZone(utc);
System.out.println(dt1); // 2014-09-23T23:03:11.000Z (OK)

DateTime dt2 = new DateTime(timestamp, utc);
System.out.println(dt2); // 2014-09-23T23:03:11.000Z (OK)

DateTime dt3 =
  DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'").parseDateTime(timestamp).withZone(utc);
System.out.println(dt3); //2014-09-23T21:03:11.000Z (WRONG!!!)

DateTime dt4 =
  DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZZ").parseDateTime(timestamp).withZone(utc);
// exception: Invalid format: "2014-09-23T23:03:11Z" is malformed at "Z"
结论:到目前为止,其他将Z视为字面量的答案都是错误的,因为输入是在本地时区处理的,而不是使用偏移UTC+00:00。请使用构造函数或特定类(我更喜欢后者的清晰度)。

关于异常:这是一个已解决的错误,在2.0版本中得到了解决,请参见release-notes。您最好更新库版本。

允许在格式模式中使用'Z'和'ZZ'来将'Z'解析为'+00:00' [2827359]


+1 因为将 'Z' 视为文字是不好的,因为它不会以这种方式解释为时区。我假设读者会认识到这一点,但我已经更新了我的答案。 - William Price
+1 谢谢指出,我也更新了我的答案!你的答案似乎是最完整的解释。 - Levite
@Meno Hochschild,只是一个问题,这是一个格式错误吗?2015-11-17T00:00:00+0000Z,同时有+0000和Z是可能的吗? - Zilev av
1
@Zilevav 这是双重信息(冗余),需要两个模式符号进行解析(必须不同 - 类似于 SimpleDateFormat 中的“zXXX”,“X” 不受 Joda-Time 支持)。这不是一个好主意。更糟糕的是,如果输入中的偏移量不明确怎么办(例如“...+01:00Z”)?SimpleDateFormat 只使用第一个偏移量“+01”,忽略第二个(Z =“00:00”),即使在严格模式下也是如此,我已经测试过了。 - Meno Hochschild

7
v1.6 API文档中可以得知:

'Z'输出没有冒号的偏移量,'ZZ'输出带有冒号的偏移量,'ZZZ'或更多输出区域ID。

当您在模式中指定Z(不使用单引号)时,您的时间戳值必须采用+HHMM-HHMM格式作为与UTC的数值偏移量。 对于指定的格式,字面字符Z不是有效输入。

例如:

  • 2014-09-23T23:03:11+0000
  • 2014-09-23T23:03:11-0500
  • 2014-09-23T23:03:11+0430

正如Levit在其他答案中提到的那样,如果目标是接受输入时间戳中的文本'Z'而不将其作为时区处理(不好的想法),则可以在模式中使用单引号括起来的‘Z’字符(...'Z')。 这类似于分隔日期组件和时间组件的字面意思是'T'的处理方式。不建议将输入中的Z作为字面值处理,因为它有特定含义,如果提供,则时区是时间戳的重要组成部分。


+1 这也很好地解释了 Z 字符的含义! - Levite

-3

为了不仅获得有效的时间戳格式,而且还要与协调世界时(UTC)保持零偏移,请使用

String timestamp = "2014-09-23T23:03:11Z";
DateTime dt = new DateTime(timestamp, DateTimeZone.UTC);

其他注意事项 / 陷阱

如果没有明确将时间戳指定为UTC,可能会默认为本地时间的零偏移量。此外,虽然以下可能是一个有效的模式,但它有些误导。

String timestamp = "2014-09-23T23:03:11Z";
String datePattern = "yyyy-MM-dd'T'HH:mm:ss'Z'";

正如Meno所描述的那样,这将零时区偏移视为文字(因此忽略它)。

还考虑到Menno Hochschild的答案更详细地解释了这一点,因为我现在不允许删除我的答案(已被接受的答案)。


2
错误的答案。请查看Meno Hochschild的答案获取详细信息。 - Basil Bourque

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