【问题标题】:SimpleDateFormat producing wrong date time when parsing "YYYY-MM-dd HH:mm"SimpleDateFormat 在解析“YYYY-MM-dd HH:mm”时产生错误的日​​期时间
【发布时间】:2013-04-01 18:28:14
【问题描述】:

我正在尝试将 String (YYYY-MM-dd HH:mm) 解析为 Date,但是错误的日期比预期的要多。

代码:

Date newDate = null;
String dateTime = "2013-03-18 08:30";
SimpleDateFormat df = new SimpleDateFormat("YYYY-MM-dd HH:mm", Locale.ENGLISH);
df.setLenient(false);
try {
    newDate = df.parse(dateTime);
} catch (ParseException e) {
    throw new InvalidInputException("Invalid date input.");
}

生产:

2012 年 12 月 30 日星期日 08:30:00 EST(错误)

我尝试关闭 Lenient,但没有成功。

更新

感谢 Sudhanshu 的回答,它帮助我解决了 Java 转换。当我将上述代码返回的日期输入数据库时​​,我得到了正确的日期,但时间始终是00:00

ps.setDate(3, new java.sql.Date(app.getDate().getTime()));

【问题讨论】:

  • 对于更新,java.sql.Date 仅用于日期,用于 SQL 的日期数据类型。来自文档:“为了符合 SQL DATE 的定义,由 java.sql.Date 实例包装的毫秒值必须通过在特定时区将小时、分钟、秒和毫秒设置为零来“标准化”实例关联的对象。”

标签: java datetime simpledateformat datetime-parsing


【解决方案1】:

虽然这里不是这种情况,但请注意SimpleDateFormat 不是线程安全的。因此在多线程环境中使用时,可能会发生竞争条件并导致错误的解析结果。解决方案是

  • 在每次使用时创建一个新实例(可能会降低性能)
  • 实例化一次,但在 synchronized 块中使用它
  • 实例化一次但使用ThreadLocal

ThreadLocal 为例:

class MyClass {
  private ThreadLocal<SimpleDateFormat> threadLocalFormat = 
    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ENGLISH));

  public void foo(String bar) {
    return threadLocalFormat.get().parse(bar);
  }
}

【讨论】:

  • 嗨@Salim。我遇到了这个。我注意到 parse 方法生成了错误的输出,而不是我看到它使用实例变量来进行计算。我使用的是静态实例。
【解决方案2】:

这是现代答案。其他答案在 2013 年编写时都是很好的答案。在那之后的一年,现代日期和时间 API 出现,以替代旧类 DateSimpleDateFormat 和朋友。恕我直言,您现在应该使用新课程。它们对程序员更友好。

    LocalDateTime newDate = null;
    String dateTime = "2013-03-18 08:30";
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm", Locale.ENGLISH);
    try {
        newDate = LocalDateTime.parse(dateTime, dtf);
    } catch (DateTimeParseException e) {
        throw new InvalidInputException("Invalid date input.");
    }

除了类名之外,它与旧代码没有什么不同。与其他示例会有差异,通常现代类将提供更清晰的代码和更少的错误机会。

为了稍微演示一下区别,让我们试试你的格式模式字符串,其中包含大写的YYYY

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm", Locale.ENGLISH);

现在代码抛出java.time.format.DateTimeParseException: Text '2013-03-18 08:30' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MonthOfYear=3, DayOfMonth=18, WeekBasedYear[WeekFields[SUNDAY,1]]=2013},ISO resolved to 08:30 of type java.time.format.Parsed。很长,我知道。需要注意的是,它提到的字段中没有年份,只有 WeekBasedYear。这是不一样的;你现在可能已经知道这正是因为大写的Y 是基于周的年份(仅适用于周数),你应该使用小写的y 来表示年份。我认为这种行为比旧课程的做法更有帮助:他们给你一个错误的结果,假装一切都很好,让你完全不知道哪里出了问题。

接下来,我了解到您希望将日期时间存储到数据库中。在过去,您会转换为一些合适的java.sql 类型。不再需要。我相信使用 JDBC 4.2 驱动程序您可以做到:

    ps.setObject(3, newDate);

不过,我还没有使用数据库对此进行过测试。请注意,您使用setObject() 而不是setDate()

【讨论】:

    【解决方案3】:

    YYYY 应该是 yyyy-

    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ENGLISH);
    

    请在此处查看 SimpleDateFormat 的文档
    Java 6:http://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html
    Java 7:http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

    【讨论】:

    • 感谢您的回答。我解决了最初的问题。当我将值输入日期基准时,我得到了正确的日期,但时间总是 00:00。 ps.setDate(3, new java.sql.Date(app.getDate().getTime()));
    • 你真的拯救了我的一天
    【解决方案4】:

    有两个问题。

    1. 格式字符串应为"yyyy-MM-dd HH:mm"
    2. 存储时间的数据类型是TimeStamp,而不是数据库中的Date

    纠正这两件事,您将能够随时间存储和检索日期。

    【讨论】:

      【解决方案5】:

      使用小写字母 Y,而不是大写字母。即 yyyy 不是 YYYY

      在此处查看 cmets:Java Simple Date Format 以及此处引用的其他答案。

      【讨论】:

        猜你喜欢
        • 2018-07-16
        • 1970-01-01
        • 2015-10-08
        • 2018-11-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-19
        相关资源
        最近更新 更多