如何计算两个TDateTime的相隔时间(年、月、日、时、分、秒)? (100分)

  • 主题发起人 叮叮当当
  • 开始时间
哦,我明白我错在什么地方了,多谢杜兄提点
你才是正确的[:)]
 
TO: 萧月禾
我也一直想寻求一种简单的解决这个问题的办法,就像你的程序,可惜事与愿违。

TO: 杜宝
我把你的代码整理了一下,附上测试程序:

新建一个Application,在窗体上放置一个TEdit和一个TTimer,双击Timer1让IDE生成OnTimer事件,然后粘贴下面整段代码,按F9运行。
代码:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

procedure DateTimeDiff(St, Et: TDateTime; out Y, M, D, H, N, S, MS: Word);

var
  Form1: TForm1;

implementation

uses DateUtils;

var
  BeginTime: TDateTime;

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  BeginTime := Now;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  Y, M, D, H, N, S, MS: Word;
begin
  DateTimeDiff(BeginTime, Now, Y, M, D, H, N, S, MS);
  Edit1.Text := Format('已运行 %d 年 %d 月 %d 日 %d 小时 %d 分 %d 秒 %d 毫秒',
    [Y, M, D, H, N, S, MS]);
end;

procedure DateTimeDiff(St, Et: TDateTime; out Y, M, D, H, N, S, MS: Word);
var
  SYear, SMonth, SDay, SHour, SMin, SSec, SMSec: Word;      // 开始时间
  EYear, EMonth, EDay, EHour, EMin, ESec, EMSec: Word;      // 结束时间
  DYear, DMonth, DDay, DHour, DMin, DSec, DMSec: Integer;   // 时差
  EDate: TDateTime;                 // 中间变量
  i: Integer;                       // 中间变量,记录时差间隔。

  procedure InitVars;
  begin
    DecodeDateTime(EDate, EYear, EMonth, EDay, EHour, EMin, ESec, EMSec);
  end;
begin
  if St > Et then                   // 参数出错
  begin
    EDate := St;                    // 交换起止时间
    St := Et;
    Et := EDate;
  end;

  // 变量初始化。
  DecodeDateTime(St, SYear, SMonth, SDay, SHour, SMin, SSec, SMSec);
  EDate := Et;
  InitVars;

  // 进位的加减,不足则借位。
  // 从最低位开始,先来毫秒位:
  DMSec := EMSec - SMSec;           // 取毫秒位差
  if DMSec < 0 then
  begin
    DMSec := 1000 + DMSec;
    EDate := IncSecond(EDate, -1);  // 借位
    InitVars;                       // 改变量
  end;
  DSec := ESec - SSec;              // 取秒位差
  if DSec < 0 then
  begin
    DSec := 60 + DSec;
    EDate := IncMinute(EDate, -1);
    InitVars;
  end;
  DMin := EMin - SMin;              // 取分钟差
  if DMin < 0 then
  begin
    DMin := 60 + DMin;
    EDate := IncHour(EDate, -1);
    InitVars;
  end;
  DHour := EHour - SHour;           // 取小时差
  if DHour < 0 then
  begin
    DHour := 24 + DHour;
    EDate := IncDay(EDate, -1);
    InitVars;
  end;

  DDay := EDay - SDay;              // 取天差,这里就复杂了!
  if DDay < 0 then
  begin
    DDay := DaysInMonth(SDay) - SDay + EDay;  // 到月底再加
    EDate := IncMonth(EDate, -1);
    InitVars;
  end;
  DMonth := EMonth - SMonth ;       // 取月差
  if DMonth < 0 then
  begin
    DMonth := 12 + DMonth;
    EDate := IncYear(EDate, -1);
    InitVars;
  end;
  DYear := EYear - SYear;           // 取年差
  Assert(DYear >= 0, '应该不可能小于0!');

  Y := DYear; M := DMonth; D := DDay;
  H := DHour; N := DMin; S := DSec; MS := DMSec;
end;

end.
你的程序才是真正符合我要求的。
向你的严谨态度致敬!
 
好象不用这么烦吧!

好象SQL Server里有专门的函数的嘛!好象就叫DATEDIFF( )的!只要两个参数就可以了!
 
“来自:叮叮当当, 时间:2002-5-29 23:15:00, ID:1131940
TO: xiao_ping
menxin的程序最后好像也有问题(计算天数的地方):
r3:=DayOf(Date2)-DayOf(Date1) +IfThen(DayOf(Date1)<=DayOf(Date2),0,DaysInMonth(StartOfTheMonth(Date2)-1));
假如
Date1=2000-01-30
Date2=2000-03-31

StartOfTheMonth(Date2)=2000-03-01
StartOfTheMonth(Date2)-1=2000-02-29 (TDataTime的整数部分就是天数,减1就等于天数减1)
DaysInMonth(StartOfTheMonth(Date2)-1)=29
DayOf(Date1)<=DayOf(Date2) 成立,因此 r3:=DayOf(Date2)-DayOf(Date1) +29=31-30+29=30 (天)
这是错误的,应该是“2个月零1天”。


to 叮叮当当:
你得到你要的答案了吗?
我测试了一下menxin的程序,发现你之前分析的结果有错。
我运行得到的结果是2个月零1天没错呀。

另外我觉得,如果月份不以固定的天数计算,会出现让人理解上的错误,
假如 Date1=2000-01-30 Date2=2000-03-29,这样应该是过了1个月28天或者1个月29天或者1个月30天?
 
不过menxin的程序的确有他的错误。

我认为还是用天表示就好,否则容易让人误解。
 
TO: 冰封
>另外我觉得,如果月份不以固定的天数计算,会出现让人理解上的错误,
>假如 Date1=2000-01-30 Date2=2000-03-29,这样应该是过了1个月28天或者1个月29天或者1个月30天?
也有点道理,差点连我自己也不知道结果应该是什么了,不过我用杜宝兄的程序试了一下,返回的是“1月30日”。我想了一下,这也很好解释呀。那个“1月”是指2月份整月(29天),30天是指3月份的1号~29号+1月份的31号。[:D]我这个也有道理的啊:D
 
谢谢大家!给分了。
 
很不爽!!!!!睡前忍不住再说两句,感觉这个贴里面说的人多,做的人少。
这种程序,我想只要多测试就能看出效果。很多方法,其实只要充分测试一下,
就可以看出好坏来了。我不知道大家测试的数据有多少,我自己测试的时候,
基本都用1000个组数据做循环,再加上小数位的,具体可以看我上面的贴子。

冰封朋友说menxin的方法是对的,好,那我们来做个测试:
新建一个工程,加一个Button 一个 Memo,把我的程序:
De2DateTime贴上,
begin
...
//结果!
Result := Format('两个日期相差%d年%d个月%d天',[Dyear,DMonth,DDay]); //这里改一下,统一输出。
end;
再把menxin兄方法改一下:
function D2(st,et:TDateTime) :String;
var
Date1,Date2:TDate;
r1,r2,r3:integer;
begin
Date1:=Min(st,et);
Date2:=Max(st,et);
r1:=YearsBetween(Date1,Date2);
r2:=MonthsBetween(Date1,Date2)-r1*12;
r3:=DayOf(Date2)-DayOf(Date1) +IfThen(DayOf(Date1)<=DayOf(Date2),0,DaysInMonth(StartOfTheMonth(Date2)-1));
r2:= r2 + r3 div DaysInMonth(StartOfTheMonth(Date2)-1);
r3:= r3 mod DaysInMonth(StartOfTheMonth(Date2)-1);
Result := Format('两个日期相差%d年%d个月%d天',[r1,r2,r3]);
end;
按冰封兄的修改方法改后的版本:
function D1(st,et:TDateTime) :String;
var
Date1,Date2:TDate;
r1,r2,r3:integer;
begin
Date1:=Min(st,et);
Date2:=Max(st,et);
r1:=YearsBetween(Date1,Date2);
r2:=MonthsBetween(Date1,Date2)-r1*12; //按冰封兄的贴子修改
r3:=DayOf(Date2)-DayOf(Date1) +IfThen(DayOf(Date1)<=DayOf(Date2),0,DaysInMonth(StartOfTheMonth(Date2)-1));
Result := Format('两个日期相差%d年%d个月%d天',[r1,r2,r3]);
end;
一起来做个测试:
先来menxin兄的:
procedure TForm1.Button1Click(Sender: TObject);
var
t1,t2:TDateTime;
i:Integer;
s,s1:String;
begin

T1:=StrToDateTime('2003-12-01'); //起始日期

for i := 0 to 370 do //循环测试370天,比对370天内的不同
begin
T2 := T1 + i*1; //测试小数不注释。
s:=D2(t1,t2);
s1:=De2DateTime(T1,T2);
if s <> s1 then //两种方法不同时显示!
memo1.Lines.Add(DateTimeToStr(t1)+' : '+DateTimeToStr(t2)+' '+s+' '+s1);
end;
end;
如果:且不说冰封兄上面说的语义上的区别:
2003-12-1 : 2003-12-31 两个日期相差0年1个月0天 两个日期相差0年0个月30天 //这里
2003-12-1 : 2004-1-31 两个日期相差0年2个月30天 两个日期相差0年1个月30天
2003-12-1 : 2004-3-1 两个日期相差0年2个月0天 两个日期相差0年3个月0天 //这里
2003-12-1 : 2004-3-30 两个日期相差0年4个月0天 两个日期相差0年3个月29天
2003-12-1 : 2004-3-31 两个日期相差0年4个月1天 两个日期相差0年3个月30天
2003-12-1 : 2004-5-1 两个日期相差0年4个月0天 两个日期相差0年5个月0天
2003-12-1 : 2004-5-31 两个日期相差0年6个月0天 两个日期相差0年5个月30天 // 这里
2003-12-1 : 2004-7-1 两个日期相差0年6个月0天 两个日期相差0年7个月0天 //这里
2003-12-1 : 2004-7-31 两个日期相差0年8个月0天 两个日期相差0年7个月30天
2003-12-1 : 2004-8-31 两个日期相差0年9个月30天 两个日期相差0年8个月30天 //这里
2003-12-1 : 2004-10-31 两个日期相差0年12个月0天 两个日期相差0年10个月30天 //这里
谁对谁错应该很明显了吧???

好,再来冰封兄的版本
procedure TForm1.Button1Click(Sender: TObject);
var
t1,t2:TDateTime;
i:Integer;
s,s1:String;
begin

T1:=StrToDateTime('2003-12-01');

for i := 0 to 370 do
begin
T2 := T1 + i; //测试小数不注释。
s:=D1(t1,t2);
s1:=De2DateTime(T1,T2);
if s <> s1 then
memo1.Lines.Add(DateTimeToStr(t1)+' : '+DateTimeToStr(t2)+' '+s+' '+s1);
end;
end;
结果:
2003-12-1 : 2004-1-31 两个日期相差0年2个月30天 两个日期相差0年1个月30天
2003-12-1 : 2004-3-1 两个日期相差0年2个月0天 两个日期相差0年3个月0天
2003-12-1 : 2004-5-1 两个日期相差0年4个月0天 两个日期相差0年5个月0天
2003-12-1 : 2004-7-1 两个日期相差0年6个月0天 两个日期相差0年7个月0天
2003-12-1 : 2004-8-31 两个日期相差0年9个月30天 两个日期相差0年8个月30天
2003-12-1 : 2004-10-31 两个日期相差0年11个月30天 两个日期相差0年10个月30天

不用我说了吧????



 
TO: 杜宝
SORRY,分是少了点。
我们可以交个朋友么?MSN:pschen@21cn.com,QQ:990080。
 
呵呵,冰封,就是针对你,就是自我称赞,怎么了?
象你说的,
>>本来论坛就是畅所欲言的地方:
那好,我测试了一下menxin的程序,再测试你修改后的程序,证明它们是错的。
(当然,我知道这并不能证明我的程序就一定是正确的。)有何不可?

>>我测试了一下menxin的程序,发现你之前分析的结果有错。
是什么结果有错,拜托你说清楚好不好?是menxin的还是你的?

>>只要照我说那样改就成了,虽然我没试过程序,不过从数学逻辑的角度上看,一定可以。
这又是什么态度?难道我说“感觉这个贴里面说的人多,做的人少。”有问题吗?





 
to 杜宝:
>>>>只要照我说那样改就成了,虽然我没试过程序,不过从数学逻辑的角度上看,一定可以。
>>这又是什么态度?难道我说“感觉这个贴里面说的人多,做的人少。”有问题吗?
这句我是对叮当得出的结果,给想了个修改的方法,而对叮当当时指的那个错误,那种修改方式是可行的,因为叮当指出一个错误,当时我误以为menxin的程序就那个错误。
就这句上我承认我回答得太片面了。

>>>>我测试了一下menxin的程序,发现你之前分析的结果有错。
>>是什么结果有错,拜托你说清楚好不好?是menxin的还是你的?
是什么结果错,我有在那个贴子之前贴出,也说了我的测试结果。你要断章取义我也没办法。至于我的还是menxin的,那没有提的必要,我的本意就是只对menxin做补充,而menxin本身程序有错,我的当然也是错的。

至于你喜欢针对我,你喜欢自我称赞,那是你的自由。
我想我作的解释很明白了,如果叮叮当当也认为我有错,那我向叮当说声“对不起”。
至于你爱怎么说你说吧,我不再与你做无谓的争执。
 
KAO,少来! 说不过了就把自己的原贴改掉,要不要我提醒你:

这本来是个技术问题,是谁把它转成对我人品的讨论的?

你说我断章取意,干嘛不说你自己断章取意???

是谁自己不做测试,还找弱智的借口来搪塞?
KAO ,手伤了??? 你上网不用手,发贴不用手???

我测试你的代码,指出你的错误,就说我针对你,你以为你是谁啊?

KAO , 你TMD小人一个!!!
 
看到邮件才知道这里发生的事
只是讨论个技术问题
别伤和气呀。。。。。。。。
 
顶部