Skip to content

Commit cf233cb

Browse files
Iteration 229: Add stats/to_timedelta.ts — convert scalars/arrays/Series to Timedelta
Run: https://github.com/githubnext/tsessebe/actions/runs/24304871817 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 716a7f3 commit cf233cb

6 files changed

Lines changed: 1040 additions & 0 deletions

File tree

playground/index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,11 @@ <h3><a href="nancumops.html" style="color: var(--accent); text-decoration: none;
404404
<p>Top-level nan-ignoring aggregate functions: <code>nansum</code>, <code>nanmean</code>, <code>nanmedian</code>, <code>nanstd</code>, <code>nanvar</code>, <code>nanmin</code>, <code>nanmax</code>, <code>nanprod</code>, <code>nancount</code>. Mirrors <code>numpy.nan*</code> functions. Works on arrays and Series.</p>
405405
<div class="status done">✅ Complete</div>
406406
</div>
407+
<div class="feature-card">
408+
<h3><a href="to_timedelta.html" style="color: var(--accent); text-decoration: none;">⏱️ toTimedelta</a></h3>
409+
<p>Convert scalars, arrays, or Series to <code>Timedelta</code> objects. Accepts pandas-style strings ("1 days 02:03:04"), ISO 8601 ("P1DT2H"), human-readable ("1h 30m"), numeric (ns/us/ms/s/m/h/D/W). <code>Timedelta</code> class with arithmetic: add/subtract/scale/abs/lt/gt/eq.</p>
410+
<div class="status done">✅ Complete</div>
411+
</div>
407412
</div>
408413
</section>
409414
</main>

playground/to_timedelta.html

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>tsb — toTimedelta</title>
7+
<style>
8+
body { font-family: system-ui, sans-serif; max-width: 860px; margin: 40px auto; padding: 0 20px; line-height: 1.6; color: #222; }
9+
h1 { font-size: 1.8rem; }
10+
h2 { font-size: 1.2rem; margin-top: 2rem; border-bottom: 1px solid #ddd; padding-bottom: 4px; }
11+
pre { background: #f6f8fa; border: 1px solid #e1e4e8; border-radius: 6px; padding: 16px; overflow: auto; font-size: 0.85rem; }
12+
code { font-family: "SFMono-Regular", Consolas, monospace; }
13+
.tag { display: inline-block; background: #dbeafe; color: #1e40af; border-radius: 4px; padding: 1px 8px; font-size: 0.75rem; font-weight: bold; }
14+
table { border-collapse: collapse; width: 100%; margin: 1rem 0; }
15+
th, td { border: 1px solid #ddd; padding: 8px 12px; text-align: left; }
16+
th { background: #f6f8fa; }
17+
textarea { width: 100%; font-family: monospace; font-size: 0.85rem; padding: 10px; border: 1px solid #ddd; border-radius: 6px; }
18+
.output { background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 6px; padding: 12px; margin-top: 8px; min-height: 40px; white-space: pre; font-family: monospace; font-size: 0.85rem; }
19+
button { background: #2563eb; color: #fff; border: none; border-radius: 6px; padding: 8px 18px; cursor: pointer; font-size: 0.9rem; margin-top: 8px; }
20+
button:hover { background: #1d4ed8; }
21+
a { color: #2563eb; }
22+
</style>
23+
</head>
24+
<body>
25+
<p><a href="index.html">← tsb playground</a></p>
26+
<h1>toTimedelta <span class="tag">stats</span></h1>
27+
<p>
28+
Convert scalars, arrays, or <code>Series</code> values to
29+
<code>Timedelta</code> objects — mirroring
30+
<a href="https://pandas.pydata.org/docs/reference/api/pandas.to_timedelta.html"
31+
><code>pandas.to_timedelta()</code></a
32+
>.
33+
</p>
34+
35+
<h2>Supported input formats</h2>
36+
<table>
37+
<tr><th>Format</th><th>Example</th><th>Result (ms)</th></tr>
38+
<tr><td>Pandas-style</td><td><code>"1 days 02:03:04"</code></td><td>93 784 000 ms</td></tr>
39+
<tr><td>Clock (HH:MM:SS)</td><td><code>"01:30:00"</code></td><td>5 400 000 ms</td></tr>
40+
<tr><td>ISO 8601</td><td><code>"P1DT2H"</code></td><td>93 600 000 ms</td></tr>
41+
<tr><td>Human-readable</td><td><code>"1h 30m 20s"</code></td><td>5 420 000 ms</td></tr>
42+
<tr><td>number (unit="ns")</td><td><code>1_000_000_000</code></td><td>1 000 ms</td></tr>
43+
<tr><td>number (unit="ms")</td><td><code>5000</code></td><td>5 000 ms</td></tr>
44+
<tr><td>Timedelta</td><td><code>new Timedelta(1000)</code></td><td>unchanged</td></tr>
45+
<tr><td>null / undefined / NaN</td><td><code>null</code></td><td><code>null</code></td></tr>
46+
</table>
47+
48+
<h2>Timedelta class</h2>
49+
<table>
50+
<tr><th>Property / Method</th><th>Description</th></tr>
51+
<tr><td><code>.totalMs</code></td><td>Total duration in milliseconds (signed)</td></tr>
52+
<tr><td><code>.days</code></td><td>Whole days</td></tr>
53+
<tr><td><code>.hours</code></td><td>Hours within the current day (0–23)</td></tr>
54+
<tr><td><code>.minutes</code></td><td>Minutes within the current hour (0–59)</td></tr>
55+
<tr><td><code>.seconds</code></td><td>Seconds within the current minute (0–59)</td></tr>
56+
<tr><td><code>.ms</code></td><td>Milliseconds within the current second (0–999)</td></tr>
57+
<tr><td><code>.abs()</code></td><td>Absolute value</td></tr>
58+
<tr><td><code>.add(other)</code></td><td>Add two Timedeltas</td></tr>
59+
<tr><td><code>.subtract(other)</code></td><td>Subtract a Timedelta</td></tr>
60+
<tr><td><code>.scale(n)</code></td><td>Multiply by a scalar</td></tr>
61+
<tr><td><code>.lt(other)</code></td><td>Less-than comparison</td></tr>
62+
<tr><td><code>.gt(other)</code></td><td>Greater-than comparison</td></tr>
63+
<tr><td><code>.eq(other)</code></td><td>Equality comparison</td></tr>
64+
<tr><td><code>.toString()</code></td><td>Pandas-style string representation</td></tr>
65+
</table>
66+
67+
<h2>Error handling</h2>
68+
<table>
69+
<tr><th>errors=</th><th>Behaviour</th></tr>
70+
<tr><td><code>"raise"</code> (default)</td><td>Throws <code>TypeError</code> on unparseable input</td></tr>
71+
<tr><td><code>"coerce"</code></td><td>Returns <code>null</code> on unparseable input</td></tr>
72+
<tr><td><code>"ignore"</code></td><td>Returns the original value unchanged</td></tr>
73+
</table>
74+
75+
<h2>Quick examples</h2>
76+
<pre><code>import { toTimedelta, Timedelta, formatTimedelta, Series } from "tsb";
77+
78+
// Scalar — various string formats
79+
toTimedelta("1 days 02:03:04"); // Timedelta(93_784_000 ms)
80+
toTimedelta("01:30:00"); // Timedelta(5_400_000 ms)
81+
toTimedelta("P1DT2H3M4S"); // ISO 8601
82+
toTimedelta("1h 30m 20s 500ms"); // human-readable
83+
84+
// Scalar — numeric
85+
toTimedelta(1_000_000_000); // default unit "ns" → 1000 ms
86+
toTimedelta(5000, { unit: "ms" }); // 5000 ms
87+
toTimedelta(2, { unit: "D" }); // 2 days
88+
89+
// Missing values
90+
toTimedelta(null); // null
91+
toTimedelta("nope", { errors: "coerce" }); // null
92+
toTimedelta("nope", { errors: "ignore" }); // "nope" (unchanged)
93+
94+
// Timedelta arithmetic
95+
const a = toTimedelta("1h") as Timedelta;
96+
const b = toTimedelta("30m") as Timedelta;
97+
a.add(b).toString(); // "0 days 01:30:00"
98+
a.subtract(b).totalMs; // 1_800_000
99+
100+
// Array
101+
toTimedelta(["1h", "30m", null]);
102+
// => [Timedelta(3_600_000), Timedelta(1_800_000), null]
103+
104+
// Series
105+
const s = new Series({ data: ["1h", "30m", null] });
106+
toTimedelta(s);
107+
// => Series&lt;Timedelta | null&gt; with dtype=timedelta
108+
109+
// formatTimedelta
110+
formatTimedelta(new Timedelta(86_400_000 + 3_661_000));
111+
// => "1 day 01:01:01"</code></pre>
112+
113+
<h2>Python / pandas equivalent</h2>
114+
<textarea rows="14" readonly>
115+
import pandas as pd
116+
117+
# Scalar
118+
pd.to_timedelta("1 days 02:03:04")
119+
pd.to_timedelta("01:30:00")
120+
pd.to_timedelta("P1DT2H3M4S")
121+
pd.to_timedelta(5000, unit="ms")
122+
123+
# Array / Series
124+
pd.to_timedelta(["1h", "30m", None])
125+
s = pd.Series(["1h", "30m", None])
126+
pd.to_timedelta(s)
127+
128+
# Timedelta arithmetic
129+
pd.Timedelta("1h") + pd.Timedelta("30m")
130+
</textarea>
131+
</body>
132+
</html>

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,3 +255,5 @@ export {
255255
export type { NanInput, NanAggOptions } from "./stats/index.ts";
256256
export { toDatetime } from "./stats/index.ts";
257257
export type { DatetimeUnit, DatetimeErrors, ToDatetimeOptions } from "./stats/index.ts";
258+
export { toTimedelta, parseFrac, formatTimedelta, Timedelta } from "./stats/index.ts";
259+
export type { TimedeltaUnit, TimedeltaErrors, ToTimedeltaOptions } from "./stats/index.ts";

src/stats/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,5 @@ export {
173173
export type { NanInput, NanAggOptions } from "./nancumops.ts";
174174
export { toDatetime } from "./to_datetime.ts";
175175
export type { DatetimeUnit, DatetimeErrors, ToDatetimeOptions } from "./to_datetime.ts";
176+
export { toTimedelta, parseFrac, formatTimedelta, Timedelta } from "./to_timedelta.ts";
177+
export type { TimedeltaUnit, TimedeltaErrors, ToTimedeltaOptions } from "./to_timedelta.ts";

0 commit comments

Comments
 (0)