FAILURE MAP
← Case archive

FA-78211 / Subtitle cue timing / Open access

Timed-text time expression evaluation: clock rounding · case 01

Clock times with half-millisecond frames end up one millisecond early.

Verified by executionVariant 1 · 9 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The exact clock value is truncated instead of rounded half up.

VERIFIED REPAIR

Round the exact clock value half up.

Unsuccessful approach: Round-half-even rounds some exact halves down.

Case contract

Evaluate a timed-text time expression to integer ms, rounded half up. Offset form <number><metric> with metric h, m, s, ms, f (frames at frame_rate) or t (ticks at tick_rate). Clock form HH:MM:SS with optional .fraction (any digits) or :FF frames (FF < frame_rate). Minutes/seconds above 59 or malformed text give None.

Why this case matters

Subtitle timing defects shift, hide or overlap captions that viewers depend on for comprehension and accessibility.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
import re
from fractions import Fraction
N = 1
observations = []
def solve(expr, frame_rate, tick_rate):
    m=re.fullmatch(r'(\d+(?:\.\d+)?)(h|ms|m|s|f|t)',expr)
    if m:
        v=Fraction(m.group(1))
        unit=m.group(2)
        scale={'h':3600000,'m':60000,'s':1000,'ms':1}
        if unit in scale:
            x=v*scale[unit]
        elif unit=='f':
            x=v*1000/frame_rate
        else:
            x=v*1000/tick_rate
        return math.floor(x+Fraction(1,2))
    m=re.fullmatch(r'(\d{2,}):(\d{2}):(\d{2})(?:\.(\d+)|:(\d{2,}))?',expr)
    if not m:
        return None
    h,mi,se,frac,fr=m.groups()
    if int(mi)>59 or int(se)>59:
        return None
    x=Fraction(int(h)*3600+int(mi)*60+int(se))*1000
    if frac:
        x+=Fraction('0.'+frac)*1000
    if fr:
        if int(fr)>=frame_rate:
            return None
        x+=Fraction(int(fr)*1000,frame_rate)
    return int(x)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: clock rounding', ['00:00:01:15', 16, 1000], 1938), ('regression variant: clock rounding', ['10:59:59.0005', 50, 90000], 39599001), ('partial repair probe: clock rounding', ['100:59:00.0005', 16, 3], 363540001), ('partial repair variant: clock rounding', ['10:30:00.0005', 24, 90000], 37800001), ('boundary control', ['10t', 25, 1000], 10), ('boundary control', ['3f', 16, 1000], 188), ('normal control', ['00:00:00:25', 25, 1000], None), ('normal control', ['10:59:59:15', 40, 1000], 39599375), ('normal control', ['15h', 50, 1000], 54000000)], [('regression: clock rounding', ['00:00:01.0005', 25, 1000], 1001), ('regression variant: clock rounding', ['10:59:59:29', 30, 90000], 39599967), ('partial repair probe: clock rounding', ['00:00:00:01', 16, 1000], 63), ('partial repair variant: clock rounding', ['100:59:59.0005', 40, 1000], 363599001), ('boundary control', ['3f', 16, 1000], 188), ('boundary control', ['00:00:00:25', 25, 1000], None), ('normal control', ['15m', 30, 1000], 900000), ('normal control', ['10:59:00:15', 40, 90000], 39540375), ('normal control', ['00:00:59:12', 30, 10000000], 59400)], [('regression: clock rounding', ['00:30:00:15', 16, 10000000], 1800938), ('regression variant: clock rounding', ['100:59:00.0005', 16, 3], 363540001), ('partial repair probe: clock rounding', ['100:30:59.0005', 40, 3], 361859001), ('partial repair variant: clock rounding', ['10:00:00.0005', 50, 3], 36000001), ('boundary control', ['00:00:00:25', 25, 1000], None), ('boundary control', ['10t', 25, 1000], 10), ('normal control', ['100:30:59:30', 25, 10000000], None), ('normal control', ['15t', 30, 3], 5000), ('normal control', ['01:60:05:12', 16, 10000000], None)], [('regression: clock rounding', ['00:30:00:01', 24, 1000], 1800042), ('regression variant: clock rounding', ['00:00:00:01', 16, 1000], 63), ('partial repair probe: clock rounding', ['01:30:05.0005', 30, 1000], 5405001), ('partial repair variant: clock rounding', ['10:30:05.0005', 30, 90000], 37805001), ('boundary control', ['10t', 25, 1000], 10), ('boundary control', ['3f', 16, 1000], 188), ('normal control', ['0t', 24, 3], 0), ('normal control', ['1.5t', 16, 3], 500), ('normal control', ['10:00:00.25', 16, 3], 36000250)], [('regression: clock rounding', ['01:59:59:01', 24, 3], 7199042), ('regression variant: clock rounding', ['100:30:00:15', 16, 90000], 361800938), ('partial repair probe: clock rounding', ['00:30:00.0005', 40, 3], 1800001), ('partial repair variant: clock rounding', ['00:59:00.0005', 24, 10000000], 3540001), ('boundary control', ['3f', 16, 1000], 188), ('boundary control', ['00:00:00:25', 25, 1000], None), ('normal control', ['10:30:59.5', 24, 1000], 37859500), ('normal control', ['01:30:05:25', 16, 90000], None), ('normal control', ['17h', 24, 10000000], 61200000)]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression: clock rounding19371938Failed
regression variant: clock rounding3959900039599001Failed
partial repair probe: clock rounding363540000363540001Failed
partial repair variant: clock rounding3780000037800001Failed
boundary control1010Passed
boundary control188188Passed
normal controlNoneNonePassed
normal control3959937539599375Passed
normal control5400000054000000Passed

SHA-256 / 592d73b83b103d937aa8fdf8ed6df909117f69ddd627e3a6acbf4b7d7718a9e7

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
import re
from fractions import Fraction
N = 1
observations = []
def solve(expr, frame_rate, tick_rate):
    m=re.fullmatch(r'(\d+(?:\.\d+)?)(h|ms|m|s|f|t)',expr)
    if m:
        v=Fraction(m.group(1))
        unit=m.group(2)
        scale={'h':3600000,'m':60000,'s':1000,'ms':1}
        if unit in scale:
            x=v*scale[unit]
        elif unit=='f':
            x=v*1000/frame_rate
        else:
            x=v*1000/tick_rate
        return math.floor(x+Fraction(1,2))
    m=re.fullmatch(r'(\d{2,}):(\d{2}):(\d{2})(?:\.(\d+)|:(\d{2,}))?',expr)
    if not m:
        return None
    h,mi,se,frac,fr=m.groups()
    if int(mi)>59 or int(se)>59:
        return None
    x=Fraction(int(h)*3600+int(mi)*60+int(se))*1000
    if frac:
        x+=Fraction('0.'+frac)*1000
    if fr:
        if int(fr)>=frame_rate:
            return None
        x+=Fraction(int(fr)*1000,frame_rate)
    return round(x)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: clock rounding', ['00:00:01:15', 16, 1000], 1938), ('regression variant: clock rounding', ['10:59:59.0005', 50, 90000], 39599001), ('partial repair probe: clock rounding', ['100:59:00.0005', 16, 3], 363540001), ('partial repair variant: clock rounding', ['10:30:00.0005', 24, 90000], 37800001), ('boundary control', ['10t', 25, 1000], 10), ('boundary control', ['3f', 16, 1000], 188), ('normal control', ['00:00:00:25', 25, 1000], None), ('normal control', ['10:59:59:15', 40, 1000], 39599375), ('normal control', ['15h', 50, 1000], 54000000)], [('regression: clock rounding', ['00:00:01.0005', 25, 1000], 1001), ('regression variant: clock rounding', ['10:59:59:29', 30, 90000], 39599967), ('partial repair probe: clock rounding', ['00:00:00:01', 16, 1000], 63), ('partial repair variant: clock rounding', ['100:59:59.0005', 40, 1000], 363599001), ('boundary control', ['3f', 16, 1000], 188), ('boundary control', ['00:00:00:25', 25, 1000], None), ('normal control', ['15m', 30, 1000], 900000), ('normal control', ['10:59:00:15', 40, 90000], 39540375), ('normal control', ['00:00:59:12', 30, 10000000], 59400)], [('regression: clock rounding', ['00:30:00:15', 16, 10000000], 1800938), ('regression variant: clock rounding', ['100:59:00.0005', 16, 3], 363540001), ('partial repair probe: clock rounding', ['100:30:59.0005', 40, 3], 361859001), ('partial repair variant: clock rounding', ['10:00:00.0005', 50, 3], 36000001), ('boundary control', ['00:00:00:25', 25, 1000], None), ('boundary control', ['10t', 25, 1000], 10), ('normal control', ['100:30:59:30', 25, 10000000], None), ('normal control', ['15t', 30, 3], 5000), ('normal control', ['01:60:05:12', 16, 10000000], None)], [('regression: clock rounding', ['00:30:00:01', 24, 1000], 1800042), ('regression variant: clock rounding', ['00:00:00:01', 16, 1000], 63), ('partial repair probe: clock rounding', ['01:30:05.0005', 30, 1000], 5405001), ('partial repair variant: clock rounding', ['10:30:05.0005', 30, 90000], 37805001), ('boundary control', ['10t', 25, 1000], 10), ('boundary control', ['3f', 16, 1000], 188), ('normal control', ['0t', 24, 3], 0), ('normal control', ['1.5t', 16, 3], 500), ('normal control', ['10:00:00.25', 16, 3], 36000250)], [('regression: clock rounding', ['01:59:59:01', 24, 3], 7199042), ('regression variant: clock rounding', ['100:30:00:15', 16, 90000], 361800938), ('partial repair probe: clock rounding', ['00:30:00.0005', 40, 3], 1800001), ('partial repair variant: clock rounding', ['00:59:00.0005', 24, 10000000], 3540001), ('boundary control', ['3f', 16, 1000], 188), ('boundary control', ['00:00:00:25', 25, 1000], None), ('normal control', ['10:30:59.5', 24, 1000], 37859500), ('normal control', ['01:30:05:25', 16, 90000], None), ('normal control', ['17h', 24, 10000000], 61200000)]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression: clock rounding19381938Passed
regression variant: clock rounding3959900039599001Failed
partial repair probe: clock rounding363540000363540001Failed
partial repair variant: clock rounding3780000037800001Failed
boundary control1010Passed
boundary control188188Passed
normal controlNoneNonePassed
normal control3959937539599375Passed
normal control5400000054000000Passed

SHA-256 / 353878981f0ef25038959975c66153450d8cc5bb5c36677e3c0173b5ea3fe5b7

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
import re
from fractions import Fraction
N = 1
observations = []
def solve(expr, frame_rate, tick_rate):
    m=re.fullmatch(r'(\d+(?:\.\d+)?)(h|ms|m|s|f|t)',expr)
    if m:
        v=Fraction(m.group(1))
        unit=m.group(2)
        scale={'h':3600000,'m':60000,'s':1000,'ms':1}
        if unit in scale:
            x=v*scale[unit]
        elif unit=='f':
            x=v*1000/frame_rate
        else:
            x=v*1000/tick_rate
        return math.floor(x+Fraction(1,2))
    m=re.fullmatch(r'(\d{2,}):(\d{2}):(\d{2})(?:\.(\d+)|:(\d{2,}))?',expr)
    if not m:
        return None
    h,mi,se,frac,fr=m.groups()
    if int(mi)>59 or int(se)>59:
        return None
    x=Fraction(int(h)*3600+int(mi)*60+int(se))*1000
    if frac:
        x+=Fraction('0.'+frac)*1000
    if fr:
        if int(fr)>=frame_rate:
            return None
        x+=Fraction(int(fr)*1000,frame_rate)
    return (x*2+1)//2
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: clock rounding', ['00:00:01:15', 16, 1000], 1938), ('regression variant: clock rounding', ['10:59:59.0005', 50, 90000], 39599001), ('partial repair probe: clock rounding', ['100:59:00.0005', 16, 3], 363540001), ('partial repair variant: clock rounding', ['10:30:00.0005', 24, 90000], 37800001), ('boundary control', ['10t', 25, 1000], 10), ('boundary control', ['3f', 16, 1000], 188), ('normal control', ['00:00:00:25', 25, 1000], None), ('normal control', ['10:59:59:15', 40, 1000], 39599375), ('normal control', ['15h', 50, 1000], 54000000)], [('regression: clock rounding', ['00:00:01.0005', 25, 1000], 1001), ('regression variant: clock rounding', ['10:59:59:29', 30, 90000], 39599967), ('partial repair probe: clock rounding', ['00:00:00:01', 16, 1000], 63), ('partial repair variant: clock rounding', ['100:59:59.0005', 40, 1000], 363599001), ('boundary control', ['3f', 16, 1000], 188), ('boundary control', ['00:00:00:25', 25, 1000], None), ('normal control', ['15m', 30, 1000], 900000), ('normal control', ['10:59:00:15', 40, 90000], 39540375), ('normal control', ['00:00:59:12', 30, 10000000], 59400)], [('regression: clock rounding', ['00:30:00:15', 16, 10000000], 1800938), ('regression variant: clock rounding', ['100:59:00.0005', 16, 3], 363540001), ('partial repair probe: clock rounding', ['100:30:59.0005', 40, 3], 361859001), ('partial repair variant: clock rounding', ['10:00:00.0005', 50, 3], 36000001), ('boundary control', ['00:00:00:25', 25, 1000], None), ('boundary control', ['10t', 25, 1000], 10), ('normal control', ['100:30:59:30', 25, 10000000], None), ('normal control', ['15t', 30, 3], 5000), ('normal control', ['01:60:05:12', 16, 10000000], None)], [('regression: clock rounding', ['00:30:00:01', 24, 1000], 1800042), ('regression variant: clock rounding', ['00:00:00:01', 16, 1000], 63), ('partial repair probe: clock rounding', ['01:30:05.0005', 30, 1000], 5405001), ('partial repair variant: clock rounding', ['10:30:05.0005', 30, 90000], 37805001), ('boundary control', ['10t', 25, 1000], 10), ('boundary control', ['3f', 16, 1000], 188), ('normal control', ['0t', 24, 3], 0), ('normal control', ['1.5t', 16, 3], 500), ('normal control', ['10:00:00.25', 16, 3], 36000250)], [('regression: clock rounding', ['01:59:59:01', 24, 3], 7199042), ('regression variant: clock rounding', ['100:30:00:15', 16, 90000], 361800938), ('partial repair probe: clock rounding', ['00:30:00.0005', 40, 3], 1800001), ('partial repair variant: clock rounding', ['00:59:00.0005', 24, 10000000], 3540001), ('boundary control', ['3f', 16, 1000], 188), ('boundary control', ['00:00:00:25', 25, 1000], None), ('normal control', ['10:30:59.5', 24, 1000], 37859500), ('normal control', ['01:30:05:25', 16, 90000], None), ('normal control', ['17h', 24, 10000000], 61200000)]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression: clock rounding19381938Passed
regression variant: clock rounding3959900139599001Passed
partial repair probe: clock rounding363540001363540001Passed
partial repair variant: clock rounding3780000137800001Passed
boundary control1010Passed
boundary control188188Passed
normal controlNoneNonePassed
normal control3959937539599375Passed
normal control5400000054000000Passed

SHA-256 / bf4e74a7da931bca0e343c55f4b7c46d26a5105996725e8b002075d03e4b2f5e

Verification & scope

A deterministic bounded teaching model with a stipulated toy contract; it does not claim conformance to any subtitle standard. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:49:33.333928+00:00.

Case digest / cf6c03dd539c29a1cf597a736ae60ea3e8dafe12be08f53cf89a76c9cdcfd001