0

When I need to increase date 1/1/2016 by 7 days, I can simply do:

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

What do I do, if I need to ignore some days (e.g. Saturdays), so the Result is 10/1/2016 ?

Kromster
  • 7,181
  • 7
  • 63
  • 111
Yahia
  • 21
  • 1
  • 6
  • i have work take from me 7 days start from 1/1/2016 then the day that i will finish it 8/1/2016 but i have A day Off "vacancy" so the day that i will finish it 9/1/2016? how can culc it – Yahia Jan 20 '16 at 08:51
  • @Krom regarding your updates, 9/1/2016 is on a weekend, further more, the original question mentioned Saturday only, and not weekends. Now you've made the answers not match the question. Not that the question was ever good. – David Heffernan Jan 20 '16 at 14:01
  • @DavidHeffernan Fixed. Perhaps this question can be salvaged. – Kromster Jan 20 '16 at 16:28
  • @KromStern Thanks. I think we need some input from the asker but I'm not sure the asker really understands SO yet. – David Heffernan Jan 20 '16 at 16:33

4 Answers4

3

Apparently, based on the somewhat cryptic comments, you wish to increment a date by a number of days, excluding Saturday. You can do that by making use of the the DayOfTheWeek function in DateUtils. This will tell you which day of the week a specified date falls on.

So your function is something like this:

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;

This is a rather crude way to achieve your goal. You can find more interesting ideas here: AddBusinessDays and GetBusinessDays

Now, in the question you suggest that 7 days from 01/01/2016, excluding Saturdays, takes you to 09/01/2016. But that is surely wrong since that date is a Saturday. The correct answer is surely 10/01/2016 which is a Sunday. In other words we need to skip over two Saturdays, on the 2nd and the 9th.

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • good i not know DayOf Function . but if the number of days more than 7 days such as 15 days need to make number of days is flexible how can calc the final date – Yahia Jan 20 '16 at 09:19
  • how about this condition if DayOf(Result) = 6 then ? – Yahia Jan 20 '16 at 09:29
  • [DayOfTheWeek](http://docwiki.embarcadero.com/Libraries/en/System.DateUtils.DayOfTheWeek) returns a number from 1..7. This is what David means. `DayOf` returns a value from 1..31. – LU RD Jan 20 '16 at 09:33
  • OK `DayOf` return from 1..31 ` DayOfTheWeek ` return weekly if i use `DayOf` and the number of Days 14 day and the num of Day off 2 day how can calc the number of day off and increment to date to reach the final date – Yahia Jan 20 '16 at 10:07
  • I think you have what you need. `DayOfTheWeek(...) = DaySaturday` detects saturday. Isn't that what you asked? If you want something else, can you let us know what else you want. – David Heffernan Jan 20 '16 at 10:09
  • i need to calc Days Off When use `Dayof` Function My Day 1/1/2016 Num of Days 14 day then Date 15/1/2016 Num Of day off 2 day (How Calc This) then Final Date 17/1/2015 – Yahia Jan 20 '16 at 10:45
  • I cannot make any sense of this. Please stop providing comments like this and please work on making your question as clear as possible. – David Heffernan Jan 20 '16 at 10:49
  • Your last edit returns `10/01/2016` and `18/01/2016` when the OP states `09/01/2016` and `17/01/2016` - it was my 1st attempt :D – fantaghirocco Jan 20 '16 at 11:18
  • @fantaghirocco I'm not sure that Yahia knows what he wants. Starting at Friday 1st Jan one day forward takes you to Sunday 3rd (skipping Saturday 2nd). Then 5 days more to Friday 8th. Then one more day to Sunday 10th (skipping Saturday 9th). After all the 9th is a Saturday. After all, we are expected not to have Saturday as an answer. So, I think the question is wrong and Sunday 10/01/2016 is the correct answer. – David Heffernan Jan 20 '16 at 11:28
  • I agree with you but Yahia takes a Sat - 09/01 - as a valid result. It's indeed unclear what the OP's asking. Anyway it seems to me that the OP wants to exclude Sat only from the initial range – fantaghirocco Jan 20 '16 at 11:32
  • @fantaghirocco Given the lack of clarity, it seems most likely to me that Yahia has not thought it through fully. There's no point in you and I arguing over what we think the specification is. Yahia needs to decide that. All we can do is give him the tools. We are not here to write complete programs for our askers. – David Heffernan Jan 20 '16 at 11:34
2

This adds a day to the calculation for each Saturday found in the ANumberOfDays range:

{.$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.

EDIT
The above may return a date on Saturday or not, depending on the UNCLEAR_WHAT_YOU_R_ASKING conditional define.

fantaghirocco
  • 4,761
  • 6
  • 38
  • 48
  • Finally ..Thanks Mooooooooooore This I mean :D – Yahia Jan 20 '16 at 13:12
  • you're welcome: if you mean accepting as an answer by "feedback", [this](http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work/5235#5235) is how it can be done if you want to – fantaghirocco Jan 20 '16 at 13:18
  • ok thanks @fantaghirocco but there are small problem i face in this nice solution ,it Ignoring Saturdays ,and increase the dayof to final date ,the final date may put in Saturday .understand me ? i need to ignore Saturday alwys . – Yahia Jan 20 '16 at 13:34
  • edited according to your comment but this is in contrast with the solution you have proposed where `1/1/2016 + 7 = 9/1/2016` because 9/1 **is** a Saturday :-\ – fantaghirocco Jan 20 '16 at 13:42
  • if `1/1/2016 +14 = 17/1/2016` ,It must Out `18/1/2016` as '16/1/2016' =Saturday ! – Yahia Jan 20 '16 at 13:57
  • @Yahia Why are you behaving this way? Why won't you ask the question in the question. Please do what I asked. Please edit the question to make it clear what you want. Furthermore, you now have all the pieces you need. If you know what you want you can implement it yourself. You should not expect us to write everything for you. – David Heffernan Jan 20 '16 at 14:00
  • @David I'm so sorry,u're right @ fantaghirocco Thanks :) – Yahia Jan 20 '16 at 14:06
  • @Yahia Do you know that you can edit your question to improve it. We still have no idea how you can desire to exclude Saturday's but also wish the answer to be a Saturday. Presumably that's just a silly mistake. But it makes it next to impossible for us to know exactly what you want. Which is then very frustrating when you start complaining that the output is not what you want. Since you have failed utterly to make it clear what you do want. I'm not even sure you know what you want. Stop thrashing around. Step back. Think. Use a pen and paper and a calendar. Decide what you want. Tell us. – David Heffernan Jan 20 '16 at 14:11
  • @David Ok,Not Need Any one Anger from Me :) I love All my Friend – Yahia Jan 20 '16 at 14:12
  • Nobody is angry. Could you please address the issues though. Do you want help or not? If you want help, can you see that making the question clear improves your chances? – David Heffernan Jan 20 '16 at 14:21
0

You've been given a couple of answers for the special case of excluding a single specific weekday. This is not particularly flexible.

You could try for implementing a function that takes a set of weekdays that are 'valid' as one of its parameters. The number of days to increment the date by is approximately AIncByDays * 7 / NoOfDaysInSet. But it gets rather tricky adjusting the result correctly for valid/invalid weekdays 'near' the start date. Even after all this complexity, you still wouldn't have a way to deal with special dates, like public holidays.

Fortunately there's different approach that's much simpler to implement and far more flexible. It's only drawback is that it's inefficient for 'large' increments.

  • The general approach is to increment 1 day at a time.
  • And on each increment check the validity of the new date.
  • Only if the new date is valid, reduce the increment counter by 1.
  • Repeat the above in a loop until the increment counter is reduced to 0.

The following uses a callback function to check the validity of each date.

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;

Now you can simply write whatever function you desire to determine a valid date and use it in the above function. E.g.

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


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

Note that it now becomes quite easy to write a function that uses only work days that aren't public holidays. E.g.

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;

The biggest advantage of this approach is that you don't have to deal with the risk of 'double-ignoring' a date because it's both a weekend day and a public holiday.

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

Disillusioned
  • 14,635
  • 3
  • 43
  • 77
-1

Determine the day of the week using DayOfTheWeek function. Now you know when will be next Saturday and whether it will get inside your period. If your period is larger than one week, then you can multiple number of Saturdays in your period by a number of full weeks. If your period is larger than 7 weeks, then you will have to add one more day for each 7 weeks.

Torbins
  • 2,111
  • 14
  • 16
  • 2
    It's not my downvote, but probably because your answer isn't v. clear. E.g. when you say "If your period is larger than one week, then you can multiple number of Saturdays in your period by a number of full weeks." that doesn't seem to take account of the fact that the partial week may still include a Saturday. Example code with comments explaining its logic would have been better, like DavidH's answer. – MartynA Jan 20 '16 at 18:19