如何检查ISO日期字符串中是否存在偏移量

3

给定一个类似于 ISO 格式的字符串

String dateTime = "2016-07-11T16:50:22.00+05:00";

有没有办法使用joda查找特定字符串中是否存在偏移量?

这是我迄今为止所做的代码,用于获取偏移量(如果存在)

public static String getDateTimeWithTimeZoneOffset(String dateTimeString)
    {
        DateTimeFormatter df = ISODateTimeFormat.dateTimeParser().withOffsetParsed();
        DateTime nDt = df.parseDateTime(dateTimeString);
        DateTimeFormatter dateFormatterWithoutMillis = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss");
        return dateFormatterWithoutMillis.print(nDt) + SPACE + nDt.getZone();
    }

上面的代码输出如下:

2016-07-11T16:50:22 +05:00

但是当我有一个没有偏移量的字符串,像下面这样:

2016-07-11T16:50:22

相同的代码将采用默认时区并打印如下:

2016-07-11T16:50:22 America/Chicago

是否有办法检查字符串中是否存在偏移量,如果没有则抛出异常?


使用正则表达式吗? - PM 77-1
1
你可以先检查长度...最小长度偏移量为1个字符(“Z”),因此假设其余部分始终为yyyy-MM-ddTHH:mm:ss,则可以确定是否肯定没有偏移。 - Jon Skeet
@striker Jon Skeet已经掌握了这个技巧。这是最便宜的方法,无需解析。请注意,偏移量可以是Z-05-05:00。顺便说一下,America/Chicago的偏移量是负数而不是正数。你的输出/代码有些可疑。 - Michael-O
当然,这听起来很合理,但在我的情况下,字符串也可能有毫秒。因此,格式可以是带有毫秒的 --> "2016-07-11T16:50:22.000" 或者没有毫秒的 --> "2016-07-11T16:50:22",基本上时间部分可能会改变。我最初编写了一个计算逻辑,通过在“T”字符处拆分字符串并从字符串的第二部分即时间部分找出“-”或“+”是否存在来找到字符串的偏移量,但我想知道是否有其他方法来实现这一点。 - Gowrav
3个回答

4

java.time

Joda-Time开发团队建议迁移至java.time类:

Joda-Time是Java SE 8之前的事实标准日期和时间库。现在要求用户迁移到java.time(JSR-310)。

这个java.time框架内置于Java 8及更高版本中。这些类取代了旧的麻烦日期时间类,如java.util.Date以及广受欢迎的第三方Joda-Time库。请参见Oracle教程。大部分java.time功能已经在ThreeTen-Backport中向Java 6和7进行了后移植,并在ThreeTenABP中进一步适用于Android。

OffsetDateTime

OffsetDateTime类表示带有指定UTC偏移量的时间线上的一个瞬间。该偏移量由ZoneOffset类表示。

LocalDateTime

如果您的输入字符串缺少任何偏移量或时区信息,则被视为“本地”日期时间。这意味着它不是时间线上的特定时刻,而是关于可能时刻的粗略概念。在应用偏移量或时区之前没有实际意义。

LocalDateTime类处理此类值。

ISO 8601

您的输入字符串恰好符合 ISO 8601 日期时间文本格式标准。

当解析/生成表示日期时间值的字符串时,java.time类默认使用这些标准格式。因此,您可以直接解析输入字符串而无需定义格式模式。

示例代码

这里的策略是尝试解析输入字符串是否包含偏移量。如果没有,则抛出异常(DateTimeParseException)。在这种情况下,我们再次尝试解析,但作为 LocalDateTime 值。如果第二次尝试引发解析异常,则输入完全不符合预期。

一些编码原则主义者可能会反对使用嵌套异常测试。虽然我理解他们的担忧,因为这种方法可能被滥用,但在这种特定类型的情况下,我认为嵌套异常测试是可以接受、逻辑清晰的。

String input = "2016-07-11T16:50:22.00"; // "2016-07-11T16:50:22.00+05:00";
Boolean hasOffset = null;
try {
    OffsetDateTime odt = OffsetDateTime.parse ( input );
    hasOffset = Boolean.TRUE;
    ZoneOffset offset = odt.getOffset ();
    System.out.println ( "input: " + input + " | hasOffset: " + hasOffset + " | odt: " + odt + " | offset: " + offset );
} catch ( java.time.format.DateTimeParseException e1 ) {
    // Perhaps input lacks offset-from-UTC. Try parsing as a local date-time.
    try {
        LocalDateTime ldt = LocalDateTime.parse ( input );
        hasOffset = Boolean.FALSE;
        System.out.println ( "input: " + input + " | hasOffset: " + hasOffset + " | ldt: " + ldt );
    } catch ( java.time.format.DateTimeParseException e2 ) {
        System.out.println ( "ERROR - Unexpected format in the input string" ); // FIXME: Handle format exception.
    }
}

当使用2016-07-11T16:50:22.00+05:00运行时。

输入:2016-07-11T16:50:22.00+05:00 | hasOffset: true | odt: 2016-07-11T16:50:22+05:00 | offset: +05:00

当使用2016-07-11T16:50:22.00运行时。

输入:2016-07-11T16:50:22.00 | hasOffset: false | ldt: 2016-07-11T16:50:22

长度测试

当然,您可以始终测试输入字符串的长度。根据您的示例输入,具有偏移量的输入将比没有偏移量的输入更长。如果您有多种输入,则此类长度测试可能不稳定或容易出错。

您代码的问题在于捕获第一个 DateTimeParseException 是正常程序流程的一部分。如果我们期望没有时区的字符串,我们应该检查并防止出现异常。 - dermoritz
@dermoritz 捕获第一个异常来检测给定格式有什么问题吗?复制所有日期时间格式化代码到外部测试给定输入是否真的有意义吗?这似乎是对避免过度使用异常来指导程序控制流的一般规则的疯狂误用。通常情况下,不要通过异常驱动程序是一个好的规则,但是使用已经编写和测试的代码来检查输入是否符合一系列预期格式,并使用异常表示通过/失败,这对我来说是完全合理的。 - Basil Bourque
这并不是你的错,而是API的问题(应该有一种方法来提供默认的时区)。异常的问题在于它们会对性能产生巨大的影响(Joshua Bloch: Effective Java,Item 57)。我按照你的方式实现了它 - 在搜索问题时我找到了这篇文章。我会坚持使用这个代码,直到我找到更好的方法 - 复制代码不是更好的方法。 - dermoritz

1
你可以像这样做:
if(dateTimeString.matches(".*(\\+|-)\\d{2}(:?\\d{2})?$")){
    // has offset.
}

这里是一个示例。


字符串 Z 在日期末尾还包含时区信息,即UTC。这里没有处理。 - N Sarj

1

Try this:

String dateWithoutMillis = dateFormatterWithoutMillis.print(nDt) + SPACE + nDt.getZone();
if (dateWithoutMillis.contains("/") {
  throw new InvalidDateStringException("NO OFFSET");
} else {
  return dateWithoutMillis
}

不要忘记添加异常:
public static class InvalidDateStringException extends Exception {

    public InvalidDateStringException(String message){
        super(message);
    }

}

如果您的解决方案是在一个字符串日期“2016-07-11T16:50:22”上而该时间来自UTC时区,但该字符串没有附加时区,那么会发生什么情况呢?我认为它不会正确捕获。@hulk - Gowrav

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