Spring @DateTimeFormat 转换错误与夏令时

Posted

技术标签:

【中文标题】Spring @DateTimeFormat 转换错误与夏令时【英文标题】:Spring @DateTimeFormat conversion error with Daylight Saving Time 【发布时间】:2011-12-22 07:35:27 【问题描述】:

我在配置了夏令时(美国/圣保罗时区)的机器上使用 Spring MVC。在我的表单类中,我使用注释 DateTimeFormat 来配置我的 Date 的输出:

public class JustificativaOcorForm 
  ...
  @NotNull
  @DateTimeFormat(pattern="yyyy-MM-dd")
  private Date dataMarcacao;
  ...

在调试时,我得到的日期是 16/10/2011 (dd/MM/yyyy),这是白天时间的开始,但 Spring 将其转换为 2011 年 10 月 15 日。为什么?

2011-11-04 16:35:31,965 [http-8080-Processor25] DEBUG org.springframework.core.convert.support.GenericConversionService - Converting value Sun Oct 16 00:00:00 BRST 2011 of [TypeDescriptor @javax.validation.constraints.NotNull @org.springframework.format.annotation.DateTimeFormat java.util.Date] to [TypeDescriptor java.lang.Long]
2011-11-04 16:35:31,965 [http-8080-Processor25] DEBUG org.springframework.core.convert.support.GenericConversionService - Converted to 1318730400000
2011-11-04 16:35:32,010 [http-8080-Processor25] DEBUG org.springframework.core.convert.support.GenericConversionService - Converted to '2011-10-15'

我看到这个问题:@DateTimeFormat in Spring produces off-by-one day error

但是 Spring 3 使用 Joda-Time,我的类路径中有 joda-time-2.0.jar,所以我不知道为什么会发生这种情况以及如何解决它。

[编辑]

我已经测试过创建 LocalData 对象,并且发现了一些东西:

LocalDate ld = new LocalDate( new SimpleDateFormat("dd/MM/yyyy").parse("16/10/2011").getTime() );
System.out.println( new SimpleDateFormat("dd/MM/yyyy HH:mm:ss Z z").format( ld.toDate() )  );
//prints 15/10/2011 00:00:00 -0200 BRST

LocalDate ld2 = new LocalDate( 2011,10,16 );
System.out.println( new SimpleDateFormat("dd/MM/yyyy HH:mm:ss Z z").format( ld2.toDate() )  );
//prints 16/10/2011 00:00:00 -0200 BRST

似乎第一种方法是认为时间是UTC,因为调试我可以看到Joda使用了DateTimeZone类的convertUTCToLocal方法。

也许这也是 Spring 的默认设置,他希望也有一个 UTC 日期,而我正在通过 BRT 日期。

所以我认为我的解决方案是将对象更改为 LocalDate 并使用第二种方式创建此对象的实例。

【问题讨论】:

不清楚 - 你是在转换 to 字符串,还是 from 字符串?另外,如果这只是一个日期,最好使用 Joda 的 LocalDate 类型。 @JonSkeet 这是字符串的日期。我可以用 @DateTimeFormat 设置 LocalDate 吗? 我当然希望如此。请注意,您的 Date 值似乎是 10 月 16 日午夜 BRST ......这是故意的吗?诚然,将其格式化为 10 月 15 日很奇怪......但如果您使用日期,通常最好完全避开时区:) @JonSkeet 我认为值是 BRST 因为服务器的时区。有趣的是,使用 SimpleDateFormat 我没有这个问题。另外,在 Spring MVC 上下文中配置 java 以避免时区的最佳方法是什么? 不幸的是,虽然我在时区上很舒服,但我根本没有使用过 Spring MVC :( 【参考方案1】:

这可能会回答您的部分问题。

当您有一个 java.util.Data 对象时,该对象将在 toString 内的系统时区打印。因此,如果您在 UTC 2011-10-16 00:00:00 中设置一个日期,该日期将在日期内部转换为 UTC 时间戳。 toString 将在您的本地时区打印该时间戳,这将是 UTC 之后的几个小时(因为圣保罗在伦敦以西),因此大约是 2011 年 10 月 15 日 22:00:00。这就是您将在断点和调试打印上看到的内容。不过,数据在内部可能仍然是正确的。

如果发现打印日期的唯一真正方法是像这样通过 SimpleDateFormat:

    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
    try 
        Date dateInUTC = dateFormat.parse("2011-10-16 00:00:00");
        Date currentDate = new Date();
        String stringInUTC = dateFormat.format(currentDate);
        System.out.println(dateInUTC);
        System.out.println(currentDate);
        System.out.println(stringInUTC);
    
    catch (ParseException e) 
        // not too worry, I wrote a nice date
    

现在打印看起来很混乱

Sun Oct 16 01:00:00 CET 2011
Thu Nov 10 15:47:46 CET 2011
2011-11-10 14:47:46

但是让我们来看看吧。

    首先,我的 SimpleDateFormat 获得了一种格式,我将其设置为假定所有输入和输出的文本都在 UTC 时区。 当我解析将被解释为 UTC 的日期 2011-10-16 00:00:00 但当我打印它时,java 使用我的语言环境 (CET) 将其打印为 2011-10-16 01:00:00 ,这也是我在断点上看到的内容 当我创建一个新的 Date() 时,该日期将在我的语言环境中,当我打印它时,它将显示 15:47(我的当前时间),但是当我使用我的时区感知 SimpleDateFormat 对其进行格式化时,它将显示时间UTC。

也就是说,java 和日期会让你感到困惑,直到你拔掉头发:)

希望这会有所帮助。

【讨论】:

非常有趣的解释!可能是 Spring 迫使某个时区。我会考虑是否可以使用 SimpleDateFormat 作为转换器而不是 GenericConversionService。 我很高兴它有帮助。我想这就是你要找的static.springsource.org/spring/docs/3.0.6.RELEASE/javadoc-api/…

以上是关于Spring @DateTimeFormat 转换错误与夏令时的主要内容,如果未能解决你的问题,请参考以下文章

mybatis查询结果date?

@DateTimeFormat注解

spring 返回时间格式统一处理

关于springmvc怎么自动把前台string类型日期字段转换成date类型

如何仅使用@DateTimeFormat 将日期@ConfigurationProperties 与时间绑定?

关于@DateTimeFormat注意点