Skip to content

Commit 6d589ad

Browse files
Iteration 230: Add stats/date_range.ts — pd.date_range() fixed-frequency Date sequences
Run: https://github.com/githubnext/tsessebe/actions/runs/24305375139 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent cf233cb commit 6d589ad

6 files changed

Lines changed: 1272 additions & 0 deletions

File tree

playground/date_range.html

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
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 — dateRange</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+
.row { display: flex; gap: 12px; flex-wrap: wrap; }
23+
.row label { font-size: 0.85rem; color: #555; display: block; margin-bottom: 2px; }
24+
.row input, .row select { padding: 6px 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 0.85rem; width: 180px; }
25+
</style>
26+
</head>
27+
<body>
28+
<p><a href="index.html">← tsb playground</a></p>
29+
<h1>dateRange <span class="tag">stats</span></h1>
30+
<p>
31+
Generate a fixed-frequency sequence of <code>Date</code> objects — mirroring
32+
<a href="https://pandas.pydata.org/docs/reference/api/pandas.date_range.html"
33+
><code>pandas.date_range()</code></a
34+
>.
35+
</p>
36+
<p>
37+
Specify at least <strong>two</strong> of <code>start</code>, <code>end</code>, and
38+
<code>periods</code>. Use <code>freq</code> to control the step size.
39+
</p>
40+
41+
<h2>Supported frequencies</h2>
42+
<table>
43+
<tr><th>Alias</th><th>Description</th><th>Example</th></tr>
44+
<tr><td><code>"D"</code></td><td>Calendar day</td><td><code>dateRange({ start: "2024-01-01", periods: 7, freq: "D" })</code></td></tr>
45+
<tr><td><code>"B"</code></td><td>Business day (Mon–Fri)</td><td><code>freq: "B"</code></td></tr>
46+
<tr><td><code>"h"</code> / <code>"H"</code></td><td>Hour</td><td><code>freq: "h"</code></td></tr>
47+
<tr><td><code>"min"</code> / <code>"T"</code></td><td>Minute</td><td><code>freq: "30min"</code></td></tr>
48+
<tr><td><code>"s"</code> / <code>"S"</code></td><td>Second</td><td><code>freq: "s"</code></td></tr>
49+
<tr><td><code>"ms"</code> / <code>"L"</code></td><td>Millisecond</td><td><code>freq: "ms"</code></td></tr>
50+
<tr><td><code>"W"</code>, <code>"W-MON"</code><code>"W-SAT"</code></td><td>Weekly, anchored to weekday</td><td><code>freq: "W-MON"</code></td></tr>
51+
<tr><td><code>"MS"</code></td><td>Month start</td><td><code>freq: "MS"</code></td></tr>
52+
<tr><td><code>"ME"</code> / <code>"M"</code></td><td>Month end</td><td><code>freq: "ME"</code></td></tr>
53+
<tr><td><code>"QS"</code></td><td>Quarter start</td><td><code>freq: "QS"</code></td></tr>
54+
<tr><td><code>"QE"</code> / <code>"Q"</code></td><td>Quarter end</td><td><code>freq: "QE"</code></td></tr>
55+
<tr><td><code>"YS"</code> / <code>"AS"</code></td><td>Year start (Jan 1)</td><td><code>freq: "YS"</code></td></tr>
56+
<tr><td><code>"YE"</code> / <code>"A"</code> / <code>"Y"</code></td><td>Year end (Dec 31)</td><td><code>freq: "YE"</code></td></tr>
57+
<tr><td><em>Multiplier</em></td><td>Integer prefix: <code>"2D"</code>, <code>"3H"</code>, <code>"6MS"</code></td><td><code>freq: "2D"</code></td></tr>
58+
</table>
59+
60+
<h2>inclusive — endpoint control</h2>
61+
<table>
62+
<tr><th>Value</th><th>Start included</th><th>End included</th></tr>
63+
<tr><td><code>"both"</code> (default)</td><td></td><td></td></tr>
64+
<tr><td><code>"neither"</code></td><td></td><td></td></tr>
65+
<tr><td><code>"left"</code></td><td></td><td></td></tr>
66+
<tr><td><code>"right"</code></td><td></td><td></td></tr>
67+
</table>
68+
69+
<h2>TypeScript usage</h2>
70+
<pre><code>import { dateRange } from "tsb";
71+
72+
// 5 daily dates starting 2024-01-01
73+
dateRange({ start: "2024-01-01", periods: 5 });
74+
// → [Jan 1, Jan 2, Jan 3, Jan 4, Jan 5]
75+
76+
// Hourly between two timestamps
77+
dateRange({
78+
start: "2024-01-01T00:00:00Z",
79+
end: "2024-01-01T06:00:00Z",
80+
freq: "h",
81+
});
82+
// → [00:00, 01:00, 02:00, 03:00, 04:00, 05:00, 06:00]
83+
84+
// 6 month-starts starting Jan 2024
85+
dateRange({ start: "2024-01-01", periods: 6, freq: "MS" });
86+
// → [Jan 1, Feb 1, Mar 1, Apr 1, May 1, Jun 1]
87+
88+
// Business days in a week
89+
dateRange({ start: "2024-01-01", end: "2024-01-07", freq: "B" });
90+
// → [Mon Jan 1, Tue Jan 2, Wed Jan 3, Thu Jan 4, Fri Jan 5]
91+
92+
// Exclude endpoints with inclusive: "neither"
93+
dateRange({ start: "2024-01-01", end: "2024-01-05", inclusive: "neither" });
94+
// → [Jan 2, Jan 3, Jan 4]</code></pre>
95+
96+
<h2>Python / pandas equivalent</h2>
97+
<textarea rows="14" readonly>
98+
import pandas as pd
99+
100+
# 5 daily dates
101+
pd.date_range(start="2024-01-01", periods=5)
102+
103+
# Hourly range
104+
pd.date_range(start="2024-01-01T00:00:00", end="2024-01-01T06:00:00", freq="h")
105+
106+
# Monthly starts
107+
pd.date_range(start="2024-01-01", periods=6, freq="MS")
108+
109+
# Business days
110+
pd.date_range(start="2024-01-01", end="2024-01-07", freq="B")
111+
112+
# Exclude endpoints
113+
pd.date_range(start="2024-01-01", end="2024-01-05", inclusive="neither")
114+
</textarea>
115+
116+
<h2>Live demo</h2>
117+
<p>Fill in the options below and click <em>Generate</em>:</p>
118+
<div class="row">
119+
<div>
120+
<label for="d-start">start</label>
121+
<input id="d-start" type="text" value="2024-01-01" />
122+
</div>
123+
<div>
124+
<label for="d-end">end (optional)</label>
125+
<input id="d-end" type="text" placeholder="leave blank" />
126+
</div>
127+
<div>
128+
<label for="d-periods">periods (optional)</label>
129+
<input id="d-periods" type="number" value="8" min="1" max="100" />
130+
</div>
131+
<div>
132+
<label for="d-freq">freq</label>
133+
<select id="d-freq">
134+
<option value="D" selected>D — daily</option>
135+
<option value="B">B — business day</option>
136+
<option value="h">h — hourly</option>
137+
<option value="min">min — minute</option>
138+
<option value="s">s — second</option>
139+
<option value="W-MON">W-MON — weekly Mon</option>
140+
<option value="MS">MS — month start</option>
141+
<option value="ME">ME — month end</option>
142+
<option value="QS">QS — quarter start</option>
143+
<option value="QE">QE — quarter end</option>
144+
<option value="YS">YS — year start</option>
145+
<option value="YE">YE — year end</option>
146+
</select>
147+
</div>
148+
<div>
149+
<label for="d-incl">inclusive</label>
150+
<select id="d-incl">
151+
<option value="both" selected>both</option>
152+
<option value="left">left</option>
153+
<option value="right">right</option>
154+
<option value="neither">neither</option>
155+
</select>
156+
</div>
157+
</div>
158+
<button onclick="runDemo()">Generate</button>
159+
<div class="output" id="out"></div>
160+
161+
<script type="module">
162+
import { dateRange } from "https://esm.sh/tsb@0.0.1";
163+
164+
window.runDemo = function () {
165+
const out = document.getElementById("out");
166+
try {
167+
const startVal = document.getElementById("d-start").value.trim();
168+
const endVal = document.getElementById("d-end").value.trim();
169+
const periodsVal = document.getElementById("d-periods").value.trim();
170+
const freq = document.getElementById("d-freq").value;
171+
const inclusive = document.getElementById("d-incl").value;
172+
173+
const opts = { freq, inclusive };
174+
if (startVal) opts.start = startVal;
175+
if (endVal) opts.end = endVal;
176+
if (periodsVal) opts.periods = Number(periodsVal);
177+
178+
const result = dateRange(opts);
179+
if (result.length === 0) {
180+
out.textContent = "(empty — no dates in range)";
181+
} else {
182+
out.textContent = result
183+
.map((d, i) => `[${i}] ${d.toISOString().slice(0, 10)} (${d.toUTCString().slice(0, 3)})`)
184+
.join("\n");
185+
}
186+
} catch (e) {
187+
out.textContent = "Error: " + e.message;
188+
}
189+
};
190+
</script>
191+
</body>
192+
</html>

playground/index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,11 @@ <h3><a href="to_timedelta.html" style="color: var(--accent); text-decoration: no
409409
<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>
410410
<div class="status done">✅ Complete</div>
411411
</div>
412+
<div class="feature-card">
413+
<h3><a href="date_range.html" style="color: var(--accent); text-decoration: none;">📅 dateRange</a></h3>
414+
<p>Generate fixed-frequency sequences of <code>Date</code> objects. Mirrors <code>pandas.date_range()</code>. Supports D/B/h/min/s/ms/W/MS/ME/QS/QE/YS/YE frequencies with multiplier prefixes. Inclusive endpoint control: both/left/right/neither.</p>
415+
<div class="status done">✅ Complete</div>
416+
</div>
412417
</div>
413418
</section>
414419
</main>

src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,3 +257,10 @@ export { toDatetime } from "./stats/index.ts";
257257
export type { DatetimeUnit, DatetimeErrors, ToDatetimeOptions } from "./stats/index.ts";
258258
export { toTimedelta, parseFrac, formatTimedelta, Timedelta } from "./stats/index.ts";
259259
export type { TimedeltaUnit, TimedeltaErrors, ToTimedeltaOptions } from "./stats/index.ts";
260+
export { dateRange, parseFreq, advanceDate, toDateInput } from "./stats/index.ts";
261+
export type {
262+
DateRangeFreq,
263+
DateRangeInclusive,
264+
DateRangeOptions,
265+
ParsedFreq,
266+
} from "./stats/index.ts";

0 commit comments

Comments
 (0)