[heatmap] date constants

This commit is contained in:
Prateeksha Singh 2018-03-21 17:09:35 +05:30
parent 786b8b1ecf
commit 8bc4d3f94c
11 changed files with 112 additions and 94 deletions

View File

@ -252,6 +252,9 @@ const FULL_ANGLE = 360;
// More colors are difficult to parse visually // More colors are difficult to parse visually
const HEATMAP_DISTRIBUTION_SIZE = 5; const HEATMAP_DISTRIBUTION_SIZE = 5;
const HEATMAP_SQUARE_SIZE = 10;
const HEATMAP_GUTTER_SIZE = 2;
const HEATMAP_COLORS = ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127']; const HEATMAP_COLORS = ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'];
const DEFAULT_CHART_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange', const DEFAULT_CHART_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange',
'yellow', 'green', 'light-green', 'purple', 'magenta', 'light-grey', 'dark-grey']; 'yellow', 'green', 'light-green', 'purple', 'magenta', 'light-grey', 'dark-grey'];
@ -2131,6 +2134,13 @@ class PieChart extends AggregationChart {
// Playing around with dates // Playing around with dates
const NO_OF_YEAR_MONTHS = 12;
const NO_OF_DAYS_IN_WEEK = 7;
const NO_OF_MILLIS = 1000;
const MONTH_NAMES = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
// https://stackoverflow.com/a/11252167/6495043 // https://stackoverflow.com/a/11252167/6495043
function treatAsUtc(dateStr) { function treatAsUtc(dateStr) {
let result = new Date(dateStr); let result = new Date(dateStr);
@ -2162,6 +2172,11 @@ function addDays(date, numberOfDays) {
date.setDate(date.getDate() + numberOfDays); date.setDate(date.getDate() + numberOfDays);
} }
function getMonthName(i, short=false) {
let monthName = MONTH_NAMES[i];
return short ? monthName.slice(0, 3) : monthName;
}
function normalize(x) { function normalize(x) {
// Calculates mantissa and exponent of a number // Calculates mantissa and exponent of a number
// Returns normalized number and exponent // Returns normalized number and exponent
@ -2373,30 +2388,24 @@ function getMaxCheckpoint(value, distribution) {
return distribution.filter(d => d < value).length; return distribution.filter(d => d < value).length;
} }
const COL_SIZE = HEATMAP_SQUARE_SIZE + HEATMAP_GUTTER_SIZE;
class Heatmap extends BaseChart { class Heatmap extends BaseChart {
constructor(parent, options) { constructor(parent, options) {
super(parent, options); super(parent, options);
this.type = 'heatmap'; this.type = 'heatmap';
this.data = options.data || {}; this.dataPoints = options.data.dataPoints || {};
this.discreteDomains = options.discreteDomains === 0 ? 0 : 1; this.discreteDomains = options.discreteDomains === 0 ? 0 : 1;
this.countLabel = options.countLabel || ''; this.countLabel = options.countLabel || '';
let today = new Date();
this.start = options.start || addDays(today, 365);
this.translateX = 0;
this.setup(); this.setup();
} }
setMargins() {
super.setMargins();
this.leftMargin = 10;
this.translateY = 10;
}
configure(args) { configure(args) {
super.configure(args); super.configure(args);
this.start = args.data.start;
this.today = new Date(); this.today = new Date();
if(!this.start) { if(!this.start) {
@ -2405,20 +2414,26 @@ class Heatmap extends BaseChart {
} }
this.firstWeekStart = new Date(this.start.toDateString()); this.firstWeekStart = new Date(this.start.toDateString());
this.lastWeekStart = new Date(this.today.toDateString()); this.lastWeekStart = new Date(this.today.toDateString());
if(this.firstWeekStart.getDay() !== 7) { if(this.firstWeekStart.getDay() !== NO_OF_DAYS_IN_WEEK) {
addDays(this.firstWeekStart, (-1) * this.firstWeekStart.getDay()); addDays(this.firstWeekStart, (-1) * this.firstWeekStart.getDay());
} }
if(this.lastWeekStart.getDay() !== 7) { if(this.lastWeekStart.getDay() !== NO_OF_DAYS_IN_WEEK) {
addDays(this.lastWeekStart, (-1) * this.lastWeekStart.getDay()); addDays(this.lastWeekStart, (-1) * this.lastWeekStart.getDay());
} }
this.no_of_cols = getWeeksBetween(this.firstWeekStart + '', this.lastWeekStart + '') + 1; this.no_of_cols = getWeeksBetween(this.firstWeekStart + '', this.lastWeekStart + '') + 1;
} }
setMargins() {
super.setMargins();
this.leftMargin = HEATMAP_SQUARE_SIZE;
this.translateY = HEATMAP_SQUARE_SIZE;
}
calcWidth() { calcWidth() {
this.baseWidth = (this.no_of_cols + 3) * 12 ; this.baseWidth = (this.no_of_cols) * COL_SIZE;
if(this.discreteDomains) { if(this.discreteDomains) {
this.baseWidth += (12 * 12); this.baseWidth += (COL_SIZE * NO_OF_YEAR_MONTHS);
} }
} }
@ -2440,13 +2455,8 @@ class Heatmap extends BaseChart {
} }
calc() { calc() {
let dataValues = Object.keys(this.dataPoints).map(key => this.dataPoints[key]);
let dataValues = Object.keys(this.data).map(key => this.data[key]);
this.distribution = calcDistribution(dataValues, HEATMAP_DISTRIBUTION_SIZE); this.distribution = calcDistribution(dataValues, HEATMAP_DISTRIBUTION_SIZE);
this.monthNames = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];
} }
render() { render() {
@ -2465,7 +2475,6 @@ class Heatmap extends BaseChart {
this.months = [this.currentMonth + '']; this.months = [this.currentMonth + ''];
this.monthWeeks = {}, this.monthStartPoints = []; this.monthWeeks = {}, this.monthStartPoints = [];
this.monthWeeks[this.currentMonth] = 0; this.monthWeeks[this.currentMonth] = 0;
this.monthStartPoints.push(13);
for(var i = 0; i < no_of_weeks; i++) { for(var i = 0; i < no_of_weeks; i++) {
let dataGroup, monthChange = 0; let dataGroup, monthChange = 0;
@ -2476,19 +2485,16 @@ class Heatmap extends BaseChart {
this.weekCol += 1 + parseInt(this.discreteDomains && monthChange); this.weekCol += 1 + parseInt(this.discreteDomains && monthChange);
this.monthWeeks[this.currentMonth]++; this.monthWeeks[this.currentMonth]++;
if(monthChange) { if(monthChange) {
this.currentMonth = (this.currentMonth + 1) % 12; this.currentMonth = (this.currentMonth + 1) % NO_OF_YEAR_MONTHS;
this.months.push(this.currentMonth + ''); this.months.push(this.currentMonth + '');
this.monthWeeks[this.currentMonth] = 1; this.monthWeeks[this.currentMonth] = 1;
} }
addDays(currentWeekSunday, 7); addDays(currentWeekSunday, NO_OF_DAYS_IN_WEEK);
} }
this.render_month_labels(); this.render_month_labels();
} }
get_week_squares_group(currentDate, index) { get_week_squares_group(currentDate, index) {
const noOfWeekdays = 7;
const squareSide = 10;
const cellPadding = 2;
const step = 1; const step = 1;
const todayTime = this.today.getTime(); const todayTime = this.today.getTime();
@ -2497,26 +2503,26 @@ class Heatmap extends BaseChart {
let dataGroup = makeSVGGroup(this.dataGroups, 'data-group'); let dataGroup = makeSVGGroup(this.dataGroups, 'data-group');
for(var y = 0, i = 0; i < noOfWeekdays; i += step, y += (squareSide + cellPadding)) { for(var y = 0, i = 0; i < NO_OF_DAYS_IN_WEEK; i += step, y += COL_SIZE) {
let dataValue = 0; let dataValue = 0;
let colorIndex = 0; let colorIndex = 0;
let currentTimestamp = currentDate.getTime()/1000; let currentTimestamp = currentDate.getTime()/NO_OF_MILLIS;
let timestamp = Math.floor(currentTimestamp - (currentTimestamp % 86400)).toFixed(1); let timestamp = Math.floor(currentTimestamp - (currentTimestamp % 86400)).toFixed(1);
if(this.data[timestamp]) { if(this.dataPoints[timestamp]) {
dataValue = this.data[timestamp]; dataValue = this.dataPoints[timestamp];
} }
if(this.data[Math.round(timestamp)]) { if(this.dataPoints[Math.round(timestamp)]) {
dataValue = this.data[Math.round(timestamp)]; dataValue = this.dataPoints[Math.round(timestamp)];
} }
if(dataValue) { if(dataValue) {
colorIndex = getMaxCheckpoint(dataValue, this.distribution); colorIndex = getMaxCheckpoint(dataValue, this.distribution);
} }
let x = 13 + (index + weekColChange) * 12; let x = (index + weekColChange) * COL_SIZE;
let dataAttr = { let dataAttr = {
'data-date': getDdMmYyyy(currentDate), 'data-date': getDdMmYyyy(currentDate),
@ -2524,7 +2530,7 @@ class Heatmap extends BaseChart {
'data-day': currentDate.getDay() 'data-day': currentDate.getDay()
}; };
let heatSquare = makeHeatSquare('day', x, y, squareSide, let heatSquare = makeHeatSquare('day', x, y, HEATMAP_SQUARE_SIZE,
this.colors[colorIndex], dataAttr); this.colors[colorIndex], dataAttr);
dataGroup.appendChild(heatSquare); dataGroup.appendChild(heatSquare);
@ -2540,7 +2546,7 @@ class Heatmap extends BaseChart {
weekColChange = 1; weekColChange = 1;
} }
this.monthStartPoints.push(13 + (index + weekColChange) * 12); this.monthStartPoints.push((index + weekColChange) * COL_SIZE);
} }
currentDate = nextDate; currentDate = nextDate;
} }
@ -2569,8 +2575,8 @@ class Heatmap extends BaseChart {
this.monthStartPoints.pop(); this.monthStartPoints.pop();
this.monthStartPoints.map((start, i) => { this.monthStartPoints.map((start, i) => {
let month_name = this.monthNames[this.months[i]].substring(0, 3); let month_name = getMonthName(this.months[i], true);
let text = makeText('y-value-text', start+12, 10, month_name); let text = makeText('y-value-text', start + COL_SIZE, HEATMAP_SQUARE_SIZE, month_name);
this.domainLabelGroup.appendChild(text); this.domainLabelGroup.appendChild(text);
}); });
} }
@ -2583,7 +2589,7 @@ class Heatmap extends BaseChart {
let count = e.target.getAttribute('data-value'); let count = e.target.getAttribute('data-value');
let dateParts = e.target.getAttribute('data-date').split('-'); let dateParts = e.target.getAttribute('data-date').split('-');
let month = this.monthNames[parseInt(dateParts[1])-1].substring(0, 3); let month = getMonthName(parseInt(dateParts[1])-1, true);
let gOff = this.chartWrapper.getBoundingClientRect(), pOff = e.target.getBoundingClientRect(); let gOff = this.chartWrapper.getBoundingClientRect(), pOff = e.target.getBoundingClientRect();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -449,10 +449,18 @@ for (var i = 0; i< 375; i++) {
timestamp = Math.floor(timestamp - 86400).toFixed(1); timestamp = Math.floor(timestamp - 86400).toFixed(1);
} }
let today = new Date();
let start = today;
let end = '';
start.setFullYear( start.getFullYear() - 2 );
let heatmap = new frappe.Chart("#chart-heatmap", { let heatmap = new frappe.Chart("#chart-heatmap", {
data: heatmapData, data: {
dataPoints: heatmapData,
start: start,
end: end,
},
type: 'heatmap', type: 'heatmap',
legendScale: [0, 1, 2, 4, 5],
height: 115, height: 115,
discreteDomains: 1, discreteDomains: 1,
colors: ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'] colors: ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c']

View File

@ -1,33 +1,31 @@
import BaseChart from './BaseChart'; import BaseChart from './BaseChart';
import { makeSVGGroup, makeHeatSquare, makeText } from '../utils/draw'; import { makeSVGGroup, makeHeatSquare, makeText } from '../utils/draw';
import { addDays, getDdMmYyyy, getWeeksBetween } from '../utils/date-utils'; import { addDays, getDdMmYyyy, getWeeksBetween, getMonthName,
NO_OF_MILLIS, NO_OF_YEAR_MONTHS, NO_OF_DAYS_IN_WEEK } from '../utils/date-utils';
import { calcDistribution, getMaxCheckpoint } from '../utils/intervals'; import { calcDistribution, getMaxCheckpoint } from '../utils/intervals';
import { HEATMAP_DISTRIBUTION_SIZE } from '../utils/constants'; import { HEATMAP_DISTRIBUTION_SIZE, HEATMAP_SQUARE_SIZE,
HEATMAP_GUTTER_SIZE } from '../utils/constants';
const COL_SIZE = HEATMAP_SQUARE_SIZE + HEATMAP_GUTTER_SIZE;
const EXTRA_COLS = 3;
export default class Heatmap extends BaseChart { export default class Heatmap extends BaseChart {
constructor(parent, options) { constructor(parent, options) {
super(parent, options); super(parent, options);
this.type = 'heatmap'; this.type = 'heatmap';
this.data = options.data || {}; this.dataPoints = options.data.dataPoints || {};
this.discreteDomains = options.discreteDomains === 0 ? 0 : 1; this.discreteDomains = options.discreteDomains === 0 ? 0 : 1;
this.countLabel = options.countLabel || ''; this.countLabel = options.countLabel || '';
let today = new Date();
this.start = options.start || addDays(today, 365);
this.translateX = 0;
this.setup(); this.setup();
} }
setMargins() {
super.setMargins();
this.leftMargin = 10;
this.translateY = 10;
}
configure(args) { configure(args) {
super.configure(args); super.configure(args);
this.start = args.data.start;
this.today = new Date(); this.today = new Date();
if(!this.start) { if(!this.start) {
@ -36,20 +34,26 @@ export default class Heatmap extends BaseChart {
} }
this.firstWeekStart = new Date(this.start.toDateString()); this.firstWeekStart = new Date(this.start.toDateString());
this.lastWeekStart = new Date(this.today.toDateString()); this.lastWeekStart = new Date(this.today.toDateString());
if(this.firstWeekStart.getDay() !== 7) { if(this.firstWeekStart.getDay() !== NO_OF_DAYS_IN_WEEK) {
addDays(this.firstWeekStart, (-1) * this.firstWeekStart.getDay()); addDays(this.firstWeekStart, (-1) * this.firstWeekStart.getDay());
} }
if(this.lastWeekStart.getDay() !== 7) { if(this.lastWeekStart.getDay() !== NO_OF_DAYS_IN_WEEK) {
addDays(this.lastWeekStart, (-1) * this.lastWeekStart.getDay()); addDays(this.lastWeekStart, (-1) * this.lastWeekStart.getDay());
} }
this.no_of_cols = getWeeksBetween(this.firstWeekStart + '', this.lastWeekStart + '') + 1; this.no_of_cols = getWeeksBetween(this.firstWeekStart + '', this.lastWeekStart + '') + 1;
} }
setMargins() {
super.setMargins();
this.leftMargin = HEATMAP_SQUARE_SIZE;
this.translateY = HEATMAP_SQUARE_SIZE;
}
calcWidth() { calcWidth() {
this.baseWidth = (this.no_of_cols + 3) * 12 ; this.baseWidth = (this.no_of_cols) * COL_SIZE;
if(this.discreteDomains) { if(this.discreteDomains) {
this.baseWidth += (12 * 12); this.baseWidth += (COL_SIZE * NO_OF_YEAR_MONTHS);
} }
} }
@ -71,13 +75,8 @@ export default class Heatmap extends BaseChart {
} }
calc() { calc() {
let dataValues = Object.keys(this.dataPoints).map(key => this.dataPoints[key]);
let dataValues = Object.keys(this.data).map(key => this.data[key]);
this.distribution = calcDistribution(dataValues, HEATMAP_DISTRIBUTION_SIZE); this.distribution = calcDistribution(dataValues, HEATMAP_DISTRIBUTION_SIZE);
this.monthNames = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];
} }
render() { render() {
@ -94,9 +93,9 @@ export default class Heatmap extends BaseChart {
this.currentMonth = currentWeekSunday.getMonth(); this.currentMonth = currentWeekSunday.getMonth();
this.months = [this.currentMonth + '']; this.months = [this.currentMonth + ''];
this.monthWeeks = {}, this.monthStartPoints = []; this.monthWeeks = {},
this.monthStartPoints = [];
this.monthWeeks[this.currentMonth] = 0; this.monthWeeks[this.currentMonth] = 0;
this.monthStartPoints.push(13);
for(var i = 0; i < no_of_weeks; i++) { for(var i = 0; i < no_of_weeks; i++) {
let dataGroup, monthChange = 0; let dataGroup, monthChange = 0;
@ -107,19 +106,16 @@ export default class Heatmap extends BaseChart {
this.weekCol += 1 + parseInt(this.discreteDomains && monthChange); this.weekCol += 1 + parseInt(this.discreteDomains && monthChange);
this.monthWeeks[this.currentMonth]++; this.monthWeeks[this.currentMonth]++;
if(monthChange) { if(monthChange) {
this.currentMonth = (this.currentMonth + 1) % 12; this.currentMonth = (this.currentMonth + 1) % NO_OF_YEAR_MONTHS;
this.months.push(this.currentMonth + ''); this.months.push(this.currentMonth + '');
this.monthWeeks[this.currentMonth] = 1; this.monthWeeks[this.currentMonth] = 1;
} }
addDays(currentWeekSunday, 7); addDays(currentWeekSunday, NO_OF_DAYS_IN_WEEK);
} }
this.render_month_labels(); this.render_month_labels();
} }
get_week_squares_group(currentDate, index) { get_week_squares_group(currentDate, index) {
const noOfWeekdays = 7;
const squareSide = 10;
const cellPadding = 2;
const step = 1; const step = 1;
const todayTime = this.today.getTime(); const todayTime = this.today.getTime();
@ -128,26 +124,26 @@ export default class Heatmap extends BaseChart {
let dataGroup = makeSVGGroup(this.dataGroups, 'data-group'); let dataGroup = makeSVGGroup(this.dataGroups, 'data-group');
for(var y = 0, i = 0; i < noOfWeekdays; i += step, y += (squareSide + cellPadding)) { for(var y = 0, i = 0; i < NO_OF_DAYS_IN_WEEK; i += step, y += COL_SIZE) {
let dataValue = 0; let dataValue = 0;
let colorIndex = 0; let colorIndex = 0;
let currentTimestamp = currentDate.getTime()/1000; let currentTimestamp = currentDate.getTime()/NO_OF_MILLIS;
let timestamp = Math.floor(currentTimestamp - (currentTimestamp % 86400)).toFixed(1); let timestamp = Math.floor(currentTimestamp - (currentTimestamp % 86400)).toFixed(1);
if(this.data[timestamp]) { if(this.dataPoints[timestamp]) {
dataValue = this.data[timestamp]; dataValue = this.dataPoints[timestamp];
} }
if(this.data[Math.round(timestamp)]) { if(this.dataPoints[Math.round(timestamp)]) {
dataValue = this.data[Math.round(timestamp)]; dataValue = this.dataPoints[Math.round(timestamp)];
} }
if(dataValue) { if(dataValue) {
colorIndex = getMaxCheckpoint(dataValue, this.distribution); colorIndex = getMaxCheckpoint(dataValue, this.distribution);
} }
let x = 13 + (index + weekColChange) * 12; let x = (index + weekColChange) * COL_SIZE;
let dataAttr = { let dataAttr = {
'data-date': getDdMmYyyy(currentDate), 'data-date': getDdMmYyyy(currentDate),
@ -155,7 +151,7 @@ export default class Heatmap extends BaseChart {
'data-day': currentDate.getDay() 'data-day': currentDate.getDay()
}; };
let heatSquare = makeHeatSquare('day', x, y, squareSide, let heatSquare = makeHeatSquare('day', x, y, HEATMAP_SQUARE_SIZE,
this.colors[colorIndex], dataAttr); this.colors[colorIndex], dataAttr);
dataGroup.appendChild(heatSquare); dataGroup.appendChild(heatSquare);
@ -171,7 +167,7 @@ export default class Heatmap extends BaseChart {
weekColChange = 1; weekColChange = 1;
} }
this.monthStartPoints.push(13 + (index + weekColChange) * 12); this.monthStartPoints.push((index + weekColChange) * COL_SIZE);
} }
currentDate = nextDate; currentDate = nextDate;
} }
@ -200,8 +196,8 @@ export default class Heatmap extends BaseChart {
this.monthStartPoints.pop(); this.monthStartPoints.pop();
this.monthStartPoints.map((start, i) => { this.monthStartPoints.map((start, i) => {
let month_name = this.monthNames[this.months[i]].substring(0, 3); let month_name = getMonthName(this.months[i], true);
let text = makeText('y-value-text', start+12, 10, month_name); let text = makeText('y-value-text', start + COL_SIZE, HEATMAP_SQUARE_SIZE, month_name);
this.domainLabelGroup.appendChild(text); this.domainLabelGroup.appendChild(text);
}); });
} }
@ -214,7 +210,7 @@ export default class Heatmap extends BaseChart {
let count = e.target.getAttribute('data-value'); let count = e.target.getAttribute('data-value');
let dateParts = e.target.getAttribute('data-date').split('-'); let dateParts = e.target.getAttribute('data-date').split('-');
let month = this.monthNames[parseInt(dateParts[1])-1].substring(0, 3); let month = getMonthName(parseInt(dateParts[1])-1, true);
let gOff = this.chartWrapper.getBoundingClientRect(), pOff = e.target.getBoundingClientRect(); let gOff = this.chartWrapper.getBoundingClientRect(), pOff = e.target.getBoundingClientRect();

View File

@ -44,6 +44,9 @@ export const FULL_ANGLE = 360;
// More colors are difficult to parse visually // More colors are difficult to parse visually
export const HEATMAP_DISTRIBUTION_SIZE = 5; export const HEATMAP_DISTRIBUTION_SIZE = 5;
export const HEATMAP_SQUARE_SIZE = 10;
export const HEATMAP_GUTTER_SIZE = 2;
const HEATMAP_COLORS = ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127']; const HEATMAP_COLORS = ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'];
const DEFAULT_CHART_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange', const DEFAULT_CHART_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange',
'yellow', 'green', 'light-green', 'purple', 'magenta', 'light-grey', 'dark-grey']; 'yellow', 'green', 'light-green', 'purple', 'magenta', 'light-grey', 'dark-grey'];

View File

@ -1,5 +1,12 @@
// Playing around with dates // Playing around with dates
export const NO_OF_YEAR_MONTHS = 12;
export const NO_OF_DAYS_IN_WEEK = 7;
export const NO_OF_MILLIS = 1000;
export const MONTH_NAMES = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
// https://stackoverflow.com/a/11252167/6495043 // https://stackoverflow.com/a/11252167/6495043
function treatAsUtc(dateStr) { function treatAsUtc(dateStr) {
let result = new Date(dateStr); let result = new Date(dateStr);
@ -31,9 +38,7 @@ export function addDays(date, numberOfDays) {
date.setDate(date.getDate() + numberOfDays); date.setDate(date.getDate() + numberOfDays);
} }
export function getMonthName(i) { export function getMonthName(i, short=false) {
let monthNames = ["January", "February", "March", "April", "May", "June", let monthName = MONTH_NAMES[i];
"July", "August", "September", "October", "November", "December" return short ? monthName.slice(0, 3) : monthName;
];
return monthNames[i];
} }