Documentation AppleScript Calendar Functions

7 Feb 2010 - 11:00

Essential Calander Functions

Here is a new version of this page

© Jürgen Schell 2010
http://www.j-schell.de

Zur deutschen Version

Documentation and Code provided unter Lesser GNU Public License.
http://www.gnu.org/licenses/lgpl.txt

No guarantee for proper behaviour of these functions is given whatsoever, regard all code as training implementations.

Essential calendar functions might be useful for some people.
Some of these functions are trivial, others are less so.
Note that almost all functions indicate deficiencies of the AppleScript
date implementation. Mac OS implements almost everything (plus a lot of useful other things), but alas, the AppleScript developers do not think that they should provide you with access to those features.

Much code in these functions has been inspired by the book "Calandrical Calculations" by Nachum Dershowitz and Edward M. Reingold, Cambridge 2008, and by parts of the source of the PHP calender extension. Still, this is a different implementation.

Jürgen Schell, Langenfeld, Germany, May 2009

General notes on all functions:

If an argument is an integer, the code does a type cast. If you provide a floating point value, the value will be rounded to an integer.

Arguments of the type date are checked for their class.

If an argument is of the wrong class or out of range, an error 1700 will be raised. The error message is more detailed.

Where a function name has the word "date" in it, the function requires an AppleScript date object as argument or delivers one, as appropriate.
Where a function name has the word "cdn" in it, the function requires a chronological Julian day number as argument or delivers one, as appropriate.

List of Functions:

newDate( Year, Month, Day )

Result: AS date object
Purpose: Locale independent creation of date from variables

Year, Month and Day are integers, floats will be rounded.
Valid ranges:
1 <= Year
1 <= Month <= 12
1 <= Day <= 127

An error will occur if an argument is out of range.

Creates a date by setting the properties. The time is set to midnight. See note at end: "Ranges in newDate / newDateTime ("spill over")".

If detection of illegal dates is needed (like "February 29 2011"), remove the comment brackets arround the check code.

newDateTime(Year, Month, Day, Hour, Minute, Second)

Result: AS date object
Purpose: Locale independent creation of date/time from variables

Year, Month and Day, Hour, Minute, Second are integers, floats will be rounded.
Valid ranges:
1 <= Year
1 <= Month <= 12
1 <= Day <= 127
1 <= Hour <= 127
1 <= Minute <= 127
1 <= Second <= 32767

An error will occur if an argument is out of range.

Creates a date by setting the properties. Note at end "Ranges in newDate / newDateTime ("spill over")".

If detection of illegal dates is needed (like "February 29 2011"), remove the comment brackets arround the check code.

DateToISOdayofweek ( date object )

Result: integer
Purpose: Day of week for countries starting day count of a week on Monday

Returns the number of a day in the week, according to the ISO way of counting, Monday = 1, ..., Sunday = 7.

DateToISOweekofyear ( date object )

Result: integer
Purpose: Week of year for countries using ISO counting of week numbers.

The function returns the number of the week the date belongs to, counted in the ISO fashion. (An ISO week has seven days always. January 1 of year XXXX may belong to the first week of XXXX, or to the last week of the preceeding year. The first week of the year is the first one, having 4 or more days in year XXXX.)

preceeding_day( day number, date object or cdn )

Result: date object or integer, depending on second argument.
Purpose: Find date of a certain day of week, preceeding a given date. Useful for several special days like Advent Sunday or US Election Day.

The function returns the date of the given weekday preceding the given date. day number can be an integer from 1 to 7. ISO convention is used, i.e. Monday is 1, Sunday is 7. AppleScript day names (Sunday, Monday...) may be used instead.

date object or cdn is either an AppleScript date object or a chronological Julian day number. If a date object is used, the function returns a date object. If a number is used, the function returns a number.

date_to_cdn ( date object )

Result: Integer
Purpose: Get the chronological Julian day number of date

Returns the chronological Julian day number of the date.

greg_to_cdn( Year, Month, Day )

Result: integer
Purpose: Convert a date given as three values to a chronological Julian day number, exceeding the limits of the AppleScript date object.

Arguments are integers.
Valid ranges:
1 <= Month <= 12
1 <= day <= 31
Values out of that range are rounded to the closest value. Day values spill over, i.e. February 29 of a common year will yield the same result as March 1.

The function returns the chronological Julian day number for a date in the Gregorian calender, given as year, month and day.

Since it is more plausible for computations, we allow for a year 0. That is: 0 stands for year 1 BCE, -1 for year 2 BCE and so on. (For the Gregorian calendar, that notation is consistent with ISO.)

cdn_to_date( day_number )

Result: AS date object
Purpose: Convert a chronological Julian day number to a date

Argumen is integer. If day_number is a float, it will be rounded.
Range:
day_number >= 1721426
(That is January 1 0001).

Returns a date object for the given chronologiace Julian day number.

cdn_to_greg( day_number )

Result: Record
Purpose: Converting a chronological Julian day number to a date record, exceeding the time limitation of AppleScript date objects

day_number is an integer.
If day number is a float, it will be rounded.

Returns a record with the elements year, month and day.

Year in the result record uses the ISO concept allowing for a year 0. That is: Year -99 in this numbering scheme is the same as Year 100 BCE in the conventional system.

julian_to_cdn( Year_ad, Month, Day )

Result: Integer
Purpose: Find the chronological Julian day number for a date in Julian calendar.

Arguments are integers with
1 <= Month <= 12
1 <= day <= 31
Values out of that range are rounded to the closest value.

The function returns the chronological Julian day number for a date in the Julian calender, given as year anno domini, month and day.

Since it is more plausible for computations, we allow for a year 0. That is: 0 stands for year 1 BCE, -1 for year 2 BC and so on.Day values spill over, i.e. February 29 of a common year will yield the same result as March 1.

cdn_to_julian( day_number )

Result: Record {year, month, day}
Purpose: Get a date description in Julian calendar from chronological Julian day number

The argument is an integer.
Valid range:
day_number ? 0

day_number is the requested chronological julian day number. Negative arguments raise an error. The result is a record with the components year, month and day, an integer each.

Years BC are returned as 0 or negative values. Hence "0" means year 1 BC, "-1" means year 2 BC…

easter_greg_date ( Year_ad )

Result: AS date object
Purpose: Find gregorian Easter date for a Year

The argument Year is an integer

Valid range:
Year_ad >= 1
Lower values return an error, floating point numbers are rounded.

For a year, given as an integer, the function returns the date of easter in that year as an AS date object. The code is heavily based on Dershowitz/Reingold.

easter_greg_cdn( Year_ad )

Result: Integer
requires: greg_to_cdn
Purpose: Finding the Gregorian Easter date for Year as a Chronological Julian Day Number

Basically the same code as easter_greg_date, but the function does not return an AppleScript date object. Rather it works within the concept of Chronologial Julian day numbers.

Argument is an integer.
Floating point numbers are rounded.

easter_jul_date ( Year_ad )

Result: AS date object
requires julian_to_cdn, cdn_to_date
Purpose: Find the date of Julian (basically: Orthodox) Easter in Gregorian calendar.
Requires: julian_to_cdn, cdn_to_date

Argument is an integer. Floating point numbers will be rounded.
Valid range:
Year_ad >= 1

Returns an AppleScript date object for the orthodox Easter date of the given year anno domini. Properties of that date object belong to the gregorian calendar, just as in any AS date object.

easter_jul_cdn( Year_ad )

Result: Integer
requires julian_to_cdn
Purpose: Finding the Julian Easter date for Year_ad as a Chronological Julian Day Number

Argument is an integer. Floating point numbers will be rounded.
Valid range:
Year_ad >= 1

Basically the same code as easter_jul_date, but the function does not return an AppleScript date object. Rather it works within the concept of Chronologial Julian day numbers.

rosh_hashanah_cdn( Year_am )

Result: Integer
Purpose: Find chronological Julian day number of Tishri 1 of year anno mundi.

The argument is an integer (year number in the Hebrew calendar).
Valid range:
Year_am >= 1

The function returns the chronological Julian day number of the day the year starts.

(Note: Year AM = Year AD + 3760 for days from January 1 to Elul 29. Year AM = Year AD + 3761 for days from Tishri 1 to Decembre 31.)

heb_to_cdn( Year_am, Month, Day )

Result: Integer
requires rosh_hashanah_cdn
Purpose: Convert a Hebrew date to a chronolocial Julian day number

all arguments are integers in the following valid ranges:

Year_am >= 1
1 <= Month <= 13
1 <= Day <= 30
Arguments out of these ranges raise an exception.

Year_am is the anno mundi year of the Hebrew date. Month is the month counting from Nisan as 1. Day is the day number within the month.

Month and leap year behaviour: Month 12 is Adar in common years, Adar I in leap years. Month 13 is Adar in common years, Adar II in leap years.

Spill over behaviour of Day: In months having 29 days, 30 will yield the 1. day of the following month. Example: Kislev 30 will yield Tevet 1 in deficient years. Adar Rishon 30 will yield Nisan 1 in common years.

cdn_to_heb( day number )

Result: Record of year, month and day
requires rosh_hashanah_cdn
Purpose: Convert a chronological Julian Day Number to a Hebrew date

Argument is integer.
Valid range:
day number >= 347998 (epoch of the Hebrew calendar)

Returns a record with the elements year, month and day, representing the date in the Hebrew calendar. For months, Nisan is counted as 1, Adar Sheni in leap years is 13.

is_leap_greg ( Year )

Result: boolean
Purpose: Test, if a given year is a leap year according to Gregorian rules

The argument is an integer, giving the year. The result is true if that year is a leap year, otherwise it is false.

is_leap_jul ( Year )

Result: boolean
Purpose: Test, if a given year is a leap year according to Julian rules

The argument is an integer, giving the year. The result is true if that year is a leap year, otherwise it is false.

is_leap_heb ( Year )

Result: boolean
Purpose: Test, if a given year is a leap year according to Hebrew calender rules

The argument is an integer >= 1, giving the year anno mundi. (Floats will be rounded.) The result is true if that year is a leap year, otherwise it is false.

_______________________________

Notes

Ranges in newDate / newDateTime ("spill over")

When setting integer properties of AS date objects, several values are possible that look strange on the first view.

year: Possible dates are limited to January 1 1000 through Decembre 31 9999 when entering a date in the script editor. This is rather a behaviour of the parser when compiling the script. By setting the year property numerically, values from 1 to 32767 (i.e. 2^15 - 1) are possible. (Note: You may set a year 0, but behaviour is strange.)

month: month actually is a constant, not an integer. Behaviour is pretty strange when values beyond 12 are set (actually, 13 seems to behave as you would expect, but testing has been very limited).

day: Days spill over as expected in the range 1 - 127. For higher values, things get strange. It seems that the highest 8 bits if the integer are intepreted in a two's complement fashion: 256 behaves like 0 and yields the last day of the previous month, 255 is -1 and yields two days before... I am not aware of any technical documentation specifying this, so be very careful when using this "feature".

hour: Same two's complement behaviour as day. I.e. behaviour is reasonable in the range 0 to 127.

minute: Same as hour

second: Range is 0 to 32767

Chronological Julian Day numbers

"Julian Dates" count time in days elapsed since noon January 1 4713 BC of the Julian Calendar (= November 24 4714 BCE). When used as a calender, these values are normalized to midnight, resulting in values having a ".5" decimal value. To simplify matters for calendrical calculations, the concept of a chronological day number has been added, using integer values.

These functions use Chronological Julian Day numbers as an intermediate calender. This allows conversion of dates between any two implemented calenders.