setPick('custom')}
+ onDateChange={updateDateRange}
+ />
{(['lingniu', 'external'] as const).map(c => (
diff --git a/src/modules/energy/date-range.test.ts b/src/modules/energy/date-range.test.ts
new file mode 100644
index 0000000..3d14392
--- /dev/null
+++ b/src/modules/energy/date-range.test.ts
@@ -0,0 +1,17 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import { dateRangeModeLabel, getQuickDateRange, normalizeDateRange } from './date-range.js';
+
+const FRIDAY = new Date(2026, 7, 7, 16, 30, 0);
+
+test('builds shared quick date ranges from local calendar days', () => {
+ assert.deepEqual(getQuickDateRange('thisWeek', FRIDAY), { start: '2026-08-03', end: '2026-08-07' });
+ assert.deepEqual(getQuickDateRange('thisMonth', FRIDAY), { start: '2026-08-01', end: '2026-08-07' });
+ assert.deepEqual(getQuickDateRange('last15', FRIDAY), { start: '2026-07-24', end: '2026-08-07' });
+});
+
+test('normalizes date order and publishes consistent labels', () => {
+ assert.deepEqual(normalizeDateRange('2026-08-07', '2026-08-01'), { start: '2026-08-01', end: '2026-08-07' });
+ assert.equal(dateRangeModeLabel('thisMonth'), '本月');
+ assert.equal(dateRangeModeLabel('custom'), '自定义区间');
+});
diff --git a/src/modules/energy/date-range.ts b/src/modules/energy/date-range.ts
new file mode 100644
index 0000000..bba7440
--- /dev/null
+++ b/src/modules/energy/date-range.ts
@@ -0,0 +1,50 @@
+import type { DateQuickPick } from './types';
+
+export type DateRangeMode = DateQuickPick | 'custom';
+
+export interface DateRangeValue {
+ start: string;
+ end: string;
+}
+
+const LABELS: Record = {
+ thisWeek: '本周',
+ thisMonth: '本月',
+ last15: '近 15 天',
+};
+
+function fmtYmd(date: Date): string {
+ return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
+}
+
+function addDays(date: Date, days: number): Date {
+ const next = new Date(date);
+ next.setDate(next.getDate() + days);
+ return next;
+}
+
+export function getQuickDateRange(pick: DateQuickPick, now = new Date()): DateRangeValue {
+ const today = new Date(now);
+ today.setHours(0, 0, 0, 0);
+ if (pick === 'thisWeek') {
+ const day = today.getDay() || 7;
+ return { start: fmtYmd(addDays(today, -(day - 1))), end: fmtYmd(today) };
+ }
+ if (pick === 'thisMonth') {
+ return { start: fmtYmd(new Date(today.getFullYear(), today.getMonth(), 1)), end: fmtYmd(today) };
+ }
+ return { start: fmtYmd(addDays(today, -14)), end: fmtYmd(today) };
+}
+
+export function normalizeDateRange(start: string, end: string): DateRangeValue {
+ return start <= end ? { start, end } : { start: end, end: start };
+}
+
+export function dateRangeModeLabel(mode: DateRangeMode): string {
+ return mode === 'custom' ? '自定义区间' : LABELS[mode];
+}
+
+export const QUICK_DATE_OPTIONS = (Object.keys(LABELS) as DateQuickPick[]).map(id => ({
+ id,
+ label: LABELS[id],
+}));