1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-23 12:50:16 +00:00
bramw_baserow/web-frontend/modules/database/utils/duration.js

177 lines
4.6 KiB
JavaScript

const MOST_ACCURATE_DURATION_FORMAT = 'h:mm:ss.sss'
const MAX_NUMBER_OF_TOKENS_IN_DURATION_FORMAT =
MOST_ACCURATE_DURATION_FORMAT.split(':').length
export const MAX_BACKEND_DURATION_VALUE_NUMBER_OF_SECS = 86400000000000 // taken from backend timedelta.max.total_seconds()
// Map guarantees the order of the entries
export const DURATION_FORMATS = new Map([
[
'h:mm',
{
description: 'h:mm (1:23)',
example: '1:23',
toString(hours, minutes) {
return `${hours}:${minutes.toString().padStart(2, '0')}`
},
round: (value) => Math.round(value / 60) * 60,
},
],
[
'h:mm:ss',
{
description: 'h:mm:ss (1:23:40)',
example: '1:23:40',
toString(hours, minutes, seconds) {
return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds
.toString()
.padStart(2, '0')}`
},
round: (value) => Math.round(value),
},
],
[
'h:mm:ss.s',
{
description: 'h:mm:ss.s (1:23:40.0)',
example: '1:23:40.0',
toString(hours, minutes, seconds) {
return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds
.toFixed(1)
.padStart(4, '0')}`
},
round: (value) => Math.round(value * 10) / 10,
},
],
[
'h:mm:ss.ss',
{
description: 'h:mm:ss.ss (1:23:40.00)',
example: '1:23:40.00',
toString(hours, minutes, seconds) {
return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds
.toFixed(2)
.padStart(5, '0')}`
},
round: (value) => Math.round(value * 100) / 100,
},
],
[
'h:mm:ss.sss',
{
description: 'h:mm:ss.sss (1:23:40.000)',
example: '1:23:40.000',
toString(hours, minutes, seconds) {
return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds
.toFixed(3)
.padStart(6, '0')}`
},
round: (value) => Math.round(value * 1000) / 1000,
},
],
])
export const DURATION_TOKENS = {
h: {
multiplier: 3600,
parse: (value) => parseInt(value),
},
mm: {
multiplier: 60,
parse: (value) => parseInt(value),
},
ss: {
multiplier: 1,
parse: (value) => parseInt(value),
},
'ss.s': {
multiplier: 1,
parse: (value) => Math.round(parseFloat(value) * 10) / 10,
},
'ss.ss': {
multiplier: 1,
parse: (value) => Math.round(parseFloat(value) * 100) / 100,
},
'ss.sss': {
multiplier: 1,
parse: (value) => Math.round(parseFloat(value) * 1000) / 1000,
},
}
export const roundDurationValueToFormat = (value, format) => {
if (value === null) {
return null
}
const roundFunc = DURATION_FORMATS.get(format).round
return roundFunc(value)
}
/**
* It tries to parse the input value using the given format.
* If the input value does not match the format, it tries to parse it using
* the most accurate format if strict is false, otherwise it throws an error.
*/
export const parseDurationValue = (
inputValue,
format = MOST_ACCURATE_DURATION_FORMAT,
strict = false
) => {
if (inputValue === null || inputValue === undefined || inputValue === '') {
return null
}
// If the input value is a number, we assume it is in seconds.
if (Number.isFinite(inputValue)) {
return inputValue > 0 ? inputValue : null
}
const parts = inputValue.split(':').reverse()
let tokens = format.split(':').reverse()
if (parts.length > tokens.length) {
if (strict || parts.length > MAX_NUMBER_OF_TOKENS_IN_DURATION_FORMAT) {
throw new Error(
`Invalid duration format: ${inputValue} does not match ${format}`
)
} else {
tokens = MOST_ACCURATE_DURATION_FORMAT.split(':').reverse()
}
}
try {
return tokens.reduce((acc, token, index) => {
if (index >= parts.length) {
return acc
}
const part = parts[index]
const parseFunc = DURATION_TOKENS[token].parse
const number = parseFunc(part)
if (isNaN(number) || number < 0) {
throw new Error(
`Invalid duration format: ${inputValue} does not match ${format}`
)
}
const multiplier = DURATION_TOKENS[token].multiplier
return acc + number * multiplier
}, 0)
} catch (e) {
return null
}
}
/**
* It formats the given duration value using the given format.
*/
export const formatDuration = (value, format) => {
if (value === null || value === undefined || value === '') {
return ''
}
const hours = Math.floor(value / 3600)
const mins = Math.floor((value - hours * 3600) / 60)
const secs = value - hours * 3600 - mins * 60
const formatFunc = DURATION_FORMATS.get(format).toString
return formatFunc(hours, mins, secs)
}