Testing for a Leap Year

set output to {}
repeat with indx from -401 to 401
	if Date_Info_Is_Leap_Year(indx) then set the end of output to indx & " "
end repeat
display dialog "" & output


on Date_Info_Is_Leap_Year(dateYear)
	--https://www.macscripter.net/u/Nigel_Garvey	https://www.macscripter.net/t/testing-for-a-leap-year/77975/4
	if dateYear < 1 then set dateYear to (dateYear * -1) - 1
	return ((dateYear mod 4 is 0) and ((dateYear mod 100 > 0) or (dateYear mod 400 is 0)))
end Date_Info_Is_Leap_Year

A nice adaptation of the handler! :sunglasses:

The rest’s quick and dirty though — building a list of lists and assuming the TIDs will be {“”} or “” when it’s coerced to text. :grin:

Thanks. I love tersely perspicuous AppleScript code. :wink:

Fair enough. But the TIDS are going to be {“”}. :stuck_out_tongue_winking_eye:

FWIW, the Shortcuts Get Date from Input action doesn’t work with dates in the BC era and cannot be used to get leap years. Somewhat surprisingly, the Adjust Date action will return a leap year (actually a leap day), but this would seem to be of no practical value. For the BC era, the math approach seems the best solution.

Sorry. A couple more takes on the slightly off-topic “leap years between … using AppleScript” theme. The first uses AppleScript date object math and can handle years between 0000 and 9999:

set startYear to 0
set endYear to 9999

leapYears(startYear, endYear)

on leapYears(startYear, endYear)
	script o
		property output : {}
	end script
	
	tell (startYear + 3) to set startYear to it - it mod 4
	tell (current date) to set {testDate, its day, its month, its year} to {it, 1, January, startYear}
	set fourYears to 1461 * days
	repeat with y from startYear to endYear by 4
		set testDate to testDate + fourYears
		if (testDate's day is 1) then
			set o's output's end to y
		else
			set testDate's day to 1
		end if
	end repeat
	
	return o's output
end leapYears

The other simply runs the 400-year cycles, jumping in and out at the appropriate years. It’s slightly faster than the date object script over the same range and can go way beyond it in either direction using the astronomical year convention:

set startYear to -9999
set endYear to 12000

leapYears(startYear, endYear)

on leapYears(startYear, endYear)
	script o
		property output : {}
	end script
	
	-- Initialise a loop variable y to the next multiple of 4 after or including startYear.
	set roundingAdjustment to 3 * ((startYear comes after 0) as integer) -- Positives and negatives are rounded up slightly differently.
	tell (startYear + roundingAdjustment) to set y to it - it mod 4
	if (y mod 400 = 0) then
		-- y's the last year of a 400-year cycle. Store it immediately and set up to start the next cycle.
		set o's output's end to y
		set y to y + 4
		set centuriesToEndOfCycle to 4
	else
		-- Otherwise, if it's one of the other century years, skip it.
		if (y mod 100 = 0) then set y to y + 4
		-- Work out which of the cycle's centuries, counting from the end, contains y.
		set centuriesToEndOfCycle to 1 + roundingAdjustment - y div 100 mod 4
	end if
	-- Initialise a variable representing the 96th year of each century to that before the current y.
	set centuryLimit to y - y mod 100 - 100 * ((y comes before 1) as integer) - 4
	
	repeat -- Each 400-year cycle.
		repeat centuriesToEndOfCycle times
			-- Generate the leap year numbers from y to this century's 96th year, or until endYear if that comes first.
			set centuryLimit to centuryLimit + 100
			if (centuryLimit comes after endYear) then set centuryLimit to endYear
			repeat with y from y to centuryLimit by 4
				set o's output's end to y
			end repeat
			set y to y + 8 -- Skip the century year …
		end repeat
		set cycleEndYear to y - 4 -- … except at the end of the cycle.
		if (cycleEndYear comes after endYear) then exit repeat
		set o's output's end to cycleEndYear
		set centuriesToEndOfCycle to 4
	end repeat
	
	return o's output
end leapYears

Nigel. I happened to notice that -2004 is shown as a year leap if -2004 is the startYear, but -2000 is not shown as a leap year if -2000 is the startYear.

Thanks, peavine. How annoying. I’ve tracked down what was causing the problem, but fixing it caused something else to fall apart! I’ve removed the script from my post while I do some more work on it….

Thanks again, peavine. I’ve now special-cased the start year being the last year of a 400-year cycle, which seems to have fixed the problem. :crossed_fingers:

Just for the sake of completeness, there’s another approach that’s probably obvious but should be mentioned, and that’s to include a list of leap years in the shortcut. This approach may give an erroneous result if any year contains three or fewer digits. The timing result is less than 10 milliseconds.

Leap Year Test.shortcut (22.4 KB)