为何使用Ada.Calendar计算2011年3月27日与28日的日期差为0天?
为何使用Ada.Calendar计算2011年3月27日与28日的日期差结果为0天?
问题重现
测试代码如下:
with Ada.Calendar; use Ada.Calendar; with Ada.Calendar.Arithmetic; use Ada.Calendar.Arithmetic; with Ada.Text_IO; use Ada.Text_IO; procedure main is t0 : Time := Time_Of (Year => 2_011, Month => 3, Day => 27); t1 : Time := Time_Of (Year => 2_011, Month => 3, Day => 28); da : Day_Count := t1 - t0; begin Put_Line (da'Img); end main;
执行结果:
[...]\obj\main.exe 0
预期结果应为1天,实际得到0天,原因及解决方法如下:
原因分析
这是夏令时切换导致的问题。2011年3月27日,欧洲多数地区(如中欧时区)执行夏令时切换,时钟被向前拨1小时——当天的23:00结束后,直接进入次日的01:00,不存在3月28日00:00这个时间点。
Ada.Calendar的Time_Of函数默认基于本地时区生成时间,当你尝试创建3月28日00:00这个不存在的时间时,系统会自动将其调整为3月28日01:00。而3月27日00:00到3月28日01:00的时间间隔为23小时,Day_Count类型仅统计完整的24小时周期,23小时不足1天,因此结果为0。
解决方法
方法1:使用Difference函数精确计算
Ada.Calendar.Arithmetic包中的Difference函数能正确处理夏令时带来的时间调整,精确拆分天数和剩余秒数:
with Ada.Calendar; use Ada.Calendar; with Ada.Calendar.Arithmetic; use Ada.Calendar.Arithmetic; with Ada.Text_IO; use Ada.Text_IO; procedure main is t0 : Time := Time_Of (Year => 2_011, Month => 3, Day => 27); t1 : Time := Time_Of (Year => 2_011, Month => 3, Day => 28); da : Day_Count; sec : Day_Duration; begin Difference (Left => t1, Right => t0, Days => da, Seconds => sec); Put_Line ("天数差: " & da'Img & ", 剩余秒数: " & sec'Img); end main;
执行后会得到正确的天数差1,剩余秒数3600(即1小时),符合实际时间间隔。
方法2:基于UTC时间计算
UTC时区不存在夏令时切换,直接使用UTC时间计算可避免这类问题:
with Ada.Calendar; use Ada.Calendar; with Ada.Calendar.Arithmetic; use Ada.Calendar.Arithmetic; with Ada.Text_IO; use Ada.Text_IO; with Ada.Calendar.Time_Zones; use Ada.Calendar.Time_Zones; procedure main is t0 : Time := Time_Of (Year => 2_011, Month => 3, Day => 27, Time_Zone => UTC_Time_Zone); t1 : Time := Time_Of (Year => 2_011, Month => 3, Day => 28, Time_Zone => UTC_Time_Zone); da : Day_Count := t1 - t0; begin Put_Line (da'Img); end main;
这段代码的执行结果会直接返回1,完全符合预期。
内容的提问来源于stack exchange,提问作者Roland Coghetto
相关产品推荐
相关产品推荐

