import { parseDurationValue, formatDurationValue, roundDurationValueToFormat, } from '@baserow/modules/database/utils/duration' const SECS_IN_MIN = 60 const SECS_IN_HOUR = 60 * 60 const VALID_DURATION_VALUES = { 'd h': [ ['1d 2h', 26 * SECS_IN_HOUR], ['2 1h', 49 * SECS_IN_HOUR], ['2 2', 50 * SECS_IN_HOUR], ['1 8', 32 * SECS_IN_HOUR], ['1d0h', 24 * SECS_IN_HOUR], ['1d25', 49 * SECS_IN_HOUR], ['5h', 5 * SECS_IN_HOUR], ['2d', 48 * SECS_IN_HOUR], ['10', 10 * SECS_IN_HOUR], ['3600.0', 1 * SECS_IN_HOUR], ['60:0.0', 1 * SECS_IN_HOUR], ['0', 0], ], 'd h:mm': [ ['1d 2:30', 26 * SECS_IN_HOUR + 30 * 60], ['2 1:30', 49 * SECS_IN_HOUR + 30 * 60], ['2 1:61', 50 * SECS_IN_HOUR + 1 * 60], ['1 8:30', 32 * SECS_IN_HOUR + 30 * 60], ['1d0:61', 25 * SECS_IN_HOUR + 1 * 60], ['1d0:30', 24 * SECS_IN_HOUR + 30 * 60], ['1d1:30', 25 * SECS_IN_HOUR + 30 * 60], ['5:30', 5 * SECS_IN_HOUR + 30 * 60], ['10:30', 10 * SECS_IN_HOUR + 30 * 60], ['0:30', 30 * 60], ['0:0', 0], ], 'd h:mm:ss': [ ['1d 2:30:45', 26 * SECS_IN_HOUR + 30 * 60 + 45], ['2 2:30:45', 50 * SECS_IN_HOUR + 30 * 60 + 45], ['1 8:30:45', 32 * SECS_IN_HOUR + 30 * 60 + 45], ['1d0:30:45', 24 * SECS_IN_HOUR + 30 * 60 + 45], ['1d1:30:45', 25 * SECS_IN_HOUR + 30 * 60 + 45], ['1d2:30:61', 26 * SECS_IN_HOUR + 31 * 60 + 1], ['5:30:45', 5 * SECS_IN_HOUR + 30 * 60 + 45], ['10:30:45', 10 * SECS_IN_HOUR + 30 * 60 + 45], ['0:30:45', 30 * 60 + 45], ['0:0:45', 45], ['0:0:0', 0], ], 'h:mm': [ ['2:30', 2 * SECS_IN_HOUR + 30 * 60], ['1:30', 1 * SECS_IN_HOUR + 30 * 60], ['2:30', 2 * SECS_IN_HOUR + 30 * 60], ['8:30', 8 * SECS_IN_HOUR + 30 * 60], ['0:30', 30 * 60], ['0:61', 61 * 60], ['0:0', 0], ['0', 0], ], 'h:mm:ss': [ ['2:30:45', 2 * SECS_IN_HOUR + 30 * 60 + 45], ['1:30:45', 1 * SECS_IN_HOUR + 30 * 60 + 45], ['2:30:45', 2 * SECS_IN_HOUR + 30 * 60 + 45], ['8:30:45', 8 * SECS_IN_HOUR + 30 * 60 + 45], ['0:30:45', 30 * 60 + 45], ['0:0:45', 45], ['0:0:61', 61], ['0:0:0', 0], ], 'h:mm:ss.s': [ ['2:30:45.1', 2 * SECS_IN_HOUR + 30 * 60 + 45.1], ['1:30:45.2', 1 * SECS_IN_HOUR + 30 * 60 + 45.2], ['2:30:45.3', 2 * SECS_IN_HOUR + 30 * 60 + 45.3], ['8:30:45.9', 8 * SECS_IN_HOUR + 30 * 60 + 45.9], ['0:30:45.5', 30 * 60 + 45.5], ['0:0:45.0', 45], ['0:0:59.9', 59.9], ['0:0:0.0', 0], ], 'h:mm:ss.ss': [ ['2:30:45.11', 2 * SECS_IN_HOUR + 30 * 60 + 45.11], ['1:30:45.22', 1 * SECS_IN_HOUR + 30 * 60 + 45.22], ['2:30:45.33', 2 * SECS_IN_HOUR + 30 * 60 + 45.33], ['8:30:45.46', 8 * SECS_IN_HOUR + 30 * 60 + 45.46], ['0:30:45.50', 30 * 60 + 45.5], ['0:0:45.00', 45], ['0:0:59.99', 59.99], ['0', 0], ], 'h:mm:ss.sss': [ ['2:30:45.111', 2 * SECS_IN_HOUR + 30 * 60 + 45.111], ['1:30:45.222', 1 * SECS_IN_HOUR + 30 * 60 + 45.222], ['2:30:45.333', 2 * SECS_IN_HOUR + 30 * 60 + 45.333], ['8:30:45.456', 8 * SECS_IN_HOUR + 30 * 60 + 45.456], ['0:30:45.500', 30 * 60 + 45.5], ['0:0:45.000', 45], ['0:0:59.999', 59.999], ['0', 0], ], } const INVALID_DURATION_VALUES = [ 'd', 'h', '1d 2h 3m', '1d 2h 3s', '1day', '2 days', '1 hour', '8 hours', '1:2:3:4', 'aaaaa', '1dd', '2hh', '1h2m', ] const DURATION_FORMATTED_VALUES = { 'd h': [ [0, '0d 0h'], [1 * SECS_IN_HOUR, '0d 1h'], [24 * SECS_IN_HOUR, '1d 0h'], [25 * SECS_IN_HOUR, '1d 1h'], [49 * SECS_IN_HOUR, '2d 1h'], [50 * SECS_IN_HOUR, '2d 2h'], [32 * SECS_IN_HOUR, '1d 8h'], [5 * SECS_IN_HOUR, '0d 5h'], ], 'd h:mm': [ [0, '0d 0:00'], [1 * SECS_IN_HOUR, '0d 1:00'], [24 * SECS_IN_HOUR, '1d 0:00'], [2 * SECS_IN_HOUR, '0d 2:00'], [49 * SECS_IN_HOUR, '2d 1:00'], [50 * SECS_IN_HOUR, '2d 2:00'], [32 * SECS_IN_HOUR, '1d 8:00'], [5 * SECS_IN_HOUR, '0d 5:00'], [5 * SECS_IN_HOUR + 30 * SECS_IN_MIN, '0d 5:30'], ], 'd h:mm:ss': [ [0, '0d 0:00:00'], [1 * SECS_IN_HOUR, '0d 1:00:00'], [24 * SECS_IN_HOUR, '1d 0:00:00'], [25 * SECS_IN_HOUR, '1d 1:00:00'], [49 * SECS_IN_HOUR, '2d 1:00:00'], [50 * SECS_IN_HOUR, '2d 2:00:00'], [32 * SECS_IN_HOUR, '1d 8:00:00'], [5 * SECS_IN_HOUR, '0d 5:00:00'], [5 * SECS_IN_HOUR + 30 * SECS_IN_MIN, '0d 5:30:00'], [5 * SECS_IN_HOUR + 5 * SECS_IN_MIN + 5, '0d 5:05:05'], ], 'h:mm': [ [0, '0:00'], [1 * SECS_IN_MIN, '0:01'], [61 * SECS_IN_MIN, '1:01'], [24 * SECS_IN_HOUR, '24:00'], [25 * SECS_IN_HOUR, '25:00'], [49 * SECS_IN_HOUR, '49:00'], [50 * SECS_IN_HOUR, '50:00'], [32 * SECS_IN_HOUR, '32:00'], [5 * SECS_IN_HOUR, '5:00'], [5 * SECS_IN_HOUR + 30 * SECS_IN_MIN, '5:30'], ], 'h:mm:ss': [ [0, '0:00:00'], [1, '0:00:01'], [61, '0:01:01'], [1 * SECS_IN_MIN, '0:01:00'], [61 * SECS_IN_MIN, '1:01:00'], [24 * SECS_IN_HOUR, '24:00:00'], [25 * SECS_IN_HOUR, '25:00:00'], [49 * SECS_IN_HOUR, '49:00:00'], [50 * SECS_IN_HOUR, '50:00:00'], [32 * SECS_IN_HOUR, '32:00:00'], [5 * SECS_IN_HOUR, '5:00:00'], [5 * SECS_IN_HOUR + 30 * SECS_IN_MIN, '5:30:00'], [5 * SECS_IN_HOUR + 5 * SECS_IN_MIN + 5, '5:05:05'], ], 'h:mm:ss.s': [ [0, '0:00:00.0'], [1.1, '0:00:01.1'], [61.9, '0:01:01.9'], [1 * SECS_IN_MIN + 1.1, '0:01:01.1'], [61 * SECS_IN_MIN + 2.2, '1:01:02.2'], [24 * SECS_IN_HOUR + 3.3, '24:00:03.3'], [25 * SECS_IN_HOUR + 9.9, '25:00:09.9'], [49 * SECS_IN_HOUR, '49:00:00.0'], [50 * SECS_IN_HOUR, '50:00:00.0'], [32 * SECS_IN_HOUR, '32:00:00.0'], [5 * SECS_IN_HOUR, '5:00:00.0'], [5 * SECS_IN_HOUR + 30 * SECS_IN_MIN, '5:30:00.0'], [5 * SECS_IN_HOUR + 5 * SECS_IN_MIN + 5, '5:05:05.0'], [5 * SECS_IN_HOUR + 5 * SECS_IN_MIN + 5.1, '5:05:05.1'], ], 'h:mm:ss.ss': [ [0, '0:00:00.00'], [1.11, '0:00:01.11'], [61.22, '0:01:01.22'], [1 * SECS_IN_MIN + 1.11, '0:01:01.11'], [61 * SECS_IN_MIN + 2.22, '1:01:02.22'], [24 * SECS_IN_HOUR + 3.33, '24:00:03.33'], [25 * SECS_IN_HOUR + 9.99, '25:00:09.99'], [49 * SECS_IN_HOUR, '49:00:00.00'], [50 * SECS_IN_HOUR, '50:00:00.00'], [32 * SECS_IN_HOUR, '32:00:00.00'], [5 * SECS_IN_HOUR, '5:00:00.00'], [5 * SECS_IN_HOUR + 30 * SECS_IN_MIN, '5:30:00.00'], [5 * SECS_IN_HOUR + 5 * SECS_IN_MIN + 5, '5:05:05.00'], [5 * SECS_IN_HOUR + 5 * SECS_IN_MIN + 5.1, '5:05:05.10'], ], 'h:mm:ss.sss': [ [0, '0:00:00.000'], [1.111, '0:00:01.111'], [61.222, '0:01:01.222'], [1 * SECS_IN_MIN + 1.111, '0:01:01.111'], [61 * SECS_IN_MIN + 2.222, '1:01:02.222'], [24 * SECS_IN_HOUR + 3.333, '24:00:03.333'], [25 * SECS_IN_HOUR + 9.999, '25:00:09.999'], [49 * SECS_IN_HOUR, '49:00:00.000'], [50 * SECS_IN_HOUR, '50:00:00.000'], [32 * SECS_IN_HOUR, '32:00:00.000'], [5 * SECS_IN_HOUR, '5:00:00.000'], [5 * SECS_IN_HOUR + 30 * SECS_IN_MIN, '5:30:00.000'], [5 * SECS_IN_HOUR + 5 * SECS_IN_MIN + 5, '5:05:05.000'], [5 * SECS_IN_HOUR + 5 * SECS_IN_MIN + 5.1, '5:05:05.100'], ], } const DURATION_ROUNDING_VALUES = { 'd h': [ [null, null], [0, 0], [1.9, 0], [30, 0], [60, 0], [29 * SECS_IN_MIN, 0], [30 * SECS_IN_MIN, 1 * SECS_IN_HOUR], [59 * SECS_IN_MIN, 1 * SECS_IN_HOUR], [1 * SECS_IN_HOUR + 29 * SECS_IN_MIN, 1 * SECS_IN_HOUR], [1 * SECS_IN_HOUR + 30 * SECS_IN_MIN, 2 * SECS_IN_HOUR], ], 'd h:mm': [ [null, null], [0, 0], [1, 0], [29, 0], [30, 1 * SECS_IN_MIN], [59, 1 * SECS_IN_MIN], [29 * SECS_IN_MIN + 29.9, 29 * SECS_IN_MIN], [29 * SECS_IN_MIN + 30, 30 * SECS_IN_MIN], ], 'd h:mm:ss': [ [null, null], [0, 0], [0.499, 0], [0.5, 1], [0.9, 1], [1, 1], [1.5, 2], ], 'h:mm': [ [null, null], [0, 0], [1, 0], [29, 0], [30, 1 * SECS_IN_MIN], [59, 1 * SECS_IN_MIN], [29 * SECS_IN_MIN + 29.9, 29 * SECS_IN_MIN], [29 * SECS_IN_MIN + 30, 30 * SECS_IN_MIN], ], 'h:mm:ss': [ [null, null], [0, 0], [0.499, 0], [0.5, 1], [0.9, 1], [1, 1], [1.5, 2], ], 'h:mm:ss.s': [ [null, null], [0, 0], [0.449, 0.4], [0.45, 0.5], [0.99, 1], [1.12, 1.1], [1.9, 1.9], [1.99, 2], ], 'h:mm:ss.ss': [ [null, null], [0, 0], [0.49, 0.49], [0.494, 0.49], [0.499, 0.5], [0.999, 1], [1.123, 1.12], [1.99, 1.99], [1.999999, 2], ], 'h:mm:ss.sss': [ [null, null], [0, 0], [0.4994, 0.499], [0.4995, 0.5], [0.9991, 0.999], [0.9995, 1], [1.123, 1.123], [1.999, 1.999], [1.999999, 2], ], } describe('Duration field type utilities', () => { describe('parse format: d h', () => { for (const [input, expected] of VALID_DURATION_VALUES['d h']) { test(`"${input}" should be parsed to ${expected}`, () => { expect(parseDurationValue(input, 'd h')).toBe(expected) }) } }) describe('parse format: d h:mm', () => { for (const [input, expected] of VALID_DURATION_VALUES['d h:mm']) { test(`"${input}" should be parsed to ${expected}`, () => { expect(parseDurationValue(input, 'd h:mm')).toBe(expected) }) } }) describe('parse format: d h:mm:ss', () => { for (const [input, expected] of VALID_DURATION_VALUES['d h:mm:ss']) { test(`"${input}" should be parsed to ${expected}`, () => { expect(parseDurationValue(input, 'd h:mm:ss')).toBe(expected) }) } }) describe('parse format: h:mm', () => { for (const [input, expected] of VALID_DURATION_VALUES['h:mm']) { test(`"${input}" should be parsed to ${expected}`, () => { expect(parseDurationValue(input, 'h:mm')).toBe(expected) }) } }) describe('parse format: h:mm:ss', () => { for (const [input, expected] of VALID_DURATION_VALUES['h:mm:ss']) { test(`"${input}" should be parsed to ${expected}`, () => { expect(parseDurationValue(input, 'h:mm:ss')).toBe(expected) }) } }) describe('parse format: h:mm:ss.s', () => { for (const [input, expected] of VALID_DURATION_VALUES['h:mm:ss.s']) { test(`"${input}" should be parsed to ${expected}`, () => { expect(parseDurationValue(input, 'h:mm:ss.s')).toBe(expected) }) } }) describe('parse format: h:mm:ss.ss', () => { for (const [input, expected] of VALID_DURATION_VALUES['h:mm:ss.ss']) { test(`"${input}" should be parsed to ${expected}`, () => { expect(parseDurationValue(input, 'h:mm:ss.ss')).toBe(expected) }) } }) describe('parse format: h:mm:ss.sss', () => { for (const [input, expected] of VALID_DURATION_VALUES['h:mm:ss.sss']) { test(`"${input}" should be parsed to ${expected}`, () => { expect(parseDurationValue(input, 'h:mm:ss.sss')).toBe(expected) }) } }) describe('parse invalid values', () => { for (const input of INVALID_DURATION_VALUES) { test(`"${input}" should be parsed to null`, () => { expect(parseDurationValue(input, 'h:mm:ss.sss')).toBe(null) }) } }) describe('format format: d h', () => { for (const [input, expected] of DURATION_FORMATTED_VALUES['d h']) { test(`"${input}" should be formatted to ${expected}`, () => { expect(formatDurationValue(input, 'd h')).toBe(expected) }) } }) describe('format format: d h:mm', () => { for (const [input, expected] of DURATION_FORMATTED_VALUES['d h:mm']) { test(`"${input}" should be formatted to ${expected}`, () => { expect(formatDurationValue(input, 'd h:mm')).toBe(expected) }) } }) describe('format format: d h:mm:ss', () => { for (const [input, expected] of DURATION_FORMATTED_VALUES['d h:mm:ss']) { test(`"${input}" should be formatted to ${expected}`, () => { expect(formatDurationValue(input, 'd h:mm:ss')).toBe(expected) }) } }) describe('format format: h:mm', () => { for (const [input, expected] of DURATION_FORMATTED_VALUES['h:mm']) { test(`"${input}" should be formatted to ${expected}`, () => { expect(formatDurationValue(input, 'h:mm')).toBe(expected) }) } }) describe('format format: h:mm:ss', () => { for (const [input, expected] of DURATION_FORMATTED_VALUES['h:mm:ss']) { test(`"${input}" should be formatted to ${expected}`, () => { expect(formatDurationValue(input, 'h:mm:ss')).toBe(expected) }) } }) describe('format format: h:mm:ss.s', () => { for (const [input, expected] of DURATION_FORMATTED_VALUES['h:mm:ss.s']) { test(`"${input}" should be formatted to ${expected}`, () => { expect(formatDurationValue(input, 'h:mm:ss.s')).toBe(expected) }) } }) describe('format format: h:mm:ss.ss', () => { for (const [input, expected] of DURATION_FORMATTED_VALUES['h:mm:ss.ss']) { test(`"${input}" should be formatted to ${expected}`, () => { expect(formatDurationValue(input, 'h:mm:ss.ss')).toBe(expected) }) } }) describe('format format: h:mm:ss.sss', () => { for (const [input, expected] of DURATION_FORMATTED_VALUES['h:mm:ss.sss']) { test(`"${input}" should be formatted to ${expected}`, () => { expect(formatDurationValue(input, 'h:mm:ss.sss')).toBe(expected) }) } }) describe('round value: d h', () => { for (const [input, expected] of DURATION_ROUNDING_VALUES['d h']) { test(`"${input}" should be rounded to ${expected}`, () => { expect(roundDurationValueToFormat(input, 'd h')).toBe(expected) }) } }) describe('round value: d h:mm', () => { for (const [input, expected] of DURATION_ROUNDING_VALUES['d h:mm']) { test(`"${input}" should be rounded to ${expected}`, () => { expect(roundDurationValueToFormat(input, 'd h:mm')).toBe(expected) }) } }) describe('round value: d h:mm:ss', () => { for (const [input, expected] of DURATION_ROUNDING_VALUES['d h:mm:ss']) { test(`"${input}" should be rounded to ${expected}`, () => { expect(roundDurationValueToFormat(input, 'd h:mm:ss')).toBe(expected) }) } }) describe('round value: h:mm', () => { for (const [input, expected] of DURATION_ROUNDING_VALUES['h:mm']) { test(`"${input}" should be rounded to ${expected}`, () => { expect(roundDurationValueToFormat(input, 'h:mm')).toBe(expected) }) } }) describe('round value: h:mm:ss', () => { for (const [input, expected] of DURATION_ROUNDING_VALUES['h:mm:ss']) { test(`"${input}" should be rounded to ${expected}`, () => { expect(roundDurationValueToFormat(input, 'h:mm:ss')).toBe(expected) }) } }) describe('round value: h:mm:ss.s', () => { for (const [input, expected] of DURATION_ROUNDING_VALUES['h:mm:ss.s']) { test(`"${input}" should be rounded to ${expected}`, () => { expect(roundDurationValueToFormat(input, 'h:mm:ss.s')).toBe(expected) }) } }) describe('round value: h:mm:ss.ss', () => { for (const [input, expected] of DURATION_ROUNDING_VALUES['h:mm:ss.ss']) { test(`"${input}" should be rounded to ${expected}`, () => { expect(roundDurationValueToFormat(input, 'h:mm:ss.ss')).toBe(expected) }) } }) describe('round value: h:mm:ss.sss', () => { for (const [input, expected] of DURATION_ROUNDING_VALUES['h:mm:ss.sss']) { test(`"${input}" should be rounded to ${expected}`, () => { expect(roundDurationValueToFormat(input, 'h:mm:ss.sss')).toBe(expected) }) } }) })