在TDateTime中添加时间

7

我想在TDateTime变量中添加秒数,以便结果是在整分钟的开头。例如,如果现在是08:30:25,我想改变TDateTime变量以存储08:31:00。

我发现TDateTime有一个Decode函数,我可以使用它。但是,并没有encode函数将修改后的时间放回TDateTime变量中。


1
我删除了你关于“TDateTime实例”的引用,因为TDateTime不是一个类或记录,因此你不能拥有它的“实例”。TDateTime只是一种类型,实际上是一个Double - Ken White
对于更一般的方法,请查看此答案,它可以让您四舍五入到最接近的分钟、15分钟、小时等。链接 - Svein Bringsli
@LU RD,明白了,看看我的解决方案。 - OnTheFly
1
TDateTime 的问题之一是它们是双精度浮点数,其中小数部分是一天中的时间。这意味着某些分钟无法表示为精确数字。因此,新的 DateUtils 库在进行计算之前总是将小数部分转换为包含从午夜开始的毫秒数的整数64。因此,使用库函数比在 TDateTime 上进行显式计算更安全。请参见 如何解决 Delphi 无法准确处理日期时间操作的问题? - LU RD
1
@DavidHeffernan,我看到了这个问题,并且也知道它的历史。我有一个数学函数可以优雅地解决这个问题。但是精度总会有限制,所以我没有公开它。我知道你认为在TDateTime上进行任何数学运算都是一种实现泄漏,因为使用TDateTime不应该利用它被实现为Double的事实。我同意使用DateUtilsSysUtils是一个更好的选择。虽然曾经有一段时间DateUtils非常有缺陷,许多人实现了自己的TDateTime工具。 - LU RD
显示剩余4条评论
3个回答

10

使用 DateUtils,可以像这样做:

Uses
  DateUtils;

var
  Seconds : Word;    

Seconds := SecondOfTheMinute(MyTime);  // Seconds from last whole minute
// Seconds := SecondOf(MyTime); is equivalent to SecondOfTheMinute()
if (Seconds > 0) then
  MyTime := IncSecond(MyTime,60 - Seconds);

1
示例中的最后一行不应该是:Mytime := IncSecond(MyTime,60 - Seconds); 吗? - Mike Jablonski

5
最近的版本中肯定有这个功能 - 请看DateUtils单元,特别是所有的Recode*例程和EncodeDateTime函数。这个单元在Delphi 2010中已经可用,可能甚至在更早的版本中也可用。

3
在旧版本中,您可以使用EncodeDate(y, m, d) + EncodeTime(h, min, 0, 0)。意思是通过组合日期和时间来创建一个完整的日期时间对象。 - Gerry Coll
DateUtils是一个旧的单元,自D6或更早版本以来就存在。但它并不是真正必要的,无法满足OP的需求。 - OnTheFly
Delphi 5肯定没有它(刚刚检查过)。无论如何,DateUtils中的RecodeMinute函数允许以一种漂亮易读的方式实现它,为什么不使用它呢。 - ain
检查过了,Delphi 7可以。 - OnTheFly

1

理论

TDateTime 数据类型表示自1899年12月30日以来的天数,作为实数。也就是说,TDateTime 的整数部分是整个天数的数量,小数部分表示一天中的时间。

实践

因此,您可以使用简单的算术运算来解决问题:

var
  Days: TDateTime;
  Mins: Extended;  { widen TDateTime's mantissa by 11 bits to accommodate division error } 

begin
  Days := Date + StrToTime('08:30:25');
  Writeln(DateTimeToStr(Days));


  Mins := Days * 24 * 60 ;  // compute minutes
  Mins := Math.Ceil(Mins);  // round them up
  Days := Mins / (24 * 60); // and back to days
  { or as simple and concise expression as: }
  // Days := Ceil(Days * MinsPerDay) / MinsPerDay;

  Writeln(DateTimeToStr(Days));

1
这段代码在时间无法被二进制浮点数精确表示时可能会失败。构造一个失败的例子应该很容易。这将是一个已经有00秒的时间。但最接近可表示的值大于真实值。 - David Heffernan
1
@David Heffernan,请证明你的说法。你说这很容易。在此之前,我不会给你提供任何帮助,抱歉。 - OnTheFly
1
@David Heffernan,这个消息是错误的,而且这个人以说废话闻名。你甚至不知道如何评估FP值的相等性。那是一个混淆我的技巧吗?别再扭捏了,拿出一个可以证明你观点的数字。我应该问你多少次?当然,其他人也可以发表这个数字。 - OnTheFly
2
它在pastebin上。很遗憾你不理解可表示性问题。你知道并非所有实数都可以表示吗?你知道形如N/1440的数字非常少是精确可表示的吗?你如何解释我给你发送的pastebin的行为?还是说,当你和我意见不合时,这是一种信仰,即我总是错的?即使证据表明相反? - David Heffernan
1
这可以通过epsilon舍入来修复。很高兴你终于面对需要修复的事实。我不知道epsilon是什么,也不知道你的意思。如果你必须保持浮点数,那么解决方法是:Math.Ceil(dt1*24*60 - 0.25) / (24*60)。这个方法可以适用到大约6000年左右。那时我们可能还在争论。 - David Heffernan
显示剩余22条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接