如何只向日期时间变量添加几天?

How to add only some days to date-time variable?

当我需要将日期 1/1/2016 增加 7 天时,我可以简单地这样做:

IncDay(myDate, 7); // Result 8/1/2016

如果我需要忽略某些日子(例如星期六),结果是 10/1/2016,我该怎么办?

显然,根据有些神秘的评论,您希望将日期增加几天,不包括星期六。您可以通过使用 DateUtils 中的 DayOfTheWeek 函数来做到这一点。这将告诉您指定日期是星期几。

所以你的函数是这样的:

function IncExcludingSaturday(FromDate: TDateTime; IncDays: Integer): TDateTime;
begin
  Assert(IncDays >= 0);

  Result := FromDate;
  if DayOfTheWeek(Result) = DaySaturday then
    Result := IncDay(Result);

  while IncDays > 0 do
  begin
    Result := IncDay(Result);
    if DayOfTheWeek(Result) = DaySaturday then
      Result := IncDay(Result);

    dec(IncDays);
  end;
end;

这是实现您的目标的一种相当粗略的方法。您可以在这里找到更多有趣的想法:AddBusinessDays and GetBusinessDays

现在,在问题中,您建议从 01/01/2016 开始的 7 天(不包括星期六)会将您带到 09/01/2016。但这肯定是错误的,因为那个日期是星期六。正确答案肯定是 10/01/2016,这是星期日。换句话说我们需要跳过两个星期六,2号和9号。

使用 DayOfTheWeek 函数确定星期几。现在您知道下周六是什么时候以及它是否会进入您的月经。如果您的经期超过一周,那么您可以将经期中的星期六乘以整周数。如果您的经期超过 7 周,那么您必须每 7 周增加一天。

这会在 ANumberOfDays 范围内的每个 星期六 的计算中增加一天:

{.$DEFINE UNCLEAR_WHAT_YOU_R_ASKING}

function IncDayIgnoringSaturdays(const AValue: TDateTime; const ANumberOfDays: Integer = 1): TDateTime;
var
  i, j: Integer;
begin
  i := ANumberOfDays;
  j := 0;
  Result := AValue;
  while i > 0 do begin
    Result := IncDay(Result);
    if DayOfTheWeek(Result) = DaySaturday then
      Inc(j);
    Dec(i);
  end;
  Result := IncDay(Result, j);

  {$IFDEF UNCLEAR_WHAT_YOU_R_ASKING}
  if DayOfTheWeek(Result) = DaySaturday then
    Result := IncDay(Result);
  {$ENDIF}
end;

begin
  WriteLn(DateTimeToStr(IncDayIgnoringSaturdays(StrToDateTime('1/1/2016'), 7)));
  WriteLn(DateTimeToStr(IncDayIgnoringSaturdays(StrToDateTime('1/1/2016'), 14)));
  ReadLn;
end.

编辑
以上可能 return 日期是否为星期六,取决于 UNCLEAR_WHAT_YOU_R_ASKING 条件定义。

对于排除单个特定工作日的特殊情况,您已经得到了几个答案。这不是特别灵活。

您可以尝试实现一个函数,该函数将 'valid' 的一组工作日作为其参数之一。将日期递增的天数约为 AIncByDays * 7 / NoOfDaysInSet。但是在 valid/invalid 工作日 'near' 开始日期正确调整结果变得相当棘手。即使在所有这些复杂性之后,您仍然无法处理特殊日期,例如 public 假期。

幸运的是,有不同的方法更易于实施且更灵活。唯一的缺点是 'large' 增量效率低下。

  • 一般的做法是一次增加 1 天。
  • 并在每次递增时检查新日期的有效性。
  • 仅当新日期有效时,递增计数器减1。
  • 循环重复上述操作,直到增量计数器减为0。

下面使用回调函数来检查每个日期的有效性。

type
  TValidDateFunc = function (ADate: TDateTime): Boolean;

function IncValidDays(AStartDate: TDateTime; AIncBy: Integer; AIsValid: TValidDateFunc): TDateTime;
var
  LIncDirection: Integer;
begin
  // Support dec using negative AIncBy
  if AIncBy >= 0 then
    LIncDirection := 1
  else
    LIncDirection := -1;

  Result := AStartDate;
  while (AIncBy <> 0) do
  begin
    IncDay(Result, LIncDirection);
    if (AIsValid(Result)) then
      Dec(AIncBy, LIncDirection);
  end;
end;

现在您可以简单地编写您想要确定有效日期的任何函数并在上述函数中使用它。例如

function DateNotSaturday(ADate: TDateTime): Boolean;
begin
  Result := (DayOfTheWeek(ADate) <> DaySaturday);
end;


NewDate := IncValidDays(SomeDate, 10, DateNotSaturday);

请注意,现在编写仅使用非 public 假期的工作日的函数变得非常容易。例如

function IsWorkDay(ADate: TDateTime): Boolean;
var
  LDay, LMonth, LYear: Word;
begin
  DecodeDate(ADate, LYear, LMonth, LDay);

  Result := True;
  Result := Result and (DayOfTheWeek(ADate) <> DaySaturday);
  Result := Result and (DayOfTheWeek(ADate) <> DaySunday);
  Result := Result and ((LDay <> 1) or (LMonth <> 1)); //Excludes New Years day.
  ...
end;

这种方法的最大优点是您不必处理 'double-ignoring' 约会的风险,因为它既是周末 public假期。

NOTE: In recent versions of Delphi you could replace the callback function with an anonymous method.