Working Days Offset (Date) Subroutine Help Needed

Hello all,

I am working on a large project that needs a due date subroutine.

Here is what I have so far:


set DUE_DATE to GET_DATE(20)

on GET_DATE(DAY_OFFSET)
	set THE_DATE to (current date) + (days * DAY_OFFSET)
	set DATE_MONTH to month of THE_DATE as number
	set DATE_DAY to day of THE_DATE as string
	set DATE_YEAR to year of THE_DATE as string
	set DUE_DATE to DATE_MONTH & "/" & DATE_DAY & "/" & DATE_YEAR as string
return DUE_DATE
end GET_DATE

The problem that I am trying to solve is the DAY_OFFSET is in working days, the date returned needs to take in account weekends. Recursion needed?

If I find a solution I will post.

Any suggestions would be greatly appreciated,
CarbonQuark

Hi,

I think this works, but need to test it still.

set working_days to 20
– converts working days to regular days
– increments reg_days when a weekend day is found
set the_count to 0
set reg_days to working_days
set cur_date to (current date)
repeat until the_count is reg_days
set the_count to the_count + 1
set this_date to cur_date + the_count * days
set this_day to weekday of this_date
if this_day is in {Saturday, Sunday} then
set reg_days to reg_days + 1
end if
end repeat
return reg_days

gl,

Hello kel,

It worked like a charm! Thanks for your help! Your Karma just went up 10 points.

Here is the final code:


set DUE_DATE to GET_DATE(20)

on GET_DATE(DAY_OFFSET)
	set DAY_COUNT to 0
	set REGULAR_DAYS to DAY_OFFSET
	set CURRENT_DATE to (current date)
	repeat until DAY_COUNT is REGULAR_DAYS
		set DAY_COUNT to DAY_COUNT + 1
		set THE_DATE to CURRENT_DATE + DAY_COUNT * days
		set THE_DAY to weekday of THE_DATE
		if THE_DAY is in {Saturday, Sunday} then
			set REGULAR_DAYS to REGULAR_DAYS + 1
		end if
	end repeat
	set DAY_OFFSET to REGULAR_DAYS
	set THE_DATE to (current date) + (days * DAY_OFFSET)
	set DATE_MONTH to month of THE_DATE as number
	set DATE_DAY to day of THE_DATE as string
	set DATE_YEAR to year of THE_DATE as string
	set DUE_DATE to DATE_MONTH & "/" & DATE_DAY & "/" & DATE_YEAR as string
	return DUE_DATE
end GET_DATE

Thanks Again,
CarbonQuark

Hi CarbonQuark,

You’re welcome.

Just what I need. More good Karma.

gl,

Why do the really intriguing problems always get posted late at night (my time)? :wink:

Here’s my attempt. It doesn’t produce quite the same results as yours as I had to guess how you wanted to interpret “working days offset” in this context. If the following’s run between Monday and Friday, it counts from the day concerned. If it’s run at the weekend, it counts from the Monday.

on get_due_date(day_offset) -- Requires Tiger or later.
	set today to (current date)
	-- If today comes at a weekend, advance it to the Monday.
	if ((today's weekday) mod 6 is 1) then set today to today - (today - (date "Monday 6 January 1000 00:00:00")) mod weeks + weeks
	-- Add the work-day offset + two days for every complete week.
	set due_date to today + (day_offset + day_offset div 5 * 2) * days
	-- If a short week spans a weekend then add two more days.
	if (due_date's weekday comes before today's weekday) then
		set due_date to due_date + 2 * days
		-- Otherwise, if the due date falls on a weekend, advance it to the Monday.
	else if ((due_date's weekday) mod 6 is 1) then
		set due_date to due_date - (due_date - (date "Monday 6 January 1000 00:00:00")) mod weeks + weeks
	end if
	
	return short date string of due_date
end get_due_date

-- Test loop:
set l to {}
repeat with i from 1 to 10
	set end of l to get_due_date(i)
end repeat
l

Nice one Nigel. I was hoping that CarbonQuarks number of days weren’t big.

gl,

The lateness of the hour was probably the reason for a knuckle-bitingly fatal flaw in my script. Now corrected above. :rolleyes:

But to really make it more interesting, Nigel & Kel, consider this (this year’s US Government holidays, but any list of dates will do):

set Holiday06 to {date "Monday, January 2, 2006 12:00:00 AM", date "Monday, February 20, 2006 12:00:00 AM", date "Tuesday, May 9, 2006 12:00:00 AM", date "Tuesday, July 4, 2006 12:00:00 AM", date "Monday, September 4, 2006 12:00:00 AM", date "Monday, October 9, 2006 12:00:00 AM", date "Friday, November 10, 2006 12:00:00 AM", date "Thursday, November 23, 2006 12:00:00 AM", date "Monday, December 25, 2006 12:00:00 AM"}

This adds the wrinkle that if one of these dates falls between now and the finish, the finish must be moved a day for it.

You’re right Adam!

It depends on where you work.

Actually not bad since dates can be compared quite easily and the list of holidays isn’t very long:

set Holiday to date "Tuesday, March 14, 2006 12:00:00 AM"
set today to (current date)
set MuchLater to date "Saturday, July 29, 2006 12:00:00 AM"
set notMuchLater to date "Wednesday, February 22, 2006 12:00:00 AM"

Holiday < MuchLater and Holiday > today --> true, so add one
Holiday < notMuchLater and Holiday > today --> false, no action

Hi Nigel,

I added this subroutine to change the weekday to number for non-Tiger OS, but what should the week start with? I’m getting wrong dates after trying several orders for the weekday list.

on get_due_date(day_offset) – Requires Tiger or later.
set today to (current date)
– If today comes at a weekend, advance it to the Monday.
if (GetWeekdayNum(today’s weekday) mod 6 is 1) then set today to today - (today - (date “Monday, January 6, 1000 12:00:00 AM”)) mod weeks + weeks
– Add the work-day offset + two days for every complete week.
set due_date to today + (day_offset + day_offset div 5 * 2) * days
– If a short week spans a weekend then add two more days.
if (GetWeekdayNum(due_date’s weekday) comes before ¬
GetWeekdayNum(today’s weekday)) then
set due_date to due_date + 2 * days
– Otherwise, if the due date falls on a weekend, advance it to the Monday.
else if (GetWeekdayNum(due_date’s weekday) mod 6 is 1) then
set due_date to due_date - (due_date - (date “Monday, January 6, 1000 12:00:00 AM”)) mod weeks + weeks
end if
return date string of due_date
end get_due_date

on GetWeekdayNum(the_weekday)
set the_days to {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}
repeat with i from 1 to 7
if the_weekday is item i of the_days then exit repeat
end repeat
return i
end GetWeekdayNum

– Test loop:
set l to {}
repeat with i from 1 to 10
set end of l to get_due_date(i)
end repeat
l

Eddited: Nevermind. I see. But shouldn’t 1 workday fall on Monday if the current date is on a weekend.l

gl,

If you take it that an order entered on Thursday will be due on Friday, you have had Thursday to work on it. If an order is entered on Saturday, however, you need Monday to work on it so it’s due Tuesday.

Hi Adam,

For consistancy, I think it should fall on Monday. You can just as easily say, it’s due by the end of Monday. This would be consistant with say a start day. You could say, a start day begins at the beginning of the day. Then you don’t need two handlers for start and due. You just use a rule.

gl,

I actually agree with you - I was just explaining what I saw. If you’re there to enter an order on Saturday or Sunday, then Monday is logically the next working day

Hi, Kel and Adam.

The purpose of the script is to calculate what the date will be a certain number of working days after the current date. In this respect, the day the script’s run is “day 0”. It would presumably normally be used on working days; but if it’s run at the weekend by a zealous clerk working out of office hours, my version pretends its running on the next normal working day, ie. Monday. Monday is then “day 0” for the calculation.

The weekday numbers, on the other hand ” obtained by automatic coercions ” are used to locate the days within the weeks. They provide a fast way to tell when dates fall on weekends and can also show when a period of less than seven days includes a weekend. (The end date has a lower weekday number than the start date.) AppleScript’s new weekday-to-integer coercions treat weeks as running from Sunday to Saturday. That accords with my own view, but I think the ISO standard is Monday to Sunday.

Interesting that kel should provide a version of the script that’s more compatible with earlier systems. I was doodling with such a beast myself this morning in case any one should happen to ask. :slight_smile:

-- Return a short date string, with a long year and leading zeros,
-- in the order configured on the user's machine.
on shortDate(theDate)
	set {year:y, day:d} to theDate
	copy theDate to b
	set b's month to January
	set m to (b - 2500000 - theDate) div -2500000
	set yyyymmdd to (y * 10000 + m * 100 + d) as string
	
	-- Get year, month, and day numbers for the machine's interpretation of 'date "1/2/3"'.
	set {year:y, day:d} to date ("1/2/3" as string)
	set y to y mod 10
	set m to 6 - y - d
	
	-- Use them as indices to arrange theDate's short date string.
	set sDate to {missing value, missing value, missing value}
	tell sDate to set {item y, item m, item d} to yyyymmdd's {text 1 thru 4, text 5 thru 6, text 7 thru 8}
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to "/"
	set sDate to sDate as string
	set AppleScript's text item delimiters to astid
	
	return sDate
end shortDate

-- Return the weekday number of a given date (Sunday = 1).
on weekdayNum(theDate)
	return (theDate - (date "Sunday 5 January 1000 00:00:00")) mod weeks div days + 1
end weekdayNum

-- Return the Monday after a given date.
on MondayAfter(theDate)
	return theDate - (theDate - (date "Monday 6 January 1000 00:00:00")) mod weeks + weeks
end MondayAfter

on get_due_date(day_offset) -- Any AppleScript version.
	set today to (current date)
	-- If today comes at a weekend, advance it to the Monday.
	if ((today's weekday is Saturday) or (today's weekday is Sunday)) then set today to MondayAfter(today)
	-- Add the work-day offset + two days for every complete week.
	set due_date to today + (day_offset + day_offset div 5 * 2) * days
	-- If a short week spans a weekend then add two more days.
	if (weekdayNum(due_date) comes before weekdayNum(today)) then
		set due_date to due_date + 2 * days
		-- Otherwise, if the due date falls on a weekend, advance it to the Monday.
	else if ((due_date's weekday is Saturday) or (due_date's weekday is Sunday)) then
		set due_date to MondayAfter(due_date)
	end if
	
	return shortDate(due_date)
end get_due_date

But I don’t think I want to be drawn into adapting it to cope with other people’s national holidays. :wink:

The other problem is that say it’s run at the end of Friday and I want it due on Monday, then it works. But if the program is run on Saturday, then there’s no way to have it due on Monday. There’s a contradiction there. :slight_smile:

Hi, kel.

The contradiction would be in expecting a script that was designed to treat Saturdays as non-working days to behave as if Saturdays were working days. :wink:

But as I hinted somewhere above, the best we can do is to interpret what’s needed. CarbonQuark posted a handler without saying what it did and without giving the context in which it would be used. The brief was only that it should take “working days” and “weekends” into account, but these terms weren’t defined either. We’ve had to guess the details for ourselves and have apparently come to different conclusions. CQ was happy with your script, so perhaps your guess was the right one. :slight_smile:

(It’s a bit like the common request for scripts that delete files “over a week old”. There are six sensible ways to interpret that, depending on whether you measure age by creation date or modification date, and whether by “over a week” you mean “before this day last week”, “before and including this day last week”, or simply “before 604800 seconds ago”.)

Wow!

Thanks for the help (kel, Adam, and Nigel).

I’m always looking to learn new things about Applescript.

I realize that there is a million ways to skin an Applescript cat, It is always interesting to see other people’s solutions and ideas to a problem.

Thanks,
CarbonQuark