【问题标题】:Java String IsValid CheckingJava 字符串是否有效检查
【发布时间】:2019-03-19 20:59:33
【问题描述】:

您能否指出为什么以下示例给出了解析异常。 IDea 是找一个无效的日期,比如 2 月 30 日

    String str = "20190310022817";

    SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");
    sdf.setLenient(false);
    try {
        System.out.println(sdf.parseObject(str));
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

【问题讨论】:

标签: java date datetime


【解决方案1】:

tl;博士

LocalDateTime
.parse( 
    "20190310022817" , 
    DateTimeFormatter.ofPattern( "uuuuMMddHHmmss" )
)
.atZone(
    ZoneId.of( "America/Montreal" ) 
)

结果是凌晨 3 点而不是凌晨 2 点。针对 DST 进行调整的功能,而不是错误。

2019-03-10T03:28:17-04:00[美国/蒙特利尔]

如果您的目标是拒绝此类无效输入而不是调整它们,请参阅my Answer 另一个解释ResolverStyle 枚举的问题:LENIENTSMART,&STRICT

避免使用旧的日期时间类

您正在使用糟糕的日期时间类,这些类在几年前已被现代 java.time 类所取代。

夏令时 (DST)

正如Answer by rgettman 中所指出的,一些疯狂到可以参与Daylight Saving Time (DST) 的司法管辖区正在您给定日期 3 月 10 日凌晨 2 点进行“提前”跳跃。这种情况发生在美国和加拿大的大部分地区,也许还有其他地方。请参阅有关 DST in the United States 的维基百科。

在这些地点,没有凌晨 2 点。当时钟敲响凌晨 2 点时,时钟立即跳到凌晨 3 点。两点钟的时间从未存在过。

顺便说一下,DST 并不是时钟更改的唯一方案。世界各地的政客都表现出在他们的管辖范围内搞乱UTC偏移量的倾向。例如,参见近年来的朝鲜、委内瑞拉、土耳其和俄罗斯。现在美国的一些州正在选择停止 DST 废话,尽管有些州正在考虑永久保留 DST。关键是,为了稳健地处理日期时间,无论当前的做法如何,您都应该始终假设政治家将在未来的某个时候做出改变,并相应地进行编码。

您的输入无效(可能)

你使用的遗留类可能会拒绝你的输入,因为你的 JVM 当前默认时区中的 DST 跳转是由于你没有指明时区而隐式应用的。这种解析有效值的严格协议并没有错,只是处理无效输入的一种方法。

ZonedDateTime 类默认使用另一种方法,根据需要进行调整。

ZonedDateTime

java.time.ZonedDateTime 类知道时区过去、现在和未来对特定地区人们使用的偏移量的变化的历史。这样该类将自动调整。请务必阅读课程文档,以便了解并同意其调整协议。

LocalDateTime

这是一些示例代码。首先,我们将您的输入解析为LocalDateTime,因为您的输入缺少任何时区指示或与UTC 的偏移量。因此,LocalDateTime 确实 代表一个时刻,不是时间轴上的一个点。因此,如果已知时区是针对该日期和时间的,我们可以应用ZoneId 来获得ZonedDateTime

在此示例中,我们任意选择两个区域来应用,因此您可以看到不同的结果。在美国,3 月 10 日是夏令时跳跃的日子。在法国等欧洲大部分地区,跳跃计划在三月的最后一天晚些时候进行。所以凌晨 2 点在法国是有效时间,但在美国却不是。

String input = "20190310022817";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuuMMddHHmmss" ) ;
LocalDateTime ldt = LocalDateTime.parse( input , f ) ;

ZoneId zParis = ZoneId.of( "Europe/Paris" ) ;
ZonedDateTime zdtParis = ldt.atZone( zParis ) ;

ZoneId zNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdtNewYork = ldt.atZone( zNewYork ) ;

转储到控制台。

System.out.println( "ldt.toString(): " + ldt ) ;
System.out.println( "zdtParis.toString(): " + zdtParis ) ;
System.out.println( "zdtNewYork.toString(): " + zdtNewYork ) ;

运行时。请参阅此code run live at IdeOne.com。注意这三个输出中每一个的小时数:

  • 前两个凌晨 2 点
  • 最后一天凌晨 3 点

ldt.toString(): 2019-03-10T02:28:17

zdtParis.toString(): 2019-03-10T02:28:17+01:00[欧洲/巴黎]

zdtNewYork.toString(): 2019-03-10T03:28:17-04:00[America/New_York]


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

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

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

  • 感谢您使用 java.time,这里当然推荐。当问题提到像 2 月 30 日这样的无效日期时,我建议我们要使用 ResolverStyle.STRICT(或者如您所知,java,time 只会选择 2 月 28 日)。
  • @OleV.V.我是这么想的,但是 Question 的第一句话与此相矛盾,暗示抛出的异常是一个惊喜。
  • @OleV.V.我在此处顶部附近添加了一个链接到我的关于ResolverStyle 枚举的类似问题的答案。我认为这涵盖了现在所有的基础。谢谢!
【解决方案2】:

字符串"20190310022817" 表示 2019 年 3 月 10 日星期日 2:28:17,由于夏令时不存在,在美国于 2019 年 3 月 10 日星期日 2:00:00 生效.

这意味着 2019 年 3 月 10 日星期日 1:59:59 之后的第二个是 2019 年 3 月 10 日星期日 3:00:00,因此该日期不存在 2:28:17(在此期间也不存在任何其他秒那个小时)。

您可以调整字符串中表示的时间,也可以识别不存在这种时间间隔并适当地处理异常。

【讨论】:

    【解决方案3】:

    在欧洲,输出是Sun Mar 10 02:28:17 CET 2019,所以你应该先设置时区。

    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault())
    

    【讨论】:

    • 只是为了说清楚:答案中的代码没有设置时区。
    猜你喜欢
    • 1970-01-01
    • 2019-05-05
    • 2019-10-04
    • 2013-02-13
    • 1970-01-01
    • 2017-05-22
    • 2017-01-20
    • 1970-01-01
    • 2014-02-09
    相关资源
    最近更新 更多