Date
All of the Omni applications incorporate the use of dates, especially OmniFocus and OmniPlan. The following materials demonstrate how to generate and manipulate the JavaScript date objects that are used by Omni Automation to describe points in time.
Deriving Dates Relative to Date
A common practice in scripts is to create JavaScript date objects that represent future dates relative to an origin date, such as today.
The following details three commonly used techniques for deriving such date objects.
1) Using JavaScript
The JavaScript language has built-in methods for creating and manipulating instances of its Date class (date objects).
This example Omni Automation script uses the built-in JavaScript methods to generate a date object that references an entry that occurs on the 20th of next month at 1:00PM
Using JavaScript Date Functions
// next month on the 20th at 1PM
d = new Date()
dateObj = new Date(d.getFullYear(), d.getMonth() + 1, 20, 13)
2) Using DateComponents Class
The DateComponents class (see details in section below) and its functions are included in the Omni Automaton Calendar class, which is shared by all Omni applications.
Using DateComponents you can avoid the complex nested date calculations inherent in the built-in JavaScript date handling. The use of DateComponents may require more statements, but is clearer to visualize, as in the following example that generates a date object referencing a time three days from today at 10:30AM
Using DateComponents Class and Functions
// in three days at 10:46AM
now = new Date()
today = Calendar.current.startOfDay(now)
dc = new DateComponents
dc.day = 3
dc.hour = 10
dc.minute = 46
dc.second = 0
dateObj = Calendar.current.dateByAddingDateComponents(today, dc)
3) Using Formatter Class Date Interpreters
In Omni applications, date entry fields support the entering of shortcut date terminology, such as the words: “today” or “tomorrow” or shorthand values like: “+3d” or “3 days”
The value for a date input field can written using any syntax supported by an OmniOutliner date field. For example:
- 2d, –3w, 1h, 1y1m, and so on • Relative dates and times put the date at a certain amount of time from right now. Negative numbers represent times in the past.
- 2 days, –3 weeks, 1 hour, 1 year 1 month, and so on • You can use the full names of units too.
- yesterday, tomorrow, tonight, next thursday, last month, this friday, and so on • You can refer to relative dates using common words. “This”, “next”, and “last” have specific meanings: this friday always means the Friday in this week, next friday always means the Friday in the next week, and last friday always means the Friday in last week, regardless of what day today is. Other units work in the same way.
- september, fri, 2019, and so on • If you enter the name of a specific time period, the date will be at its beginning. So september means September first.
- 5/23/08 10a, 9.30.09 2:00 PM, and so on • You can use the short date format as defined in your Language & Region system preferences.
- 2w sat, 4d @ 5p, mon 6a, aug 6 tue 5p, and so on • Mix the available formats however you like.
- now, 9, 14:00, tom, and so on • Omni’s date parser makes its best guess at things like bare numbers, times, and word fragments.
Using an instance of the shared Formatter.Date class, you can convert those supported shorthand terms into date objects for use in scripts!
For example, the following script shows how to create a date object for three days from today at 9:30AM
Using Formatter Class and Functions
// in three days at 9:30AM
fmtr = Formatter.Date.withStyle(Formatter.Date.Style.Short)
dateObj = fmtr.dateFromString('3d @ 9:30a')
DateComponents Class
The DateComponents class represents the elements that comprise a date, such as day, hour, year, and minute.
Constructors
new DateComponents() → (DateComponents) • Creates a new instance of the DateComponents class.
Instance Properties
The properties of an instance of the DateComponents class:
date (Date or null) • The date object of the component
day (Number or null) • The number of days in the component
era (Number or null) • The number of eras in the component
hour (Number or null) • The number of hours in the component
minute (Number or null) • The number of minutes in the component
month (Number or null) • The number of months in the component
nanosecond (Number or null) • The number of nanoseconds in the component
second (Number or null) • The number of seconds in the component
timeZone (TimeZone or null) • The time zone of the component
year (Number or null) • The year of the component
Script examples using the DateComponents class of the Calendar class instance functions:
Date Components from Date
date = new Date()
dc = Calendar.current.dateComponentsFromDate(date)
dc.month + "/" + dc.day + "/" + dc.year
//--> 8/23/21
Date from Components
dc = new DateComponents()
dc.month = 12
dc.day = 31
dc.year = 2021
date = Calendar.current.dateFromDateComponents(dc)
//--> Fri Dec 31 2021 00:00:00 GMT-0800 (PST)
Components Between Dates
startDate = new Date("8/1/2021")
endDate = new Date("10/15/2021")
result = Calendar.current.dateComponentsBetweenDates(startDate, endDate)
console.log(result.month)
//--> 2
console.log(result.day)
//--> 14
Creating a relative date object 45 days and 17 hours from the start of today:
Generate Relative Future Date/Time
now = new Date()
today = Calendar.current.startOfDay(now)
console.log(today)
//--> Wed Oct 14 2020 00:00:00 GMT-0700 (PDT)
duration = new DateComponents()
duration.day = 45
duration.hour = 17
targetDate = Calendar.current.dateByAddingDateComponents(today, duration)
console.log(targetDate)
//--> Sat Nov 28 2020 17:00:00 GMT-0800 (PST)
A function for retrieving all occurrences of a specified weekday in a specified month. In JavaScript, the index of a weekday is an integer from 0 (Sunday) to 6 (Saturday).
Get All Occurrences of Weekday in Month
function weekdayOccurrencesInMonth(weekdayIndex, monthIndex, yearIndex){
cal = Calendar.current
dc = new DateComponents()
dc.day = 1
dc.month = monthIndex
dc.year = yearIndex
monthLength = new Date(yearIndex, monthIndex, 0).getDate()
matchedDates = new Array()
for (var i = 1; i < (monthLength + 1); i++) {
dc.day = i
d = cal.dateFromDateComponents(dc)
if (d.getDay() === weekdayIndex){matchedDates.push(d)}
}
return matchedDates
}
Using the function to get the 2nd Monday of May 2021:
2nd Monday of May 2021
secondMonday = weekdayOccurrencesInMonth(1, 5, 2021)[1]
console.log(secondMonday)
Date Comparison Functions
The properties and functions of the DateComponents class can be used to create comparison functions that check whether the provided date object falls on a specific date or range.
A function that returns true if the provided date/time takes place today:
Does this take place today?
function dateOccursToday(dateToCheck){
cal = Calendar.current
now = new Date()
midnightToday = cal.startOfDay(now)
dc = cal.dateComponentsFromDate(midnightToday)
dc.day = dc.day + 1
midnightTomorrow = cal.dateFromDateComponents(dc)
return ( dateToCheck >= midnightToday && dateToCheck < midnightTomorrow)
}}
A function that returns true if the provided date/time took place yesterday:
Did this take place yesterday?
function dateOccurredYesterday(dateToCheck){
cal = Calendar.current
now = new Date()
midnightToday = cal.startOfDay(now)
dc = cal.dateComponentsFromDate(midnightToday)
dc.day = dc.day - 1
midnightYesterday = cal.dateFromDateComponents(dc)
return ( dateToCheck >= midnightYesterday && dateToCheck < midnightToday)
}
A function that returns true if the provided date/time takes place tomorrow:
Does this take place tomorrow?
function dateOccursTomorrow(dateToCheck){
cal = Calendar.current
now = new Date()
midnightToday = cal.startOfDay(now)
dc = cal.dateComponentsFromDate(midnightToday)
dc.day = dc.day + 1
midnightTomorrow = cal.dateFromDateComponents(dc)
dc = cal.dateComponentsFromDate(midnightToday)
dc.day = dc.day + 2
dayAfterTomorrow = cal.dateFromDateComponents(dc)
return (dateToCheck >= midnightTomorrow && dateToCheck < dayAfterTomorrow)
}}
A function that returns true if the provided date/time takes place next week:
Does this take place next week?
function dateOccursNextWeek(dateToCheck){
fmatr = Formatter.Date.withStyle(Formatter.Date.Style.Short)
weekStart = fmatr.dateFromString('next week')
dc = new DateComponents()
dc.day = 7
followingWeek = Calendar.current.dateByAddingDateComponents(weekStart, dc)
return (dateToCheck >= weekStart && dateToCheck < followingWeek)
}
A function that returns true if the provided date/time takes place this month:
Does this take place this month?
function dateOccursThisMonth(dateToCheck){
cal = Calendar.current
currentMonthIndex = cal.dateComponentsFromDate(new Date()).month
targetMonthIndex = cal.dateComponentsFromDate(dateToCheck).month
return (targetMonthIndex === currentMonthIndex)
}
A function that returns true if the provided date/time takes place next month:
Does this take place next month?
function dateOccursNextMonth(dateToCheck){
cal = Calendar.current
dc = cal.dateComponentsFromDate(new Date())
dc.day = 1
dc.month = dc.month + 1
nextMonth = cal.dateFromDateComponents(dc)
nextMonthIndex = cal.dateComponentsFromDate(nextMonth).month
targetMonthIndex = cal.dateComponentsFromDate(dateToCheck).month
return (nextMonthIndex === targetMonthIndex)
}
A function that returns true if the provided date/time takes place on the provided target date:
Does this take place on target date?
function dateOccursOnTargetDate(dateToCheck, targetDate){
cal = Calendar.current
targetDateStart = cal.startOfDay(targetDate)
dc = cal.dateComponentsFromDate(targetDateStart)
dc.day = dc.day + 1
dayAfterTargetDate = cal.dateFromDateComponents(dc)
return ( dateToCheck >= targetDateStart && dateToCheck < dayAfterTargetDate)
}
Date Library
To provide easy access to the comparison functions from other scripts and plug-ins, the comparison functions have been placed into a single-file plug-in library file. This Omni Automation library can be called from within scripts or plug-ins for any of the Omni applications. TAP|CLICK the “Download Library” button to download the library plug-in archive.
The library, once installed, can be loaded and called in scripts using the following statements:
Calling the Date Library
libFile = PlugIn.find("com.omni-automation.all.date-library")
lib = libFile.library("all-date-library")
// lib.function-to-call()
lib.info()
The example library includes a function titled info() that returns the names and values of all of the library’s properties and functions. Adding this function to the libraries you create is a good practice, and provides users with a quick way to learn how to use your library.
Library Info Function
lib.info = function(){
libProps = Object.getOwnPropertyNames(lib)
libProps.forEach((propName, index) => {
if (index != 0){console.log(" ")}
console.log("•", propName)
item = lib[propName]
if (typeof item === "string"){
console.log(item)
} else if (typeof item === "function"){
console.log(item.toString())
} else if (typeof item === "object"){
if(item instanceof Version){
console.log(item.versionString)
} else if(item instanceof PlugIn){
console.log(item.identifier)
}
}
})
}
NOTE: In order to have the function code returned correctly when the library info() function is called, align your functions in the library to be flushed to the left side — removing some of preceding tabs:
The Date Comparison Library
/*{
"type": "library",
"targets": ["omnifocus","omnigraffle","omnioutliner","omniplan"],
"identifier": "com.omni-automation.all.date-library",
"author": "Otto Automator",
"version": "1.1",
"description": "A library of date functions."
}*/
(() => {
const lib = new PlugIn.Library(new Version("1.1"));
// returns true if provided date/time occurs today
lib.dateOccursToday = function(dateToCheck){
cal = Calendar.current
now = new Date()
midnightToday = cal.startOfDay(now)
dc = cal.dateComponentsFromDate(midnightToday)
dc.day = dc.day + 1
midnightTomorrow = cal.dateFromDateComponents(dc)
return (dateToCheck >= midnightToday && dateToCheck < midnightTomorrow)
}
// returns true if provided date/time took place yesterday
lib.dateOccurredYesterday = function(dateToCheck){
cal = Calendar.current
now = new Date()
midnightToday = cal.startOfDay(now)
dc = cal.dateComponentsFromDate(midnightToday)
dc.day = dc.day - 1
midnightYesterday = cal.dateFromDateComponents(dc)
return (dateToCheck >= midnightYesterday && dateToCheck < midnightToday)
}
// returns true if the provided date/time takes place tomorrow
lib.dateOccurrsTomorrow = function(dateToCheck){
cal = Calendar.current
now = new Date()
midnightToday = cal.startOfDay(now)
dc = cal.dateComponentsFromDate(midnightToday)
dc.day = dc.day + 1
midnightTomorrow = cal.dateFromDateComponents(dc)
dc = cal.dateComponentsFromDate(midnightToday)
dc.day = dc.day + 2
dayAfterTomorrow = cal.dateFromDateComponents(dc)
return (dateToCheck >= midnightTomorrow && dateToCheck < dayAfterTomorrow)
}
// returns true if the provided date/time takes place next week
lib.dateOccursNextWeek = function(dateToCheck){
fmatr = Formatter.Date.withStyle(Formatter.Date.Style.Short)
weekStart = fmatr.dateFromString('next week')
dc = new DateComponents()
dc.day = 7
followingWeek = Calendar.current.dateByAddingDateComponents(weekStart, dc)
return (dateToCheck >= weekStart && dateToCheck < followingWeek)
}
// returns true if the provided date/time takes place this month
lib.dateOccurrsThisMonth = function(dateToCheck){
cal = Calendar.current
currentMonthIndex = cal.dateComponentsFromDate(new Date()).month
targetMonthIndex = cal.dateComponentsFromDate(dateToCheck).month
return (targetMonthIndex === currentMonthIndex)
}
// returns true if the provided date/time takes place next month
lib.dateOccurrsNextMonth = function(dateToCheck){
cal = Calendar.current
dc = cal.dateComponentsFromDate(new Date())
dc.day = 1
dc.month = dc.month + 1
nextMonth = cal.dateFromDateComponents(dc)
nextMonthIndex = cal.dateComponentsFromDate(nextMonth).month
targetMonthIndex = cal.dateComponentsFromDate(dateToCheck).month
return (nextMonthIndex === targetMonthIndex)
}
// returns true if the provided date/time takes place on the provided target date
lib.dateOccursOnTargetDate = function(dateToCheck, targetDate){
cal = Calendar.current
targetDateStart = cal.startOfDay(targetDate)
dc = cal.dateComponentsFromDate(targetDateStart)
dc.day = dc.day + 1
dayAfterTargetDate = cal.dateFromDateComponents(dc)
return (dateToCheck >= targetDateStart && dateToCheck < dayAfterTargetDate)
}
lib.info = function(){
libProps = Object.getOwnPropertyNames(lib)
libProps.forEach((propName, index) => {
if (index != 0){console.log(" ")}
console.log("•", propName)
item = lib[propName]
if (typeof item === "string"){
console.log(item)
} else if (typeof item === "function"){
console.log(item.toString())
} else if (typeof item === "object"){
if(item instanceof Version){
console.log(item.versionString)
} else if(item instanceof PlugIn){
console.log(item.identifier)
}
}
})
}
return lib;
})();
Here’s an example script calling the library:
Call Date Comparison Library
libFile = PlugIn.find("com.omni-automation.all.date-library")
lib = libFile.library("all-date-library")
date = new Date(new Date().setHours(25, 15, 0))
if (lib.dateOccurrsTomorrow(date)){console.log("Date occurs tomorrow!")}