两个日期选择器之间的月份差异

时间:2017-10-16 09:52:29

标签: delphi datetime calendar

我使用这段代码来获取Delphi中两个日期选择器之间的区别。

Scanner sc = new Scanner(textfile);
int lenght = 0;
while (sc.hasNextLine()) {              
    try {
        for (int j = 0; j != 0; j++) {                      
            ImageMatrixGUI.getInstance().addImage(Land);
            ArrayImages.add(Land);                  
        }
        char tipo = sc.next().charAt(0);            
        System.out.println(tipo);               
        if (tipo == 'p') {
            ImageMatrixGUI.getInstance().addImage(pine);
        }
    }
}

当我运行该程序时,默认情况下弹出两个日期选择器的表单我将日期设置为月份差异。

如果我尝试更改结束日期,在第一次更改中它可以正常工作,但如果我再次尝试更改它,则不会根据开始日期和结束日期之间的月份差异更新标签。 基本上onchange事件不会捕获所有事件,或者至少是我理解的事件。我需要正确获取updateLabelandBtn过程的输出。

这是从另一种形式调用的子表单:

procedure TForm.CreateForm;
var
currDate : TDateTime;
begin
  currDate := Now;
  dtpStartDate.Date := currDate;
  dtpEndDate.Date :=  IncMonth(currDate);

end;

procedure TForm.updateLabelandBtn;
var
monthsDiff :Double;
begin
monthsDiff := (MonthOf( dtpEndDate.Date ) - MonthOf( dtpStartDate.Date )) + ((DayOf( dtpEndDate.Date ) - DayOf( dtpStartDate.Date))/32);
if (dtpEndDate.Date > dtpStartDate.Date ) then
 begin
  if (monthsDiff >1 ) then
   begin
    lblMsg.Caption := 'Wait';
    btnStart.Enabled := True;
   end
 end
else
 begin
  btnStart.Enabled := False;
  lblMsg.Caption := 'start date should be smaller than end date.';
 end;
end;

procedure TForm.dtpEndDateChange(Sender: TObject);
begin
updateLabelandBtn;
end;


procedure TForm.dtpStartDateChange(Sender: TObject);
begin
updateLabelandBtn;

end;
end.

输入示例:

 begin
    try
      Form.CreateForm;
      if Form.ShowModal = mrOk then
      RunQuery;
    finally
      Form.Release
    end

4 个答案:

答案 0 :(得分:2)

首先,你的差异计算相当奇怪,所以我建议改为使用DaysBetween()DaysInMonth()函数:

<击>

<击>
if DaysBetween(dtpEndDate.Date, dtpStartDate.Date) >= DaysInMonth(dtpStartDate.Date) then

<击> (见下面的编辑)

这可以正确接受,例如2月1日和3月1日以及7月1日和8月1日或1月15日(明年15月12日)

其次,据我所知,从开始到结束必须至少有一个月的时间。如果未满足此条件,则应添加错误消息。

编辑&#34; saastn&#34;在他们自己的答案中评论了这个答案,并指出了开始日期是在下个月不存在的一天的错误,结束日期是在下个月的第一个或第二个月。例如,开始日期= 31.1,结束日期= 1.3。为了涵盖这些日期,他们建议也考虑月份序数的差异,因此公式变为

if ((DaysBetween(dtpEndDate.Date, dtpStartDate.Date) >= DaysInMonth(dtpStartDate.Date)) or
    (MonthOf(dtpEndDate.Date) - MonthOf(dtpStartDate.Date) >= 2)) then

请在&#34; saastn&#34;

的答案中进一步阅读

答案 1 :(得分:2)

考虑到已接受的答案,似乎这个问题已经缩小到&#34;如何检查两个日期之间的差异是否少于一个月?&#34; 。 Toms建议使用DaysBetweenDaysInMonth函数。以下函数使用他的逻辑:

function DatesDiffLessThanAMonth_Tom(const FromDate, ToDate: TDate): Boolean;
begin
  Result :=
    (DaysBetween(ToDate, FromDate) <= DaysInMonth(FromDate));
end;

它适用于大多数情况,但仍然有一些例外:

FromDate     ToDate        DsIM1   DsB
2016-01-30 - 2016-03-01    31      31
2016-01-31 - 2016-03-01    31      30
2016-01-31 - 2016-03-02    31      31
2016-03-31 - 2016-05-01    31      31
2016-05-31 - 2016-07-01    31      31
2016-08-31 - 2016-10-01    31      31
2016-10-31 - 2016-12-01    31      31
2017-01-29 - 2017-03-01    31      31
2017-01-30 - 2017-03-01    31      30
2017-01-30 - 2017-03-02    31      31
2017-01-31 - 2017-03-01    31      29
2017-01-31 - 2017-03-02    31      30
2017-01-31 - 2017-03-03    31      31
2017-03-31 - 2017-05-01    31      31
2017-05-31 - 2017-07-01    31      31
2017-08-31 - 2017-10-01    31      31
2017-10-31 - 2017-12-01    31      31

我们可以将日期的一个月考虑在内以克服这个问题:

function DatesDiffLessThanAMonth_Tom_Mod(const FromDate, ToDate: TDate): Boolean;
begin
  Result :=
    (DaysBetween(ToDate, FromDate) <= DaysInMonth(FromDate)) and
    (MonthOf(ToDate) - MonthOf(FromDate) < 2);
end;

我用另一个功能检查了这一点,我相信它更具可读性和更快(2938毫秒相比,18221216呼叫为4125毫秒),并且它们都返回了从1900到5000的相同结果:

function DatesDiffLessThanAMonth(const FromDate, ToDate: TDate): Boolean;
var
  Y1, M1, D1, Y2, M2, D2: Word;
begin
  DecodeDate(FromDate, Y1, M1, D1);
  DecodeDate(ToDate, Y2, M2, D2);
  Result :=
    ((Y1 = Y2) and (M1 = M2)) or
    ((Y1 = Y2) and (M1 = M2 -1) and (D2 <= D1)) or
    ((Y1 = Y2 - 1) and (M1 = 12) and (M2 = 1) and (D2<=D1));
end;

此功能的另一个优点是它可以用于其他12个日历系统,如Solar Hijri (Jalali)Islamic (the Lunar one)日历。实际上我基本上为Hijri日历开发了这个功能。我不知道此日历包含DaysBetweenDaysInMonth函数的任何可靠日期库,而在Hijri中编码和解码日期well tested libraries并将它们转换为阳历日历

答案 2 :(得分:1)

'then'和'lblMsg.Caption:='等待'之间应该有'开始'。在格式化代码时很容易看到问题。

if (dtpEndDate.Date > dtpStartDate.Date ) then
 begin
  if (monthsDiff >1 )
   then lblMsg.Caption := 'Wait';  // oops!
  btnStart.Enabled := True;
 end

应该是

if (dtpEndDate.Date > dtpStartDate.Date ) then
 begin
  if (monthsDiff >1 ) then
   begin
    lblMsg.Caption := 'Wait';  
    btnStart.Enabled := True;
   end
 end
else
 begin
  btnStart.Enabled := False;
  lblMsg.Caption := 'start date should be smaller than end date.';
 end;

答案 3 :(得分:1)

使用MonthSpan或MonthsBetween存在一个基本问题,即两者都基于一个月的平均长度(每月30.4375天)给出近似值。我怀疑你想要更像以下内容:

编辑花了数年时间考虑:

function LogicalMonthSpan( const pDate1, pDate2 : TDateTime ) : Double;
begin
  Result := (YearOf( pDate2 ) - YearOf(pDate1))* 12 + (MonthOf( pDate2 ) - MonthOf( pDate1 )) + ((DayOf( PDate2 ) - DayOf( pDate1))/32);
end;

将考虑2月15日至3月16日> 1个月,而MonthSpan会显示它不到1个月