我在TCL中有一些代码试图测量两个日期之间的时间。
这两个第一部作品,但正如你看到最后一部作品不起作用;它完全错了,因为一年只能有12个月,但这还不止于此。
无论如何,我认为闰年是问题所在,但我不确定。你能帮忙吗?
proc isTimeAgo {t1} {
set t2 "1387524660"
#set t2 [clock seconds]
set cnt [expr {(($t2 - $t1) / 31536000)}]
set cur [clock add $t1 $cnt years]
set res {}
foreach unit {years months weeks days hours minutes seconds} {
while {$cur <= $t2} {
set cur [clock add $cur 1 $unit]
incr cnt
}
set cur [clock add $cur -1 $unit]
incr cnt -1
if {$cnt} {
lappend res $cnt $unit
}
set cnt 0
}
return $res
}
puts "1: [isTimeAgo "1355988659"]"
puts "2: [isTimeAgo "1355988660"]"
puts "3: [isTimeAgo "1355988661"]"
proc days_per_month year {
set leap [expr {($year%4)==0 && (!($year%400) || ($year%100))}]
set days [list 31 [expr {$leap ? 29 : 28}] 31 30 31 30 31 31 30 31 30 31]
return $days
}
结果是:
1: 1 years 1 seconds <- Correct
2: 1 years <- Correct
3: 11 months 4 weeks 1 days 23 hours 59 minutes 59 seconds <- Wrong
答案 0 :(得分:2)
日期和时间算术令人惊讶地难以正确,因为人们的意思是如此彻底不确定。在一段时间内添加间隔时,您必须在月份之前添加年份(因为闰年),日期前几个月(因为月份长度不同),以及时间前几天(因为DST变化)。这非常棘手,我们有一个处理复杂性的命令:clock add
(至少需要Tcl 8.5)。走向相反的方向?这是一个尝试每个单位的问题,直到你超越。
proc getInterval {from to} {
set result {}
foreach unit {year month week day hour minute second} {
set n 0
while 1 {
set new [clock add $from 1 $unit]
if {$new > $to} break
set from $new
incr n
}
lappend result $n $unit
}
return $result
}
让我们尝试一下:
% getInterval 1355988659 [clock seconds]
1 year 4 month 0 week 6 day 6 hour 52 minute 39 second
我想我们可以添加以确保from
在to
之前,并省略零项。我将这些留作练习。
请注意,您可能需要更改clock add
使用的区域设置和时区以获得您期望的答案(分别使用-locale
和-timezone
选项)。有关这可能会产生什么影响以及一些示例,请参见the documentation。