【问题标题】:dateAdd doesn't add enough hours when DST endsdateAdd 在 DST 结束时没有添加足够的小时数
【发布时间】:2021-11-05 15:16:48
【问题描述】:

最近,一位同事提出了一个有趣的问题。创建一个 dateTime 对象,所有时间部分都为零。用dateAdd(...) 添加小时和分钟。这发生在 2021 年 10 月 31 日。夏令时在中欧结束。我们在服务器上观察到添加的时间比预期的少一小时,这意味着以下代码最终会打印时间戳{ts '2021-10-31 07:30:00'}

<cfset dOct31 = createDateTime( 2021, 10, 31, 0, 0, 0 )>

<cfset dOct310800 = dateAdd( 'n', 30, dOct31 )>
<cfset dOct310800 = dateAdd( 'h', 8, dOct310800 )>
<cfdump var="#dOct31#">
<cfdump var="#dOct310800#">

我在TryCF 上运行此代码,并且在任何可用服务器上都没有发现此问题。我认为这可能是因为语言环境,但将 TryCF 代码设置为 German (Standard) 并没有改变任何事情。

哪些 (JAVA) 设置会影响此行为?我可以禁用此行为吗?这是一个令人不快的惊喜。

【问题讨论】:

  • CF 使用Calendar class 进行日期数学运算。该类使用 jvm 的默认 TimeZone,因此它知道 DST。由于计算跨越了 DST 边界,因此它包含了该日期的 -1 小时 DST 调整。 trycf.com 上的结果不同的原因是该服务器使用没有 DST 的 UTC 时区。 AFAIK,无法禁用该行为,您必须使用其他方法。

标签: coldfusion dst


【解决方案1】:

CF 在日期数学中使用旧的 Calendar 类。该类使用 jvm 的默认 TimeZone,因此它知道 DST。由于计算跨越了 DST 边界,它包含了该日期的 -1 小时 DST 调整。 AFAIK,无法禁用该行为。

trycf.com 结果不同,因为该服务器使用没有 DST 的 UTC 时区。但是,将其更改为“Europe/Berlin”确实会重现 the issue described

我不知道有任何可靠的 cfml 解决方法。但是,较新的 java.time 类应该可以解决问题(目前推荐使用 CF 使用的遗留类)。较新的包包括几个类,用于在没有 TimeZone 的情况下处理日期和时间,例如 LocalDateTime:

TryCF Example

// Create CF date as usual
dOct31 = createDateTime( 2021, 10, 31, 0, 0, 0 );

// Perform "Local" version of DateAdd(...)
zoneID = createObject("java", "java.time.ZoneId").of("Europe/Berlin");
instant = createObject("java", "java.time.LocalDateTime")
    .ofInstant( dOct31.toInstant(), zoneID )
    .plusMinutes( 30 )
    .plusHours( 8 )
    .atZone( zoneID )
    .toInstant()
;
 
// Convert back into CF date {ts '2021-10-31 08:30:00'} 
dOct310800 = parseDateTime( instant.toString() );

请记住,其他 CF 日期函数仍包含 DST。例如,这两个日期中的 dateDiff("h") 将包括 DST 在 10 月“回落”时添加的额外小时。

// Result is 9 hours - not 8 - due to DST
SetTimeZone( "Europe/Berlin") ;
writeDump( 
    dateDiff("h"
        , "2021-10-31 00:00:00"
        , "2021-10-31 08:00:00"
    ) 
);

此外,如果 LocalDateTime 在当前时区无效,则在转换回 CF 日期时会自动转换为有效时间。

SetTimeZone( "Europe/Berlin") ;

// 2:00 AM doesn't exist in the current time zone
zoneID = createObject("java", "java.time.ZoneId").of("Europe/Berlin");
instant = createObject("java", "java.time.LocalDateTime")
    .parse( "2021-03-28T00:00:00" )
    .plusHours( 2 )
    .atZone( zoneID )
    .toInstant()
;
 
// Result is {ts '2021-03-28 03:00:00'} 
result = parseDateTime( instant.toString() );
writeDump( result );

【讨论】:

    猜你喜欢
    • 2013-09-29
    • 2019-01-04
    • 2022-10-18
    • 1970-01-01
    • 2021-11-26
    • 1970-01-01
    • 2011-02-09
    • 1970-01-01
    • 2023-04-05
    相关资源
    最近更新 更多