analytics/assets/js/dashboard/stats/graph/date-formatter.js
2023-01-24 10:43:43 +01:00

128 lines
4.0 KiB
JavaScript

import {parseUTCDate, formatMonthYYYY, formatDay, formatDayShort} from '../../util/date'
const browserDateFormat = Intl.DateTimeFormat(navigator.language, { hour: 'numeric' })
const is12HourClock = function() {
return browserDateFormat.resolvedOptions().hour12
}
const parseISODate = function(isoDate) {
const date = parseUTCDate(isoDate)
const minutes = date.getMinutes();
return { date, minutes }
}
const formatHours = function(isoDate) {
const monthIndex = 1
const dateParts = isoDate.split(/[^0-9]/);
dateParts[monthIndex] = dateParts[monthIndex] - 1
const localDate = new Date(...dateParts)
return browserDateFormat.format(localDate)
}
const monthIntervalFormatter = {
long(isoDate, options) {
const formatted = this.short(isoDate, options)
return options.isBucketPartial ? `Partial of ${formatted}` : formatted
},
short(isoDate, _options) {
const { date } = parseISODate(isoDate)
return formatMonthYYYY(date)
}
}
const weekIntervalFormatter = {
long(isoDate, options) {
const formatted = this.short(isoDate, options)
return options.isBucketPartial ? `Partial week of ${formatted}` : `Week of ${formatted}`
},
short(isoDate, _options) {
const { date } = parseISODate(isoDate)
return formatDayShort(date)
}
}
const dateIntervalFormatter = {
long(isoDate, _options) {
const { date } = parseISODate(isoDate)
return formatDay(date)
},
short(isoDate, _options) {
const { date } = parseISODate(isoDate)
return formatDayShort(date)
}
}
const hourIntervalFormatter = {
long(isoDate, options) {
return this.short(isoDate, options)
},
short(isoDate, _options) {
const formatted = formatHours(isoDate)
if (is12HourClock()) {
return formatted.replace(' ', '').toLowerCase()
} else {
return formatted.replace(/[^0-9]/g, '').concat(":00")
}
}
}
const minuteIntervalFormatter = {
long(isoDate, options) {
if (options.period == 'realtime') {
const minutesAgo = Math.abs(isoDate)
return minutesAgo === 1 ? '1 minute ago' : minutesAgo + ' minutes ago'
} else {
return this.short(isoDate, options)
}
},
short(isoDate, options) {
if (options.period === 'realtime') return isoDate + 'm'
const { minutes } = parseISODate(isoDate)
const formatted = formatHours(isoDate)
if (is12HourClock()) {
return formatted.replace(' ', ':' + (minutes < 10 ? `0${minutes}` : minutes)).toLowerCase()
} else {
return formatted.replace(/[^0-9]/g, '').concat(":" + (minutes < 10 ? `0${minutes}` : minutes))
}
}
}
// Each interval has a different date and time format. This object maps each
// interval with two functions: `long` and `short`, that formats date and time
// accordingly.
const factory = {
month: monthIntervalFormatter,
week: weekIntervalFormatter,
date: dateIntervalFormatter,
hour: hourIntervalFormatter,
minute: minuteIntervalFormatter
}
/**
* Returns a function that formats a ISO 8601 timestamp based on the given
* arguments.
*
* The preferred date and time format in the dashboard depends on the selected
* interval and period. For example, in real-time view only the time is necessary,
* while other intervals require dates to be displayed.
*
* @param {string} interval - The interval of the query, e.g. `minute`, `hour`
* @param {boolean} longForm - Whether the formatted result should be in long or
* short form.
* @param {string} period - The period of the query, e.g. `12mo`, `day`
* @param {boolean} isPeriodFull - Indicates whether the interval has been cut
* off by the requested date range or not. If false, the returned formatted date
* indicates this cut off, e.g. `Partial week of November 8`.
*/
export default function dateFormatter(interval, longForm, period, isPeriodFull) {
const displayMode = longForm ? 'long' : 'short'
const options = { period: period, interval: interval, isBucketPartial: !isPeriodFull }
return function(isoDate, _index, _ticks) {
return factory[interval][displayMode](isoDate, options)
}
}