diff --git a/packages/webui/src/client/lib/Components/CounterComponents.tsx b/packages/webui/src/client/lib/Components/CounterComponents.tsx
index 504969259d..fec18f2f66 100644
--- a/packages/webui/src/client/lib/Components/CounterComponents.tsx
+++ b/packages/webui/src/client/lib/Components/CounterComponents.tsx
@@ -9,7 +9,7 @@ export const OverUnderClockComponent = (props: OverUnderProps): JSX.Element => {
return (
- {RundownUtils.formatDiffToTimecode(props.value, true, false, true, true, true, undefined, true, true)}
+ {RundownUtils.formatDiffToTimecodeOverUnder(props.value, true)}
)
@@ -26,7 +26,7 @@ export const PlannedEndComponent = (props: OverUnderProps): JSX.Element => {
export const TimeToPlannedEndComponent = (props: OverUnderProps): JSX.Element => {
return (
- {RundownUtils.formatDiffToTimecode(props.value, true, false, true, true, true, undefined, true, true)}
+ {RundownUtils.formatDiffToTimecodeOverUnder(props.value, true)}
)
}
@@ -34,7 +34,7 @@ export const TimeToPlannedEndComponent = (props: OverUnderProps): JSX.Element =>
export const TimeSincePlannedEndComponent = (props: OverUnderProps): JSX.Element => {
return (
- {RundownUtils.formatDiffToTimecode(props.value, true, false, true, true, true, undefined, true, true)}
+ {RundownUtils.formatDiffToTimecodeOverUnder(props.value, true)}
)
}
diff --git a/packages/webui/src/client/lib/rundown.ts b/packages/webui/src/client/lib/rundown.ts
index 0e99d5d45a..700c506131 100644
--- a/packages/webui/src/client/lib/rundown.ts
+++ b/packages/webui/src/client/lib/rundown.ts
@@ -265,6 +265,36 @@ export namespace RundownUtils {
)
}
+ /** Format a duration (ms) to a timecode string, showing hours only when needed. e.g. "23:45" or "1:23:45" */
+ export function formatDiffToTimecodeHours(milliseconds: number): string {
+ return formatDiffToTimecode(milliseconds, false, true, true, false, true)
+ }
+
+ /**
+ * Format a live countdown timer (ms). Shows "+" for positive, blank prefix when negative (rounds toward zero),
+ * using hard-floor rounding so the display doesn't jump ahead of the actual value.
+ */
+ export function formatDiffToTimecodeCountdown(milliseconds: number): string {
+ return formatDiffToTimecode(milliseconds, true, false, true, false, true, '', false, true)
+ }
+
+ /**
+ * Format a budget/remaining duration (ms) with a "+" prefix for positive values (time remaining)
+ * and an en-dash for negative values (over budget).
+ */
+ export function formatDiffToTimecodeWithSign(milliseconds: number): string {
+ return formatDiffToTimecode(milliseconds, false, false, true, false, true, '+')
+ }
+
+ /**
+ * Format an over/under diff (ms) with "+" for positive, en-dash for negative, smart-floor rounding,
+ * and smart-hours. Used for start-time diffs and over/under clocks.
+ * Pass `floorTime: true` to use hard-floor rounding (display won't jump ahead of the actual value).
+ */
+ export function formatDiffToTimecodeOverUnder(milliseconds: number, floorTime?: boolean): string {
+ return formatDiffToTimecode(milliseconds, true, false, true, true, true, undefined, floorTime, floorTime)
+ }
+
export function isInsideViewport(
scrollLeft: number,
scrollWidth: number,
diff --git a/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx b/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx
index 3ff4cc99ba..17bc1d0d07 100644
--- a/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx
+++ b/packages/webui/src/client/ui/ClockView/TTimerDisplay.tsx
@@ -18,7 +18,7 @@ export function TTimerDisplay({ timer }: Readonly): JSX.Elem
const diff = calculateTTimerDiff(timer, now)
const overUnder = calculateTTimerOverUnder(timer, now)
- const timeStr = RundownUtils.formatDiffToTimecode(Math.abs(diff), false, true, true, false, true)
+ const timeStr = RundownUtils.formatDiffToTimecodeHours(Math.abs(diff))
const timerSign = diff >= 0 ? '' : '-'
return (
diff --git a/packages/webui/src/client/ui/ClockView/Timediff.tsx b/packages/webui/src/client/ui/ClockView/Timediff.tsx
index 2f6b93b265..928754add6 100644
--- a/packages/webui/src/client/ui/ClockView/Timediff.tsx
+++ b/packages/webui/src/client/ui/ClockView/Timediff.tsx
@@ -4,7 +4,7 @@ import { RundownUtils } from '../../lib/rundown.js'
export function Timediff({ time: rawTime }: { time: number }): JSX.Element {
const time = -rawTime
const isNegative = Math.floor(time / 1000) > 0
- const timeString = RundownUtils.formatDiffToTimecode(time, true, false, true, false, true, '', false, true)
+ const timeString = RundownUtils.formatDiffToTimecodeCountdown(time)
return (
{t('({{timecode}})', {
- timecode: RundownUtils.formatDiffToTimecode(expectedDuration, false, true, true, false, true),
+ timecode: RundownUtils.formatDiffToTimecodeHours(expectedDuration),
})}
) : (
- RundownUtils.formatDiffToTimecode(expectedDuration, false, true, true, false, true)
+ RundownUtils.formatDiffToTimecodeHours(expectedDuration)
)
) : isOnlyRundownInPlaylist && isLoopDefined(playlist) ? (
{t('({{timecode}})', {
- timecode: RundownUtils.formatDiffToTimecode(playlistExpectedDuration, false, true, true, false, true),
+ timecode: RundownUtils.formatDiffToTimecodeHours(playlistExpectedDuration),
})}
) : (
- RundownUtils.formatDiffToTimecode(playlistExpectedDuration, false, true, true, false, true)
+ RundownUtils.formatDiffToTimecodeHours(playlistExpectedDuration)
))
return (
diff --git a/packages/webui/src/client/ui/RundownView/RundownDividerHeader.tsx b/packages/webui/src/client/ui/RundownView/RundownDividerHeader.tsx
index 6b0551b8ec..2689a35790 100644
--- a/packages/webui/src/client/ui/RundownView/RundownDividerHeader.tsx
+++ b/packages/webui/src/client/ui/RundownView/RundownDividerHeader.tsx
@@ -86,7 +86,7 @@ export function RundownDividerHeader({ rundown, playlist }: IProps): JSX.Element
{expectedDuration ? (
{t('Planned Duration')}
- {RundownUtils.formatDiffToTimecode(expectedDuration, false, true, true, false, true)}
+ {RundownUtils.formatDiffToTimecodeHours(expectedDuration)}
) : null}
{expectedEnd ? (
diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/CurrentPartOrSegmentRemaining.tsx b/packages/webui/src/client/ui/RundownView/RundownHeader/CurrentPartOrSegmentRemaining.tsx
index a1d97e76fd..08146caaeb 100644
--- a/packages/webui/src/client/ui/RundownView/RundownHeader/CurrentPartOrSegmentRemaining.tsx
+++ b/packages/webui/src/client/ui/RundownView/RundownHeader/CurrentPartOrSegmentRemaining.tsx
@@ -147,7 +147,7 @@ export const CurrentPartOrSegmentRemaining: React.FC = (pro
className={ClassNames(props.className, Math.floor(displayTimecode / 1000) > 0 ? props.heavyClassName : undefined)}
role="timer"
>
- {RundownUtils.formatDiffToTimecode(displayTimecode || 0, true, false, true, false, true, '', false, true)}
+ {RundownUtils.formatDiffToTimecodeCountdown(displayTimecode || 0)}
)
}
@@ -166,7 +166,7 @@ export const RundownHeaderPartRemaining: React.FC = (props)
label={props.label}
className={ClassNames(props.className, Math.floor(displayTimecode / 1000) > 0 ? props.heavyClassName : undefined)}
>
- {RundownUtils.formatDiffToTimecode(displayTimecode || 0, true, false, true, false, true, '', false, true)}
+ {RundownUtils.formatDiffToTimecodeCountdown(displayTimecode || 0)}
)
}
@@ -187,7 +187,7 @@ export const RundownHeaderSegmentBudget: React.FC<{
{label}
0 ? 'overtime' : undefined)}>
- {RundownUtils.formatDiffToTimecode(displayTimecode || 0, true, false, true, false, true, '', false, true)}
+ {RundownUtils.formatDiffToTimecodeCountdown(displayTimecode || 0)}
)
diff --git a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx
index 176eb08a25..65dd18919a 100644
--- a/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx
+++ b/packages/webui/src/client/ui/RundownView/RundownHeader/RundownHeaderTimers.tsx
@@ -40,7 +40,7 @@ function SingleTimer({ timer }: Readonly) {
const diff = calculateTTimerDiff(timer, now)
const overUnder = calculateTTimerOverUnder(timer, now)
- const timeStr = RundownUtils.formatDiffToTimecode(Math.abs(diff), false, true, true, false, true)
+ const timeStr = RundownUtils.formatDiffToTimecodeHours(Math.abs(diff))
const isCountingDown = mode.type === 'countdown' && diff < 0 && isRunning
return (
diff --git a/packages/webui/src/client/ui/RundownView/RundownTiming/CurrentPartElapsed.tsx b/packages/webui/src/client/ui/RundownView/RundownTiming/CurrentPartElapsed.tsx
index f69443b985..f69a2be1b5 100644
--- a/packages/webui/src/client/ui/RundownView/RundownTiming/CurrentPartElapsed.tsx
+++ b/packages/webui/src/client/ui/RundownView/RundownTiming/CurrentPartElapsed.tsx
@@ -19,7 +19,7 @@ export function CurrentPartElapsed({ currentPartId, className }: IPartElapsedPro
return (
- {RundownUtils.formatDiffToTimecode(displayTimecode || 0, true, false, true, false, true, '', false, true)}
+ {RundownUtils.formatDiffToTimecodeCountdown(displayTimecode || 0)}
)
}
diff --git a/packages/webui/src/client/ui/RundownView/RundownTiming/PartDuration.tsx b/packages/webui/src/client/ui/RundownView/RundownTiming/PartDuration.tsx
index b3200d436b..1d7793465f 100644
--- a/packages/webui/src/client/ui/RundownView/RundownTiming/PartDuration.tsx
+++ b/packages/webui/src/client/ui/RundownView/RundownTiming/PartDuration.tsx
@@ -48,15 +48,15 @@ export function PartDisplayDuration(props: IPartDurationProps): JSX.Element | nu
{props.label}
{props.fixed ? (
- {RundownUtils.formatDiffToTimecode(budget, false, false, true, false, true, '+')}
+ {RundownUtils.formatDiffToTimecodeWithSign(budget)}
) : props.countUp ? (
- {RundownUtils.formatDiffToTimecode(playedOut, false, false, true, false, true, '+')}
+ {RundownUtils.formatDiffToTimecodeWithSign(playedOut)}
) : (
- {RundownUtils.formatDiffToTimecode(duration, false, false, true, false, true, '+')}
+ {RundownUtils.formatDiffToTimecodeWithSign(duration)}
)}
>
diff --git a/packages/webui/src/client/ui/RundownView/RundownTiming/PlaylistEndTiming.tsx b/packages/webui/src/client/ui/RundownView/RundownTiming/PlaylistEndTiming.tsx
index 5cc865342d..12158d57fa 100644
--- a/packages/webui/src/client/ui/RundownView/RundownTiming/PlaylistEndTiming.tsx
+++ b/packages/webui/src/client/ui/RundownView/RundownTiming/PlaylistEndTiming.tsx
@@ -55,7 +55,7 @@ export function PlaylistEndTiming({
role="timer"
>
{!hideDiffLabel && {t('Diff')}}
- {RundownUtils.formatDiffToTimecode(overUnderClock, true, false, true, true, true, undefined, true, true)}
+ {RundownUtils.formatDiffToTimecodeOverUnder(overUnderClock, true)}
) : null
) : null}
diff --git a/packages/webui/src/client/ui/RundownView/RundownTiming/RundownName.tsx b/packages/webui/src/client/ui/RundownView/RundownTiming/RundownName.tsx
index d41e581732..af16785576 100644
--- a/packages/webui/src/client/ui/RundownView/RundownTiming/RundownName.tsx
+++ b/packages/webui/src/client/ui/RundownView/RundownTiming/RundownName.tsx
@@ -76,17 +76,8 @@ export function RundownName({
)}
{!hideDiff && rundownPlaylist.startedPlayback && rundownPlaylist.activationId && !rundownPlaylist.rehearsal
- ? expectedStart &&
- RundownUtils.formatDiffToTimecode(
- rundownPlaylist.startedPlayback - expectedStart,
- true,
- false,
- true,
- true,
- true
- )
- : expectedStart &&
- RundownUtils.formatDiffToTimecode(getCurrentTime() - expectedStart, true, false, true, true, true)}
+ ? expectedStart && RundownUtils.formatDiffToTimecodeOverUnder(rundownPlaylist.startedPlayback - expectedStart)
+ : expectedStart && RundownUtils.formatDiffToTimecodeOverUnder(getCurrentTime() - expectedStart)}
)
}
diff --git a/packages/webui/src/client/ui/SegmentList/LinePart.tsx b/packages/webui/src/client/ui/SegmentList/LinePart.tsx
index 6a30af3d0a..2fc8994942 100644
--- a/packages/webui/src/client/ui/SegmentList/LinePart.tsx
+++ b/packages/webui/src/client/ui/SegmentList/LinePart.tsx
@@ -112,17 +112,7 @@ export function LinePart({
return (
<>
{part.instance.part.expectedDuration !== undefined && part.instance.part.expectedDuration > 0 && (
-
- {RundownUtils.formatDiffToTimecode(
- part.instance.part.expectedDuration,
- false,
- false,
- true,
- false,
- true,
- '+'
- )}
-
+ {RundownUtils.formatDiffToTimecodeWithSign(part.instance.part.expectedDuration)}
)}
{(part.instance.part.expectedDuration === 0 || part.instance.part.expectedDuration === undefined) && (
––:––
diff --git a/packages/webui/src/client/ui/SegmentStoryboard/StoryboardPartThumbnail/Renderers/VTThumbnailRenderer.tsx b/packages/webui/src/client/ui/SegmentStoryboard/StoryboardPartThumbnail/Renderers/VTThumbnailRenderer.tsx
index d01d4752a7..aba1a82365 100644
--- a/packages/webui/src/client/ui/SegmentStoryboard/StoryboardPartThumbnail/Renderers/VTThumbnailRenderer.tsx
+++ b/packages/webui/src/client/ui/SegmentStoryboard/StoryboardPartThumbnail/Renderers/VTThumbnailRenderer.tsx
@@ -97,9 +97,7 @@ function VTThumbnailRendererWithTiming({
>
- {contentLeft > 0 ? (
- {RundownUtils.formatDiffToTimecode(contentLeft, false, false, true, false, true, '+')}
- ) : null}
+ {contentLeft > 0 ? {RundownUtils.formatDiffToTimecodeWithSign(contentLeft)} : null}
) : null
}
diff --git a/packages/webui/src/client/ui/Shelf/PieceCountdownPanel.tsx b/packages/webui/src/client/ui/Shelf/PieceCountdownPanel.tsx
index ec1e4335e9..e5b3e8d55e 100644
--- a/packages/webui/src/client/ui/Shelf/PieceCountdownPanel.tsx
+++ b/packages/webui/src/client/ui/Shelf/PieceCountdownPanel.tsx
@@ -83,7 +83,7 @@ export function PieceCountdownPanel({
overtime: Math.floor(displayTimecode / 1000) > 0,
})}
>
- {RundownUtils.formatDiffToTimecode(displayTimecode || 0, true, false, true, false, true, '', false, true)}
+ {RundownUtils.formatDiffToTimecodeCountdown(displayTimecode || 0)}
)