Testing for a Leap Year

Four steps are used to determine if a year is a leap year. They can be presented in different order, but my preference for coding purposes is;

  1. A year that can be evenly divided by 400 is a leap year (e.g. 2000).

  2. Except as provided in step 1, a year that can be evenly divided by 100 is NOT a leap year (e.g. 1900).

  3. Except as provided in step 2, a year that can be evenly divided by 4 is a leap year (e.g. 2004).

  4. Every year not covered by steps 1 through 3 is NOT a leap year (e.g. 2002).

A simple and fast (1 millisecond) AppleScript that performs the above tasks is:

set testYear to 1900 --> false
set testYear to 2000 --> true
set testYear to 2002 --> false
set testYear to 2004 --> true

if (testYear mod 400 is 0) then
	return true
else if (testYear mod 100 is 0) then
	return false
else if (testYear mod 4 is 0) then
	return true
else
	return false
end if

The exact same logic is employed in this shortcut, which takes 10 milliseconds to run.

Leap Year Test Math.shortcut (22.8 KB)

Another approach is to test whether the specified year has a February 29th. The following is an example that takes 20 milliseconds.

Leap Year Test Date.shortcut (22.4 KB)

Google AI says the following can be used, but it didn’t work for me. I tried changing | to || and & to &&, but that also didn’t work.

Leap Year Test Expression.shortcut (21.9 KB)

Please measure this :wink:

use AppleScript version "2.5"
use framework "Foundation"
use scripting additions

set theYear to 2028

set cal to my NSCalendar's currentCalendar()
set dateComponents to my NSDateComponents's new()
set |month| of dateComponents to 2
set |year| of dateComponents to theYear
set theDate to cal's dateFromComponents:dateComponents
set theRange to cal's rangeOfUnit:(my NSCalendarUnitDay) inUnit:(my NSCalendarUnitMonth) forDate:theDate
set isLeapYear to theRange's |length| as integer is 29

1 Like

Thanks Stefan. That works great. The timing result was less than a millisecond.

Here is another way in pure AppleScript…

repeat with aDate in {1900, 2000, 2002, 2004}
	isLeapYear(aDate)
end repeat

on isLeapYear(y)
	if (date ("12/1/" & y)) - (date ("1/1/" & y)) > 28857600 then return true
	return false
end isLeapYear

Rearranging the mod logic into the order of likelihood of the conditions needing to be tested (and thus making it statistically faster):

return ((testYear mod 4 is 0) and ((testYear mod 100 > 0) or (testYear mod 400 is 0)))

The logic in the Google AI version appears to work, but I don’t know how the operators are supposed to be written in a Shortcuts setting.