122 lines
3.8 KiB
JavaScript
122 lines
3.8 KiB
JavaScript
/**
|
|
* Given a width, return a function that takes a string, and
|
|
* pads it at both ends to the given width
|
|
* @param {number} width
|
|
* @returns {function(string): string}
|
|
*/
|
|
const printCenter = width =>
|
|
s => s.padStart(width / 2 + s.length / 2, ' ').padEnd(width);
|
|
|
|
/**
|
|
* Given an locale string and options, return a function that takes a date
|
|
* object, and retrurns the date formatted to the locale and options.
|
|
* @param {string} locale
|
|
* @param {DateTimeFormatOptions} options
|
|
* @returns {function(Date): string}
|
|
*/
|
|
const localeName = (locale, options) => {
|
|
const formatter = new Intl.DateTimeFormat(locale, options);
|
|
return date => formatter.format(date);
|
|
};
|
|
|
|
/**
|
|
* Increment the date by number.
|
|
* @param {Date} date
|
|
* @param {number} inc
|
|
* @returns {Date}
|
|
*/
|
|
const addDay = (date, inc = 1) => {
|
|
const res = new Date(date.valueOf());
|
|
res.setDate(date.getDate() + inc);
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Given a date, build a string of the week, and return it along with
|
|
* the mutated date object.
|
|
* @param {Date} date
|
|
* @returns {[boolean, Date, string]}
|
|
*/
|
|
const makeWeek = date => {
|
|
const month = date.getMonth();
|
|
let [wdi, md, m] = [date.getUTCDay(), date.getDate(), date.getMonth()];
|
|
const line = Array(7).fill(' ').map((e, i) => {
|
|
if (i === wdi && m === month) {
|
|
const result = (md + '').padStart(2, ' ');
|
|
date = addDay(date);
|
|
[wdi, md, m] = [date.getUTCDay(), date.getDate(), date.getMonth()];
|
|
return result;
|
|
} else {
|
|
return e;
|
|
}
|
|
}).join(' ');
|
|
return [month !== m, date, line];
|
|
}
|
|
|
|
/**
|
|
* Print a nicely formatted calender for the given year in the given locale.
|
|
* @param {number} year The required year of the calender
|
|
* @param {string} locale The locale string. Defaults to US English.
|
|
* @param {number} cols The number of columns for the months. Defaults to 3.
|
|
* @param {number} coll_space The space between the columns. Defaults to 5.
|
|
*/
|
|
const cal = (year, locale = 'en-US', cols = 3, coll_space = 5) => {
|
|
const MONTH_LINES = 9; // Number of lines that make up a month.
|
|
const MONTH_COL_WIDTH = 20; // Character width of a month
|
|
const COL_SPACE = ' '.padStart(coll_space);
|
|
const FULL_WIDTH = MONTH_COL_WIDTH * cols + coll_space * (cols - 1);
|
|
|
|
const collArr = Array(cols).fill('');
|
|
const monthName = localeName(locale, {month: 'long'});
|
|
const weekDayShort = localeName(locale, {weekday: 'short'});
|
|
const monthCenter = printCenter(MONTH_COL_WIDTH);
|
|
const pageCenter = printCenter(FULL_WIDTH);
|
|
|
|
// Get the weekday in the given locale.
|
|
const sun = new Date(Date.UTC(2017, 0, 1)); // A sunday
|
|
const weekdays = Array(7).fill('').map((e, i) =>
|
|
weekDayShort(addDay(sun, i)).padStart(2, ' ').substring(0, 2)).join(' ');
|
|
|
|
// The start date.
|
|
let date = new Date(Date.UTC(year, 0, 1, 0, 0, 0));
|
|
let nextMonth = true;
|
|
let line = '';
|
|
const fullYear = date.getUTCFullYear();
|
|
|
|
// The array into which each of the lines are populated.
|
|
const accumulate = [];
|
|
|
|
// Populate the month table heading and columns.
|
|
const preAmble = date => {
|
|
accumulate.push(monthCenter(' '))
|
|
accumulate.push(monthCenter(monthName(date)));
|
|
accumulate.push(weekdays);
|
|
};
|
|
|
|
// Accumulate the week lines for the year.
|
|
while (date.getUTCFullYear() === fullYear) {
|
|
if (nextMonth) {
|
|
if (accumulate.length % MONTH_LINES !== 0) {
|
|
accumulate.push(monthCenter(' '))
|
|
}
|
|
preAmble(date);
|
|
}
|
|
[nextMonth, date, line] = makeWeek(date);
|
|
accumulate.push(line);
|
|
}
|
|
|
|
// Print the calendar.
|
|
console.log(pageCenter(String.fromCodePoint(0x1F436)));
|
|
console.log(pageCenter(`--- ${fullYear} ---`));
|
|
accumulate.reduce((p, e, i) => {
|
|
if (!p.includes(i)) {
|
|
const indexes = collArr.map((e, ci) => i + ci * MONTH_LINES);
|
|
console.log(indexes.map(e => accumulate[e]).join(COL_SPACE));
|
|
p.push(...indexes);
|
|
}
|
|
return p;
|
|
}, []);
|
|
};
|
|
|
|
cal(1969, 'en-US', 3);
|