Refactor: better rounding (#1594)

This commit is contained in:
Alex Christoffer Rasmussen
2025-06-05 06:13:49 +02:00
committed by Carlos Valente
parent c1054711b0
commit b9ab1c6fd7
10 changed files with 93 additions and 181 deletions
@@ -1,4 +1,4 @@
import { millisToHours, millisToMinutes, millisToSeconds, secondsInMillis } from './conversionUtils';
import { millisToSeconds, secondsInMillis } from './conversionUtils';
describe('millisToSecond()', () => {
test('null values', () => {
@@ -37,82 +37,6 @@ describe('millisToSecond()', () => {
});
});
describe('millisToMinutes()', () => {
test('null values', () => {
const t = { val: null, result: 0 };
expect(millisToMinutes(t.val)).toBe(t.result);
});
test('valid millis', () => {
const t = { val: 3600000, result: 60 };
expect(millisToMinutes(t.val)).toBe(t.result);
});
test('negative millis', () => {
const t = { val: -3600000, result: -60 };
expect(millisToMinutes(t.val)).toBe(t.result);
});
test('0', () => {
const t = { val: 0, result: 0 };
expect(millisToMinutes(t.val)).toBe(t.result);
});
test('-0', () => {
const t = { val: -0, result: 0 };
expect(millisToMinutes(t.val)).toBe(t.result);
});
test('86401000 (24 hours and 1 second)', () => {
const t = { val: 86401000, result: 1440 };
expect(millisToMinutes(t.val)).toBe(t.result);
});
test('-86401000 (-24 hours and 1 second)', () => {
// negative numbers are rounded up
const t = { val: -86401000, result: -1441 };
expect(millisToMinutes(t.val)).toBe(t.result);
});
});
describe('millisToHours()', () => {
test('null values', () => {
const t = { val: null, result: 0 };
expect(millisToHours(t.val)).toBe(t.result);
});
test('valid millis', () => {
const t = { val: 3600000, result: 1 };
expect(millisToHours(t.val)).toBe(t.result);
});
test('negative millis', () => {
const t = { val: -3600000, result: -1 };
expect(millisToHours(t.val)).toBe(t.result);
});
test('0', () => {
const t = { val: 0, result: 0 };
expect(millisToHours(t.val)).toBe(t.result);
});
test('-0', () => {
const t = { val: -0, result: 0 };
expect(millisToHours(t.val)).toBe(t.result);
});
test('86401000 (24 hours and 1 second)', () => {
const t = { val: 86401000, result: 24 };
expect(millisToHours(t.val)).toBe(t.result);
});
test('-86401000 (-24 hours and 1 second)', () => {
// negative numbers are rounded up
const t = { val: -86401000, result: -25 };
expect(millisToHours(t.val)).toBe(t.result);
});
});
describe('secondsInMillis()', () => {
it('return 0 if value is null', () => {
expect(secondsInMillis(null)).toBe(0);
@@ -1,4 +1,5 @@
import type { MaybeNumber } from 'ontime-types';
import { TimerType } from 'ontime-types';
export const MILLIS_PER_SECOND = 1000;
export const MILLIS_PER_MINUTE = 1000 * 60;
@@ -7,44 +8,28 @@ export const MILLIS_PER_HOUR = 1000 * 60 * 60;
export const dayInMs = 86400000;
export const maxDuration = dayInMs - MILLIS_PER_SECOND;
/**
* Utility converts milliseconds to a specific unit
* @param millis
* @param conversion
* @returns
*/
function convertMillis(millis: MaybeNumber, conversion: number): number {
if (!millis) {
return 0;
}
return Math.floor(millis / conversion);
}
/**
* Converts value in milliseconds to seconds
* @param millis
* @returns
*/
export function millisToSeconds(millis: MaybeNumber): number {
return convertMillis(millis, MILLIS_PER_SECOND);
}
export function millisToSeconds(
millis: MaybeNumber,
direction: TimerType.CountDown | TimerType.CountUp = TimerType.CountDown,
) {
if (millis === null) return 0;
/**
* Converts value in milliseconds to minutes
* @param millis
* @returns
*/
export function millisToMinutes(millis: MaybeNumber): number {
return convertMillis(millis, MILLIS_PER_MINUTE);
}
let seconds = 0;
if (direction === TimerType.CountDown) {
seconds = Math.ceil(millis / MILLIS_PER_SECOND);
}
/**
* Converts value in milliseconds to hours
* @param millis
* @returns
*/
export function millisToHours(millis: MaybeNumber): number {
return convertMillis(millis, MILLIS_PER_HOUR);
if (direction === TimerType.CountUp) {
seconds = Math.floor(millis / MILLIS_PER_SECOND);
}
// this is there to avoid result giving -0
return seconds === 0 ? 0 : seconds;
}
/**
@@ -1,3 +1,5 @@
import { TimerType } from 'ontime-types';
import { dayInMs, MILLIS_PER_HOUR } from './conversionUtils';
import { formatFromMillis, millisToString, removeLeadingZero } from './timeFormatting';
@@ -13,32 +15,33 @@ describe('millisToString()', () => {
test('negative times are rounded up', () => {
const testScenarios = [
{ millis: 300, expected: '00:00:00' },
{ millis: -300, expected: '-00:00:01' },
{ millis: 1000, expected: '00:00:01' },
{ millis: -1000, expected: '-00:00:01' },
{ millis: 1500, expected: '00:00:01' },
{ millis: -1500, expected: '-00:00:02' },
{ millis: 60000 - 1, expected: '00:00:59' },
{ millis: -(60000 - 1), expected: '-00:01:00' },
{ millis: 60000, expected: '00:01:00' },
{ millis: -60000, expected: '-00:01:00' },
{ millis: 600000, expected: '00:10:00' },
{ millis: -600000, expected: '-00:10:00' },
{ millis: 3600000, expected: '01:00:00' },
{ millis: -3600000, expected: '-01:00:00' },
{ millis: 36000000, expected: '10:00:00' },
{ millis: -36000000, expected: '-10:00:00' },
{ millis: 86399000, expected: '23:59:59' },
{ millis: -86399000, expected: '-23:59:59' },
{ millis: 86400000, expected: '24:00:00' },
{ millis: -86400000, expected: '-24:00:00' },
{ millis: 86401000, expected: '24:00:01' },
{ millis: -86401000, expected: '-24:00:01' },
{ millis: 300, expected_down: '00:00:01', expected_up: '00:00:00' },
{ millis: -300, expected_down: '-00:00:00', expected_up: '-00:00:01' },
{ millis: 1000, expected_down: '00:00:01', expected_up: '00:00:01' },
{ millis: -1000, expected_down: '-00:00:01', expected_up: '-00:00:01' },
{ millis: 1500, expected_down: '00:00:02', expected_up: '00:00:01' },
{ millis: -1500, expected_down: '-00:00:01', expected_up: '-00:00:02' },
{ millis: 60000 - 1, expected_down: '00:01:00', expected_up: '00:00:59' },
{ millis: -(60000 - 1), expected_down: '-00:00:59', expected_up: '-00:01:00' },
{ millis: 60000, expected_down: '00:01:00', expected_up: '00:01:00' },
{ millis: -60000, expected_down: '-00:01:00', expected_up: '-00:01:00' },
{ millis: 600000, expected_down: '00:10:00', expected_up: '00:10:00' },
{ millis: -600000, expected_down: '-00:10:00', expected_up: '-00:10:00' },
{ millis: 3600000, expected_down: '01:00:00', expected_up: '01:00:00' },
{ millis: -3600000, expected_down: '-01:00:00', expected_up: '-01:00:00' },
{ millis: 36000000, expected_down: '10:00:00', expected_up: '10:00:00' },
{ millis: -36000000, expected_down: '-10:00:00', expected_up: '-10:00:00' },
{ millis: 86399000, expected_down: '23:59:59', expected_up: '23:59:59' },
{ millis: -86399000, expected_down: '-23:59:59', expected_up: '-23:59:59' },
{ millis: 86400000, expected_down: '24:00:00', expected_up: '24:00:00' },
{ millis: -86400000, expected_down: '-24:00:00', expected_up: '-24:00:00' },
{ millis: 86401000, expected_down: '24:00:01', expected_up: '24:00:01' },
{ millis: -86401000, expected_down: '-24:00:01', expected_up: '-24:00:01' },
];
testScenarios.forEach((scenario) => {
expect(millisToString(scenario.millis)).toBe(scenario.expected);
expect(millisToString(scenario.millis, { direction: TimerType.CountDown })).toBe(scenario.expected_down);
expect(millisToString(scenario.millis, { direction: TimerType.CountUp })).toBe(scenario.expected_up);
});
});
@@ -48,20 +51,21 @@ describe('millisToString()', () => {
test('random properties', () => {
const testScenarios = [
{ millis: 300, expected: '00:00:00' },
{ millis: 1000, expected: '00:00:01' },
{ millis: 1500, expected: '00:00:01' },
{ millis: 60000, expected: '00:01:00' },
{ millis: 600000, expected: '00:10:00' },
{ millis: 3600000, expected: '01:00:00' },
{ millis: 36000000, expected: '10:00:00' },
{ millis: 86399000, expected: '23:59:59' },
{ millis: 86400000, expected: '24:00:00' },
{ millis: 86401000, expected: '24:00:01' },
{ millis: 300, expected_down: '00:00:01', expected_up: '00:00:00' },
{ millis: 1000, expected_down: '00:00:01', expected_up: '00:00:01' },
{ millis: 1500, expected_down: '00:00:02', expected_up: '00:00:01' },
{ millis: 60000, expected_down: '00:01:00', expected_up: '00:01:00' },
{ millis: 600000, expected_down: '00:10:00', expected_up: '00:10:00' },
{ millis: 3600000, expected_down: '01:00:00', expected_up: '01:00:00' },
{ millis: 36000000, expected_down: '10:00:00', expected_up: '10:00:00' },
{ millis: 86399000, expected_down: '23:59:59', expected_up: '23:59:59' },
{ millis: 86400000, expected_down: '24:00:00', expected_up: '24:00:00' },
{ millis: 86401000, expected_down: '24:00:01', expected_up: '24:00:01' },
];
testScenarios.forEach((scenario) => {
expect(millisToString(scenario.millis)).toBe(scenario.expected);
expect(millisToString(scenario.millis, { direction: TimerType.CountDown })).toBe(scenario.expected_down);
expect(millisToString(scenario.millis, { direction: TimerType.CountUp })).toBe(scenario.expected_up);
});
});
});
@@ -1,4 +1,4 @@
import type { MaybeNumber } from 'ontime-types';
import type { MaybeNumber, TimerType } from 'ontime-types';
import { millisToSeconds, secondsToHours, secondsToMinutes } from './conversionUtils.js';
@@ -8,6 +8,7 @@ export function pad(val: number): string {
type FormatOptions = {
fallback?: string;
direction?: TimerType.CountDown | TimerType.CountUp;
};
/**
@@ -23,7 +24,7 @@ export function millisToString(millis?: MaybeNumber, options?: FormatOptions): s
const isNegative = millis < 0;
const totalSeconds = Math.abs(millisToSeconds(millis));
const totalSeconds = Math.abs(millisToSeconds(millis, options?.direction));
const seconds = totalSeconds % 60;
const minutes = secondsToMinutes(totalSeconds) % 60;
const hours = secondsToHours(totalSeconds);