【问题标题】:How to check if a localized date time is valid in Java or JS如何检查本地化日期时间在 Java 或 JS 中是否有效
【发布时间】:2017-04-24 09:57:54
【问题描述】:

我相信应该有一个库来检查这一点。我想知道是否有一个用于 java 或 javascript 的。

例如,如果时区位于美国/洛杉矶,由于夏令时,2016/03/13/02:20am 将不是有效的日期时间。

【问题讨论】:

  • 是的,它是有效的。 ;-) 但是你的错误加强了你的问题。手动预测这样的东西很容易出错。计算机很擅长。

标签: javascript java timezone dst


【解决方案1】:

Java

使用DateTimeFormatter 对象解析字符串以获得ZonedDateTime。如果无法解析字符串,则会抛出 DateTimeParseException。捕获该异常。

您可以通过调用DateTimeFormatter.ofLocalized… 方法之一来获取本地化格式化程序。

这已经在 Stack Overflow 上讨论过很多次了。所以搜索代码示例和更多讨论。

您的示例数据不正确。 America/Los_Angeles 的 DST 切换确实是 2016-03-13,但这是从凌晨 2 点跳到凌晨 3 点的“提前”调整。因此,凌晨 1:20 的时间不是问题。您想要凌晨 2 点的 2016-03-13T02:20:00 示例时刻。但即使这是由 java.time 处理的。在这种情况下应用时区的行为是提前一个小时。阅读文档并确保您了解其行为。

String input = "2016-03-13T02:20:00" ;
LocalDateTime ldt = LocalDateTime.parse( input );
ZoneId z = ZoneId.of( "America/Los_Angeles" );
ZonedDateTime zdt = ldt.atZone( z );
System.out.println( "input: " + input + " | " + "zdt: " + zdt );

输入:2016-03-13T02:20:00 | zdt: 2016-03-13T03:20-07:00[美国/洛杉矶]

见live code in IdeOne.com。

要更改此行为,请使用 ResolverStyle 和 DateTimeFormatter。已经在 Stack Overflow 上讨论过;搜索代码示例和更多信息。

ISO 8601

重要提示 #1:不要使用本地化字符串交换日期时间数据。使用ISO 8601 格式。该标准就是为此目的而发明的。 java.time 类在解析/生成字符串时默认使用这些格式。

重要提示 #2: 尽可能坚持使用 UTC 值,而不是分区。


关于java.time

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

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

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

从哪里获得 java.time 类?

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

【讨论】:

    【解决方案2】:

    Java 答案:

    当地的时间戳“2016/03/13/01:20am”在洛杉矶确实有效,但一小时后我们的标准时间和夏令时间有一个差距:

    String input = "2016/03/13/02:20am";
    ZoneId pst = ZoneId.of("America/Los_Angeles");
    
    DateTimeFormatter fmtr =
      new DateTimeFormatterBuilder()
        .appendPattern("uuuu/MM/dd/hh:mm")
        .parseCaseInsensitive()
        .appendText(ChronoField.AMPM_OF_DAY)
        .toFormatter()
        .withLocale(Locale.ENGLISH);
    LocalDateTime ldt = LocalDateTime.parse(input, fmtr);
    
    System.out.println(ldt); // 2016-03-13T02:20
    System.out.println(ldt.atZone(pst)); // 2016-03-13T03:20-07:00[America/Los_Angeles]
    System.out.println(
        "Valid local timestamp: " + !pst.getRules().getValidOffsets(ldt).isEmpty()); // false
    

    因此我们看到Java-8 中内置的新java.time-package 提供了an API 如何确定任何时区中本地时间戳的有效性。还应注意,所提出的解决方案确实不需要任何异常捕获方式。

    当将无效的本地时间戳转换为瞬间/时刻时,仅使用一种固定的转换策略,即将本地时间戳向前推进冬夏时间之间的差距大小,参见上面的转换为对象的示例输入ZonedDateTime。


    旁注:如果人们不在 Java-8 上,或者如果他们正在寻找更优雅的替代方案,那么使用我的库 Time4J 可能是一个替代方案:

    String input = "2016/03/13/02:20am";
    
    PlainTimestamp tsp =
        ChronoFormatter.ofTimestampPattern(
            "uuuu/MM/dd/hh:mma",
            PatternType.CLDR,
            Locale.ENGLISH
        ).parse(input);
    
    System.out.println(tsp); // 2016-03-13T02:20
    System.out.println("Valid local timestamp: " + tsp.isValid(AMERICA.LOS_ANGELES)); // false
    

    将本地时间戳转换为瞬间/时刻时,转换策略是可配置的(与标准 Java 相比):

        // simulation of JDK-behaviour: 2016-03-13T10:20:00Z (= 2016-03-13T03:20:00-07:00)
        System.out.println(tsp.in(Timezone.of(AMERICA.LOS_ANGELES)));
    
        // jump to next valid time: 2016-03-13T10:00:00Z (= 2016-03-13T03:00:00-07:00)
        System.out.println(
            tsp.in(
                Timezone.of(AMERICA.LOS_ANGELES).with(
                    GapResolver.NEXT_VALID_TIME.and(OverlapResolver.EARLIER_OFFSET)
                )
            )
        );
    
        // throws an exception for invalid timestamp (similar to Joda-Time-library)
        // => Invalid local timestamp due to timezone transition: 
        // => local-date=2016-03-13, local-time=T02:20 [LOS_ANGELES]
        tsp.in(
            Timezone.of(AMERICA.LOS_ANGELES).with(
                GapResolver.ABORT.and(OverlapResolver.EARLIER_OFFSET)
            )
        );
    

    脚注:如上所述,如果尝试将无效的 LocalDateTime 转换为 DateTime,流行的库 Joda-Time 总是抛出异常。此效果可用于确定本地日期时间是否有效(通过异常捕获)。

    JavaScript 答案:

    我不是 JavaScript 专家,但我通过 Google 在库 MomentJS 中找到了这个函数。引用:

    DST 是否从 2.3.0 转移,已弃用 2.14.0

    moment('2013-03-10 2:30', 'YYYY-MM-DD HH:mm').isDSTShifted()

    注意:从 2.14.0 版开始,此功能已弃用。它没有 修改矩对象后给出正确答案。更多 信息参考moment/3160

    另一个重要的验证是知道日期是否已经过 由 DST 移动。例如,在美国大部分地区: moment('2013-03-10 2:30', 'YYYY-MM-DD HH:mm').format(); //=> '2013-03-10T01:30:00-05:00'

    这是因为夏令时将时间从 2:00 转移到 3:00,所以 2:30 不是真正的时间。结果时间是 取决于浏览器,可以向前或向后调整时间。采用 moment#isDSTShifted 来测试这种情况。

    注意:在 2.3.0 之前,此条件下的 Moment 对象总是返回 暂时为假#isValid;他们现在返回 true。

    JavaScript 专家可能会找到更好的解决方案,请在此处展示。

    【讨论】:

    • 在我的回答中扩展了 JS 部分。谢谢! :)
    【解决方案3】:

    只是为 JavaScript 添加一个答案 - Meno 在 Moment.js 上正确选择。我会补充一些细节。

    不幸的是,如果日期/时间落入间隙中,我们不能简单地询问时刻,因为引擎盖下的 JS Date 对象在转换方面做了一些时髦的事情,这些事情不在规范中并且在不同环境中不一致。 Moment 曾经有一个函数isDSTShifted,但正因为如此,它已被弃用。最终,一个人真的需要原始输入作为比较的基础来判断时间是否已经改变,并且由于我们可以操纵一个时刻(加、减、设置等),我们没有办法提供一个更好的实施。

    但是 - 由于您确实有特定的输入,因此您可以通过将输出与相同格式下的输入进行比较来自己执行此操作,看看它是否仍然相同。

    var s = "2016/03/13/02:20am";
    var f = "YYYY/MM/DD/hh:mma";
    var shifted = moment(s, f).format(f) !== s;
    

    如果您想使用命名时区,那么您还需要使用 moment-timezone 插件。

    var s = "2016/03/13/02:20am";
    var f = "YYYY/MM/DD/hh:mma"
    var z = "America/Los_Angeles";
    var shifted = moment.tz(s, f, z).format(f) !== s;
    

    【讨论】:

      猜你喜欢
      • 2011-08-13
      • 2016-10-24
      • 1970-01-01
      • 1970-01-01
      • 2012-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-28
      相关资源
      最近更新 更多