historical_time_series.js

const sevenDays = (7 * 24 * 60 * 60 * 1000);
const daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
const daysOfMonth = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

function advanceToDayOfWeek(date, dayOfWeek) {
  var pubDate = new Date(date.getTime());
  while (dayOfWeek && daysOfWeek[pubDate.getUTCDay()] !== dayOfWeek) {
    pubDate.setUTCDate(pubDate.getUTCDate() + 1);
  }
  return pubDate;
}

const periods = {
  'weekly': (date) => date.setTime(date.getTime() + sevenDays),
  'bimonthly': (date) => date.setUTCMonth(date.getUTCMonth() + 2),
  'monthly': (date) => (date.getUTCDate() !== 1) ? date.setUTCDate(1) : date.setUTCMonth(date.getUTCMonth() + 1),
  'biweekly': function (date) {
    var i = (date.getUTCDate() === 1) ? 15 : 1;
    date.setUTCDate(i);
    if (i === 1) {
      date.setUTCMonth(date.getUTCMonth() + 1);
    }
  }
};

function formatDate(pubDate, format) {
  var monthYear = daysOfMonth[pubDate.getUTCMonth()] + " " + pubDate.getUTCFullYear();
  if (format === 'full') {
    return daysOfWeek[pubDate.getUTCDay()] + " " + pubDate.getUTCDate() + " " + monthYear;
  }
  return monthYear;
}

// TODO: Seasonal, volume/issues?

/**
 * Create a date series for periodicals, in the past.
 *
 * @param [params] {Object} params
 *      @param [params.period='weekly'] {String} one of 'weekly', 'monthly', 'biweekly' or 'bimonthly'
 *      @param [params.dayOfWeek] {String} one of 'Saturday' to 'Sunday', if you want the dates to be
 *          adjusted to the next day of the week (for example if a periodical is always published on Mondays).
 *      @param [params.startDate='1956-01-01'] {String|Date} a string (in format 'YYYY-MM-DD') or a date object that
 *          is the date on which the sequence will start.
 *      @param [params.endDate='1958-07-14'] {String|Date} a string (in format 'YYYY-MM-DD') or a date object that
 *          is the date before which the sequence will end.
 * @return {Array} an array of string dates in the format 'Monday 2 Jan 1956'.
 */
export function timeSeries({
    period = 'weekly',
    dayOfWeek,
    format = 'full',
    startDate = '1956-01-01',
    endDate = '1958-07-14'
  }) {
  period = periods[period];
  if (!period) {
    throw new Error('Invalid period: ' + period);
  }
  startDate = new Date(startDate);
  endDate = new Date(endDate);

  let dateStrings = [];
  for (let date = startDate; date < endDate; period(date)) {
    // pub date moves the date to a day of the week, but keeps calculating using the existing date.
    let pubDate = advanceToDayOfWeek(date, dayOfWeek);
    if (pubDate < endDate) {
      let dateString = formatDate(pubDate, format);
      dateStrings.push(dateString);
    }
  }
  return dateStrings;
}