feat(MUI-39) : timeFormat parse/format HH:MM pour le sélecteur d'heure

This commit is contained in:
2026-05-27 09:55:22 +02:00
parent 50699bd582
commit 661910a8d9
2 changed files with 63 additions and 0 deletions
@@ -0,0 +1,31 @@
import {describe, expect, it} from 'vitest'
import {clampHours, clampMinutes, formatTime, padSegment, parseTime} from './timeFormat'
describe('timeFormat', () => {
it('parse une chaîne HH:MM valide', () => {
expect(parseTime('09:05')).toEqual({hours: 9, minutes: 5})
})
it('renvoie null pour vide ou invalide', () => {
expect(parseTime('')).toBeNull()
expect(parseTime(null)).toBeNull()
expect(parseTime('abc')).toBeNull()
expect(parseTime('12')).toBeNull()
})
it('clamp les valeurs hors bornes au parsing', () => {
expect(parseTime('99:88')).toEqual({hours: 23, minutes: 59})
})
it('formate avec zéro-padding', () => {
expect(formatTime(9, 5)).toBe('09:05')
expect(formatTime(0, 0)).toBe('00:00')
})
it('clamp et pad les helpers', () => {
expect(clampHours(30)).toBe(23)
expect(clampHours(-2)).toBe(0)
expect(clampMinutes(75)).toBe(59)
expect(padSegment(7)).toBe('07')
})
})
@@ -0,0 +1,32 @@
export interface TimeParts {
hours: number
minutes: number
}
export function clampHours(value: number): number {
if (Number.isNaN(value)) return 0
return Math.min(23, Math.max(0, Math.trunc(value)))
}
export function clampMinutes(value: number): number {
if (Number.isNaN(value)) return 0
return Math.min(59, Math.max(0, Math.trunc(value)))
}
export function padSegment(value: number): string {
return value.toString().padStart(2, '0')
}
export function parseTime(value: string | null | undefined): TimeParts | null {
if (!value) return null
const match = /^(\d{1,2}):(\d{1,2})$/.exec(value.trim())
if (!match) return null
return {
hours: clampHours(Number.parseInt(match[1], 10)),
minutes: clampMinutes(Number.parseInt(match[2], 10)),
}
}
export function formatTime(hours: number, minutes: number): string {
return `${padSegment(clampHours(hours))}:${padSegment(clampMinutes(minutes))}`
}