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) {
const 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) {
const i = date.getUTCDate() === 1 ? 15 : 1;
date.setUTCDate(i);
if (i === 1) {
date.setUTCMonth(date.getUTCMonth() + 1);
}
},
};
function formatDate(pubDate, format) {
const monthYear = daysOfMonth[pubDate.getUTCMonth()] + " " + pubDate.getUTCFullYear();
if (format === "full") {
return daysOfWeek[pubDate.getUTCDay()] + " " + pubDate.getUTCDate() + " " + monthYear;
}
return monthYear;
}
// TODO: Seasonal, volume/issues?
/**
* Generates a series of publication dates for a periodical, within a historical date range.
* Useful for assigning realistic issue dates to magazines and newspapers.
*
* @param {Object} options - Time series options
* @param {string} [options.period="weekly"] - Publication frequency: "weekly", "biweekly",
* "monthly", or "bimonthly".
* @param {string} [options.dayOfWeek] - Advance each date to the next occurrence of this day
* (e.g. "Monday"). If not provided, dates are not adjusted to a specific weekday.
* @param {string} [options.format="full"] - Date format: "full" produces "Monday 2 Jan 1956";
* any other value produces "Jan 1956".
* @param {string|Date} [options.startDate="1956-01-01"] - Start of the date range, as a
* "YYYY-MM-DD" string or Date object.
* @param {string|Date} [options.endDate="1958-07-14"] - End of the date range (exclusive), as a
* "YYYY-MM-DD" string or Date object.
* @returns {string[]} An array of formatted date strings within the given range
*/
export function createTimeSeries({
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);
const 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.
const pubDate = advanceToDayOfWeek(date, dayOfWeek);
if (pubDate < endDate) {
const dateString = formatDate(pubDate, format);
dateStrings.push(dateString);
}
}
return dateStrings;
}