Repository: tkarabela/pysubs2 (language: python)

Issue:
Incorrect time after the shift
There is the line:
`Dialogue: 1,1:17:41.04,1:17:47.54,ED,,0,0,0,,sono mama no kimi ga sukidato`

After shifting it back by 1045 ms it looks like this:
`Dialogue: 1,1:17:39.100,1:17:46.50,ED,,0,0,0,,sono mama no kimi ga sukidato`

Look at the fractional part of the start time: 1:17:**39.100**. Should be 1:17:40.00.

The command i used:
`pysubs2.exe --shift-back 1045ms example.ass`

Here is an example file: [example.zip](https://github.com/tkarabela/pysubs2/files/12724432/example.zip)


Discussion / hints:

Fix patch:
diff --git a/pysubs2/substation.py b/pysubs2/substation.py
index 034511e..4123f4c 100644
--- a/pysubs2/substation.py
+++ b/pysubs2/substation.py
@@ -150,18 +150,18 @@ class SubstationFormat(FormatBase):
     """SubStation Alpha (ASS, SSA) subtitle format implementation"""
 
     @staticmethod
-    def ms_to_timestamp(ms: int) -> str:
+    def ms_to_timestamp(requested_ms: int) -> str:
         """Convert ms to 'H:MM:SS.cc'"""
-        if ms < 0:
-            ms = 0
-        if ms > MAX_REPRESENTABLE_TIME:
+        if requested_ms < 0:
+            requested_ms = 0
+        if requested_ms > MAX_REPRESENTABLE_TIME:
             warnings.warn("Overflow in SubStation timestamp, clamping to MAX_REPRESENTABLE_TIME", RuntimeWarning)
-            ms = MAX_REPRESENTABLE_TIME
-
-        h, m, s, ms = ms_to_times(ms)
+            requested_ms = MAX_REPRESENTABLE_TIME
 
         # Aegisub does rounding, see https://github.com/Aegisub/Aegisub/blob/6f546951b4f004da16ce19ba638bf3eedefb9f31/libaegisub/include/libaegisub/ass/time.h#L32
-        cs = ((ms + 5) - (ms + 5) % 10) // 10
+        round_ms = ((requested_ms + 5) - (requested_ms + 5) % 10)
+        h, m, s, ms = ms_to_times(round_ms)
+        cs = ms // 10
 
         return f"{h:01d}:{m:02d}:{s:02d}.{cs:02d}"
 


Test patch:
diff --git a/tests/test_substation.py b/tests/test_substation.py
index 35e948e..9913b80 100644
--- a/tests/test_substation.py
+++ b/tests/test_substation.py
@@ -5,7 +5,7 @@
 
 from textwrap import dedent
 from pysubs2 import SSAFile, SSAEvent, SSAStyle, make_time, Color, Alignment
-from pysubs2.substation import color_to_ass_rgba, color_to_ssa_rgb, rgba_to_color, MAX_REPRESENTABLE_TIME
+from pysubs2.substation import color_to_ass_rgba, color_to_ssa_rgb, rgba_to_color, MAX_REPRESENTABLE_TIME, SubstationFormat
 import pytest
 
 
@@ -453,6 +453,21 @@ def test_reading_invalid_alignment_raises_warning():
     assert subs.styles["Default"].alignment == Alignment.BOTTOM_CENTER
 
 
+def test_ass_ms_to_timestamp():
+    # see issue #76
+
+    assert SubstationFormat.ms_to_timestamp(4659990) == "1:17:39.99"
+    assert SubstationFormat.ms_to_timestamp(4659991) == "1:17:39.99"
+    assert SubstationFormat.ms_to_timestamp(4659992) == "1:17:39.99"
+    assert SubstationFormat.ms_to_timestamp(4659993) == "1:17:39.99"
+    assert SubstationFormat.ms_to_timestamp(4659994) == "1:17:39.99"
+    assert SubstationFormat.ms_to_timestamp(4659995) == "1:17:40.00"
+    assert SubstationFormat.ms_to_timestamp(4659996) == "1:17:40.00"
+    assert SubstationFormat.ms_to_timestamp(4659997) == "1:17:40.00"
+    assert SubstationFormat.ms_to_timestamp(4659998) == "1:17:40.00"
+    assert SubstationFormat.ms_to_timestamp(4659999) == "1:17:40.00"
+
+
 def test_bad_style_format_line_issue_89():
     subs = SSAFile.from_string(ASS_STYLES_FORMAT_ISSUE_89)
     assert subs.styles["Default"].bold