Time Periods

Supported time periods for building schedules and performing time based calculations.

Overview

Time periods are the crux of the Later library and are used to define new schedules. Later comes with a large assortment of time periods and is also fully extensible making it easy to create custom time periods.

While time periods are primarily used by Later to define schedules and calculate occurrences, they are also useful for performing time based calculations. Calculating values such as ISO week number, moving between days of the year, or figuring out how many days are in a month are all possible using the time period interface.

If you don't see the time period that you need for your schedule, Later is fully extensible and it is easy to write your own. See the custom time period at the bottom of this page for an example.

Interface

All time periods implement the same public interface for interacting with them:

name
The name of the time period.
range
The rough number of seconds that are covered when moving from one instance of this time period to the next instance.
val(date)
The value of this time period for the date specified.
isValid(date, value)
True if the specified value is valid for the specified date, false otherwise.
extent(date)
The minimum and maximum valid values for the time period for the specified date. If the minimum value is not 0, 0 can be specified in schedules to indicate the maximum value. This makes working with non-constant extents (like days in a month) easier.
start(date)
The first second in which the value is the same as the value of the specified date. For example, the start of an hour would be the hour with 0 minutes and 0 seconds.
end(date)
The last second in which the value is the same as the value of the specified date. For example, the end of an hour would be the hour with 59 minutes and 59 seconds.
next(date, value)
Returns the next date where the value is the value specified. Sets the value to 1 if value specified is greater than the max allowed value.
prev(date, value)
Returns the previous date where the value is the value specified. Sets the value to the max allowed value if the value specified is greater than the max allowed value.

Second (second, s)

Seconds in a minute, from 0 to 59.


Using seconds in a schedule:

  var sched = {schedules: [{s: [0, 15, 30, 45]}]};

Performing seconds based calculations:

  var d = new Date('2013-03-22T10:02:05Z');

  later.second.name;
  --> 'second'

  later.second.range;
  --> 1

  later.second.val(d);
  --> 5

  later.second.isValid(d, 10);
  --> false

  later.second.extent();
  --> [0, 59]

  later.second.start(d);
  --> 'Fri, 22 Mar 2013 10:02:05 GMT'

  later.second.end(d);
  --> 'Fri, 22 Mar 2013 10:02:05 GMT'

  later.second.next(d, 27);
  --> 'Fri, 22 Mar 2013 10:02:27 GMT'

  later.second.prev(d, 27);
  --> 'Fri, 22 Mar 2013 10:01:27 GMT'

Minute (minute, m)

Minutes in an hour, from 0 to 59.


Using minutes in a schedule:

  var sched = {schedules: [{m: [0, 15, 30, 45]}]};

Performing minutes based calculations:

  var d = new Date('2013-03-22T10:02:05Z');

  later.minute.name;
  --> 'minute'

  later.minute.range;
  --> 60

  later.minute.val(d);
  --> 2

  later.minute.isValid(d, 2);
  --> true

  later.minute.extent();
  --> [0, 59]

  later.minute.start(d);
  --> 'Fri, 22 Mar 2013 10:02:00 GMT'

  later.minute.end(d);
  --> 'Fri, 22 Mar 2013 10:02:59 GMT'

  later.minute.next(d, 27);
  --> 'Fri, 22 Mar 2013 10:27:00 GMT'

  later.minute.prev(d, 27);
  --> 'Fri, 22 Mar 2013 09:27:59 GMT'

Hour (hour, h)

Hours in a day, from 0 to 23.


Using hours in a schedule:

  var sched = {schedules: [{h: [0, 5, 12]}]};

Performing hours based calculations:

  var d = new Date('2013-03-22T10:02:05Z');

  later.hour.name;
  --> 'hour'

  later.hour.range;
  --> 3600

  later.hour.val(d);
  --> 10

  later.hour.isValid(d, 2);
  --> false

  later.hour.extent();
  --> [0, 23]

  later.hour.start(d);
  --> 'Fri, 22 Mar 2013 10:00:00 GMT'

  later.hour.end(d);
  --> 'Fri, 22 Mar 2013 10:59:59 GMT'

  later.hour.next(d, 5);
  --> 'Sat, 23 Mar 2013 05:00:00 GMT'

  later.hour.prev(d, 21);
  --> 'Thu, 21 Mar 2013 21:59:59 GMT'

Time (time, t)

Time of day, represented as seconds since midnight. From 0 to 86399.


Using time in a schedule:

  var sched = {schedules: [{t: [6500]}]};

Performing time based calculations:

  var d = new Date('2013-03-22T10:02:05Z');

  later.time.name;
  --> 'time'

  later.time.range;
  --> 1

  later.time.val(d);
  --> 36125

  later.time.isValid(d, 36125);
  --> true

  later.time.extent();
  --> [0, 86399]

  later.time.start(d);
  --> 'Fri, 22 Mar 2013 00:00:00 GMT'

  later.time.end(d);
  --> 'Fri, 22 Mar 2013 23:59:59 GMT'

  later.time.next(d, 60);
  --> 'Sat, 23 Mar 2013 00:01:00 GMT'

  later.time.prev(d, 60);
  --> 'Fri, 22 Mar 2013 00:01:00 GMT'

Day (day, D)

Days of a month, from 1 to max days in month. Specify 0 for the last day of the month.


Using days in a schedule:

  var sched = {schedules: [{D: [0]}]};

Performing day based calculations:

  var d = new Date('2013-03-22T10:02:05Z');

  later.day.name;
  --> 'day'

  later.day.range;
  --> 86400

  later.day.val(d);
  --> 22

  later.day.isValid(d, 3);
  --> false

  later.day.extent(d);
  --> [1, 31]

  later.day.start(d);
  --> 'Fri, 22 Mar 2013 00:00:00 GMT'

  later.day.end(d);
  --> 'Fri, 22 Mar 2013 23:59:59 GMT'

  later.day.next(d, 11);
  --> 'Thu, 11 Apr 2013 00:00:00 GMT'

  later.day.prev(d, 2);
  --> 'Sat, 02 Mar 2013 23:59:59 GMT'

Day of week (dayOfWeek, dw, d)

Days of a week, from 1 to 7. Specify 0 for the last day of the week (Saturday).


Using days of week in a schedule:

  var sched = {schedules: [{dw: [2,3,4,5,6]}]};

Performing day of week based calculations:

  var d = new Date('2013-03-22T10:02:05Z');

  later.dayOfWeek.name;
  --> 'day of week'

  later.dayOfWeek.range;
  --> 86400

  later.dayOfWeek.val(d);
  --> 6

  later.dayOfWeek.isValid(d, 3);
  --> false

  later.dayOfWeek.extent();
  --> [1, 7]

  later.dayOfWeek.start(d);
  --> 'Fri, 22 Mar 2013 00:00:00 GMT'

  later.dayOfWeek.end(d);
  --> 'Fri, 22 Mar 2013 23:59:59 GMT'

  later.dayOfWeek.next(d, 1);
  --> 'Sun, 24 Mar 2013 00:00:00 GMT'

  later.dayOfWeek.prev(d, 5);
  --> 'Thu, 21 Mar 2013 23:59:59 GMT'

Day of week count (dayOfWeekCount, dc)

The nth day of the week within a month, from 1 to max weeks in a month. Specify 0 for the last day instance. Used together with the day of the week time period to specify things like the 2nd Tuesday or last Friday of a month.


Using days of week count in a schedule:

  var sched = {schedules: [{dc: [2]}]};

Performing day of week count based calculations:

  var d = new Date('2013-03-22T10:02:05Z');

  later.dayOfWeekCount.name;
  --> 'day of week count'

  later.dayOfWeekCount.range;
  --> 604800

  later.dayOfWeekCount.val(d);
  --> 4

  later.dayOfWeekCount.isValid(d, 4);
  --> true

  later.dayOfWeekCount.extent(d);
  --> [1, 5]

  later.dayOfWeekCount.start(d);
  --> 'Fri, 22 Mar 2013 00:00:00 GMT'

  later.dayOfWeekCount.end(d);
  --> 'Thu, 28 Mar 2013 23:59:59 GMT'

  // zero is special cased and means the last instance of
  // a day of the week in the month, instead of meaning the
  // first day of the week with the highest instance count
  // which would have been Mar 29 with value 5.
  later.dayOfWeekCount.next(d, 0);
  --> 'Mon, 25 Mar 2013 00:00:00 GMT'

  later.dayOfWeekCount.prev(d, 2);
  --> 'Thu, 14 Mar 2013 23:59:59 GMT'

Day of year (dayOfYear, dy)

Days in a year, from 1 to max days in year. Specify 0 for last day of the year.


Using days of year in a schedule:

  var sched = {schedules: [{dy: [189, 267]}]};

Performing day of year based calculations:

  var d = new Date('2013-03-22T10:02:05Z');

  later.dayOfYear.name;
  --> 'day of year'

  later.dayOfYear.range;
  --> 86400

  later.dayOfYear.val(d);
  --> 81

  later.dayOfYear.isValid(d, 4);
  --> false

  later.dayOfYear.extent(d);
  --> [1, 365]

  later.dayOfYear.start(d);
  --> 'Fri, 22 Mar 2013 00:00:00 GMT'

  later.dayOfYear.end(d);
  --> 'Fri, 22 Mar 2013 23:59:59 GMT'

  later.dayOfYear.next(d, 256);
  --> 'Fri, 13 Sep 2013 00:00:00 GMT'

  later.dayOfYear.prev(d, 44);
  --> 'Wed, 13 Feb 2013 23:59:59 GMT'

Week of month (weekOfMonth, wm)

Weeks in a month where the 1st of the month is week 1 and following weeks start on Sunday. From 1 to max weeks in the month. Specify 0 for last week of the month.


Using weeks of month in a schedule:

  var sched = {schedules: [{wm: [1, 2]}]};

Performing week of month based calculations:

  var d = new Date('2013-03-22T10:02:05Z');

  later.weekOfMonth.name;
  --> 'week of month'

  later.weekOfMonth.range;
  --> 604800

  later.weekOfMonth.val(d);
  --> 4

  later.weekOfMonth.isValid(d, 4);
  --> true

  later.weekOfMonth.extent(d);
  --> [1, 6]

  later.weekOfMonth.start(d);
  --> 'Sun, 17 Mar 2013 00:00:00 GMT'

  later.weekOfMonth.end(d);
  --> 'Sat, 23 Mar 2013 23:59:59 GMT'

  later.weekOfMonth.next(d, 1);
  --> 'Mon, 01 Apr 2013 00:00:00 GMT'

  later.weekOfMonth.prev(d, 2);
  --> 'Sat, 09 Mar 2013 23:59:59 GMT'

ISO Week of year (weekOfYear, wy)

The ISO 8601 week of the year. From 1 to max ISO week in the year. Specify 0 for last ISO week of the year.


Using weeks of year in a schedule:

  var sched = {schedules: [{wy: [13,26,39,0]}]};

Performing week of year based calculations:

  var d = new Date('2013-03-22T10:02:05Z');

  later.weekOfYear.name;
  --> 'week of year'

  later.weekOfYear.range;
  --> 604800

  later.weekOfYear.val(d);
  --> 12

  later.weekOfYear.isValid(d, 21);
  --> false

  later.weekOfYear.extent(d);
  --> [1, 52]

  later.weekOfYear.start(d);
  --> 'Mon, 18 Mar 2013 00:00:00 GMT'

  later.weekOfYear.end(d);
  --> 'Sun, 24 Mar 2013 23:59:59 GMT'

  later.weekOfYear.next(d, 47);
  --> 'Mon, 18 Nov 2013 00:00:00 GMT'

  later.weekOfYear.prev(d, 52);
  --> 'Sun, 30 Dec 2012 23:59:59 GMT'

Month (month, M)

Months of the year, from 1 to 12. Specify 0 for the last month of the year.


Using months in a schedule:

  var sched = {schedules: [{M: [3,5,7]}]};

Performing months based calculations:

  var d = new Date('2013-03-22T10:02:05Z');

  later.month.name;
  --> 'month'

  later.month.range;
  --> 2629740

  later.month.val(d);
  --> 3

  later.month.isValid(d, 3);
  --> true

  later.month.extent();
  --> [1, 12]

  later.month.start(d);
  --> 'Fri, 01 Mar 2013 00:00:00 GMT'

  later.month.end(d);
  --> 'Sun, 31 Mar 2013 23:59:59 GMT'

  later.month.next(d, 11);
  --> 'Fri, 01 Nov 2013 00:00:00 GMT'

  later.month.prev(d, 2);
  --> 'Thu, 28 Feb 2013 23:59:59 GMT'

Year (year, Y)

Years, from 1970 to 2099.


Using years in a schedule:

  var sched = {schedules: [{Y: [2013, 2014, 2015]}]};

Performing years based calculations:

  var d = new Date('2013-03-22T10:02:05Z');

  later.year.name;
  --> 'year'

  later.year.range;
  --> 31556900

  later.year.val(d);
  --> 2013

  later.year.isValid(d, 2013);
  --> true

  later.year.extent();
  --> [1970, 2099]

  later.year.start(d);
  --> 'Tue, 01 Jan 2013 00:00:00 GMT'

  later.year.end(d);
  --> 'Tue, 31 Dec 2013 23:59:59 GMT'

  later.year.next(d, 2014);
  --> 'Wed, 01 Jan 2014 00:00:00 GMT'

  later.year.prev(d, 2012);
  --> 'Mon, 31 Dec 2012 23:59:59 GMT'

Writing a custom time period

Later is fully extensible and it is easy to create your own custom time periods that can be used to define new schedules. To keep things simple, we'll walk through creating a new time period for indicating morning, afternoon, and evening. For our purposes, morning will be before noon and have a value of 0, afternoon will be before 6pm and have a value of 1, and evening will be before midnight and have a value of 2.


The first step is to create a name and id for the modifier and add it to the later namespace.

  later.partOfDay = later.pd = {
    // interface implementation goes here
  };

Next, we need to implement the time period interface. First we will just specify the name of this time period.

  name: 'part of day',

The range is approximately 6 hours. Though some of our periods are longer and some shorter, we'll use the shortest range which is afternoon at 6 hours.

  range: later.h.range * 6,

We then implement val to return the appropriate value based on the definition described previously.

  val: function(d) {
    return later.h.val(d) < 12 ? 0 :
           later.h.val(d) < 6 ? 1 :
           2;
  },

Then we can use our new val function to implement isValid.

  isValid: function(d, val) {
    return later.pd.val(d) === val;
  },

The extent is always going to be the same for every day so we can just return a constant array here.

  extent: function(d) { return [0, 2]; },

Next we need to implement start and end based on the current time period. This will be the start and end of each part of the day that we've defined.

  start: function(d) {
    var hour = later.pd.val(d) === 0 ? 0 :
                  later.pd.val(d) === 1 ? 12 :
                  6;

    // later.date.next is a helper function for creating the date in UTC or
    // localTime as appropriate
    return later.date.next(
      later.Y.val(d),
      later.M.val(d),
      later.D.val(d),
      hour
    );
  },

  end: function(d) {
    var hour = later.pd.val(d) === 0 ? 11 :
                  later.pd.val(d) === 1 ? 5 :
                  23;

    // later.date.prev is a helper function for creating the date in UTC or
    // localTime as appropriate, and automatically adjusts the date to be at
    // the last second of the specified time
    return later.date.prev(
      later.Y.val(d),
      later.M.val(d),
      later.D.val(d),
      hour
    );
  },

Finally, we need to implement next and prev so that you can move to different parts of the day. We need to make sure to increment and decrement the day appropriately if we've already passed the specified value.

  next: function(d, val) {
    var hour = val === 0 ? 0 : val === 1 ? 12 : 18;

    return later.date.next(
      later.Y.val(d),
      later.M.val(d),
      // increment the day if we already passed the desired time period
      later.D.val(d) + (hour < later.h.val(d) ? 1 : 0),
      hour
    );
  },

  prev: function(d, val) {
    var hour = val === 0 ? 11 : val === 1 ? 5 : 23;

    return later.date.prev(
      later.Y.val(d),
      later.M.val(d),
      // decrement the day if we already passed the desired time period
      later.D.val(d) + (hour > later.h.val(d) ? -1 : 0),
      hour
    );
  }

Full implementation

Here is the code for the completed example. To use the time period, just add this code after including Later into your project and before you use it in any schedules.

  later.partOfDay = later.pd = {

    name: 'part of day',

    range: later.h.range * 6,

    val: function(d) {
      return later.h.val(d) < 12 ? 0 :
             later.h.val(d) < 18 ? 1 :
             2;
    },

    isValid: function(d, val) {
      return later.pd.val(d) === val;
    },

    extent: function(d) { return [0, 2]; },

    start: function(d) {
      var hour = later.pd.val(d) === 0 ? 0 :
                    later.pd.val(d) === 1 ? 12 :
                    18;

      return later.date.next(
        later.Y.val(d),
        later.M.val(d),
        later.D.val(d),
        hour
      );
    },

    end: function(d) {
      var hour = later.pd.val(d) === 0 ? 11 :
                    later.pd.val(d) === 1 ? 5 :
                    23;

      return later.date.prev(
        later.Y.val(d),
        later.M.val(d),
        later.D.val(d),
        hour
      );
    },

    next: function(d, val) {
      var hour = val === 0 ? 0 : val === 1 ? 12 : 18;

      return later.date.next(
        later.Y.val(d),
        later.M.val(d),
        // increment the day if we already passed the desired time period
        later.D.val(d) + (hour < later.h.val(d) ? 1 : 0),
        hour
      );
    },

    prev: function(d, val) {
      var hour = val === 0 ? 11 : val === 1 ? 5 : 23;

      return later.date.prev(
        later.Y.val(d),
        later.M.val(d),
        // decrement the day if we already passed the desired time period
        later.D.val(d) + (hour > later.h.val(d) ? -1 : 0),
        hour
      );
    }
  };

Usage

Using the custom time period is exactly the same as using a built-in time period.

  // use our new time period to specify every 15 mins at night
  var sched = later.parse.recur().every(15).minute().on(2).customPeriod('pd'),
      next = later.schedule(sched).next(1, new Date(2013, 3, 21));

  console.log(next.toUTCString());
  --> Sun, 21 Apr 2013 18:00:00 GMT