@kenz1117/dsh-ui-usage-billing 1.4.8 → 1.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -298,16 +298,22 @@ function isPeakHour(beijingHour) {
298
298
  /**
299
299
  * 由时刻(epoch 毫秒)推断计费时段;时刻未知/非法时按高峰计(保守:未知
300
300
  * 时刻不低估成本,与社区 dsh-usage-chart 的 tierAt 语义一致)。
301
- * 周末(北京时间周六/周日)全天不区分峰谷,统一按低谷价。
301
+ * 周末(北京时间周六/周日)与中国法定节假日全天不区分峰谷,统一按低谷价
302
+ * (官方 2026-09-19 口径:调休上班的周末、法定节假日全天均按空闲时段计费;
303
+ * 调休上班日全部落在周末,故由周末分支覆盖,无需单列)。
302
304
  * @param timeMs - Unix epoch 毫秒;null/undefined/NaN 视为未知。
303
305
  */
304
306
  function tierAt(timeMs) {
305
307
  if (timeMs === null || timeMs === void 0 || !Number.isFinite(timeMs)) return "peak";
306
- return tierAtWithBounds(timeMs, TIER_BOUNDARY_MINUTES);
308
+ return tierAtWithBounds(timeMs, TIER_BOUNDARY_MINUTES, true);
307
309
  }
308
- /** 按给定峰段边界判档:周末全天低谷;工作日分钟数落在任一 [b(i), b(i+1)) 峰段即为高峰。 */
309
- function tierAtWithBounds(timeMs, bounds) {
310
- if (isBeijingWeekend(timeMs)) return "offPeak";
310
+ /**
311
+ * 按给定峰段边界判档:低谷日全天低谷;工作日分钟数落在任一 [b(i), b(i+1)) 峰段即为高峰。
312
+ * @param holidays - 是否把中国法定节假日并入低谷日。该官方口径只适用于 DeepSeek 按量分时;
313
+ * 智谱 Coding Plan 的积分口径未见节假日说明,故默认不启用,由调用点显式传入。
314
+ */
315
+ function tierAtWithBounds(timeMs, bounds, holidays = false) {
316
+ if (isBeijingWeekend(timeMs) || holidays && isBeijingHoliday(timeMs)) return "offPeak";
311
317
  const minute = Math.floor(beijingMillisOfDay(timeMs) / 6e4);
312
318
  for (let i = 0; i + 1 < bounds.length; i += 2) {
313
319
  const start = bounds[i] ?? 0;
@@ -321,6 +327,28 @@ function isBeijingWeekend(timeMs) {
321
327
  const day = new Date(timeMs + 288e5).getUTCDay();
322
328
  return day === 0 || day === 6;
323
329
  }
330
+ /**
331
+ * 中国法定节假日 —— **生成块,勿手改**。数据源
332
+ * [NateScarlet/holiday-cn](https://github.com/NateScarlet/holiday-cn)(逐年抓取国务院公告;
333
+ * 2026 年依据 https://www.gov.cn/zhengce/zhengceku/202511/content_7047091.htm)。
334
+ *
335
+ * 只收录峰谷计费起点({@link WEEKEND_OFFPEAK_START_MS},北京时间 2026-08-23 00:00)之后的日期:
336
+ * 更早的调用由 v1 规则定价,不涉及节假日。表中没有的年份退化为「仅周末低谷」,
337
+ * 国务院公布下一年安排后在此追加一条即可。
338
+ *
339
+ * 编码为空格分隔的 `YYYYMMDD` 列表(按年分组、同年日期连续排列,便于逐年追加):
340
+ * 每个键长度固定为 8,故 `includes` 不会跨键误匹配;相比「区间 + 运行时展开」
341
+ * 省掉一段解析代码——client / index 两个 face 都要过 256 KiB 单文件上限。
342
+ */
343
+ const CHINA_HOLIDAYS = "20260925 20260926 20260927 20261001 20261002 20261003 20261004 20261005 20261006 20261007";
344
+ /** 北京日期键 YYYYMMDD(与宿主时区无关)。 */
345
+ function beijingDateKey(timeMs) {
346
+ return new Date(timeMs + 288e5).toISOString().slice(0, 10).replace(/-/g, "");
347
+ }
348
+ /** 时刻是否落在北京时间法定节假日(仅覆盖 {@link CHINA_HOLIDAYS} 内已收录的年份)。 */
349
+ function isBeijingHoliday(timeMs) {
350
+ return CHINA_HOLIDAYS.includes(beijingDateKey(timeMs));
351
+ }
324
352
  /** 峰谷切换边界(北京时间的当日分钟数):09:00 / 12:00 / 14:00 / 18:00。 */
325
353
  const TIER_BOUNDARY_MINUTES = [
326
354
  540,
@@ -10,7 +10,8 @@
10
10
  * Google-style two-band billing is modeled per model: Gemini's Flex tier
11
11
  * prices spare-capacity traffic at -50%; DeepSeek splits peak
12
12
  * (weekdays 09:00-12:00 / 14:00-18:00 Beijing) at 2x the off-peak rate —
13
- * weekends (Sat/Sun, Beijing) are charged at the off-peak rate all day.
13
+ * weekends (Sat/Sun, Beijing) and Chinese statutory holidays are charged at
14
+ * the off-peak rate all day (see {@link CHINA_HOLIDAYS}).
14
15
  * The estimator mixes both bands by a configured peak share ({@link DEFAULT_PEAK_SHARE}).
15
16
  *
16
17
  * Time-limited launch promos ({@link PricePromo}) never mutate the catalog:
@@ -182,7 +183,9 @@ export declare function isPeakHour(beijingHour: number): boolean;
182
183
  /**
183
184
  * 由时刻(epoch 毫秒)推断计费时段;时刻未知/非法时按高峰计(保守:未知
184
185
  * 时刻不低估成本,与社区 dsh-usage-chart 的 tierAt 语义一致)。
185
- * 周末(北京时间周六/周日)全天不区分峰谷,统一按低谷价。
186
+ * 周末(北京时间周六/周日)与中国法定节假日全天不区分峰谷,统一按低谷价
187
+ * (官方 2026-09-19 口径:调休上班的周末、法定节假日全天均按空闲时段计费;
188
+ * 调休上班日全部落在周末,故由周末分支覆盖,无需单列)。
186
189
  * @param timeMs - Unix epoch 毫秒;null/undefined/NaN 视为未知。
187
190
  */
188
191
  export declare function tierAt(timeMs: number | null | undefined): PriceTierId;
@@ -191,9 +194,12 @@ export declare function tierAt(timeMs: number | null | undefined): PriceTierId;
191
194
  *
192
195
  * 下一切换点统一定义为档位真正变化的最近边界:自当前时刻起逐天扫描工作日的
193
196
  * 09:00 / 12:00 / 14:00 / 18:00,候选时刻的档位由 {@link tierAt} 判定——
194
- * 周末(周六/周日)北京全天低谷、没有边界,扫描自然跳过;工作日深夜跨周末
195
- * 时落到周一 09:00 而非周末伪边界(issue #33)。
196
- * 最坏情形(周五 18:00 后 → 周一 09:00)约 63h,7 天窗口必然覆盖。
197
+ * 周末(周六/周日)与法定节假日北京全天低谷、没有边界,扫描自然跳过;工作日
198
+ * 深夜跨周末时落到周一 09:00 而非周末伪边界(issue #33)。
199
+ * 最坏情形是「长假 + 相邻周末」:最长连休 8 天(2025-10-01 ~ 10-08 型)时,
200
+ * 假期前最后一个工作日深夜的下一切换可落在约 9.4 天之后——故扫描窗口取
201
+ * 10 天。原实现取 7 天(注释记「最坏 63h」),在连休下会扫不到真边界而落进
202
+ * 兜底伪边界。
197
203
  * @param nowMs - 当前时刻(epoch 毫秒)。
198
204
  * @returns 当前档位与到下一切换边界的毫秒数。
199
205
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kenz1117/dsh-ui-usage-billing",
3
3
  "description": "Usage billing dashboard for DeepSeek Harness: sidebar cost metrics plus a full dashboard modal, priced from a current multi-provider catalog with real usage aggregated from session logs.",
4
- "version": "1.4.8",
4
+ "version": "1.4.9",
5
5
  "keywords": [
6
6
  "deepseek-harness",
7
7
  "deepseek",