Skip to content

chore: 迁移日期时间工具库至luxon#336

Open
YufJi wants to merge 28 commits into
testfrom
chore/luxon
Open

chore: 迁移日期时间工具库至luxon#336
YufJi wants to merge 28 commits into
testfrom
chore/luxon

Conversation

@YufJi
Copy link
Copy Markdown
Collaborator

@YufJi YufJi commented Feb 5, 2026

背景:moment + moment-timezone + date-fns的依赖导致基础库体积过大

解决方式:luxon

Summary by CodeRabbit

  • 新功能

    • 初始化顺序调整,支持在初始化工具前预先注册数据类型(含 Date/DateTime)。
  • 依赖更新

    • 移除 Moment/部分 date-fns 包,新增 Luxon 及其类型声明。
  • 改进

    • 全面迁移为 Luxon 实现的日期时间格式化、解析与时区处理。
    • 全局暴露名由 sdkUtils/sdkHelpers 调整为 Utils/Helpers。
  • 测试

    • 大量测试迁移与新增,验证新日期处理行为并保持兼容性。

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 5, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

将项目的日期/时间实现从 moment/moment-timezone 与 date-fns 迁移到 Luxon,新增基于 Luxon 的日期工具模块,重构格式化/解析与初始化流程,并在大量测试中替换全局引用与断言以匹配新实现(无外部交互流程变更)。

Changes

Cohort / File(s) Summary
依赖与包清单
packages/basic/package.json, packages/vue2/package.json, packages/vue2/source/package.json
移除 moment/moment-timezone/date-fns(部分),新增 luxon 及类型,添加/调整 devDeps 与测试脚本。
初始化与全局暴露
packages/basic/jest.setup.js
改为从 ./src/index 导入 Basic;新增 Basic.initDataTypes(...) 调用;将 global.sdkUtilsglobal.Utilsglobal.sdkHelpersglobal.Helpers;更新错误日志文本。
核心日期工具重写
packages/basic/src/sdk/modules/utils/date.ts, packages/basic/src/sdk/modules/utils/index.ts
新增基于 Luxon 的完整日期工具集合并将 Utils 中的日期相关实现替换为 Luxon 驱动,保留对外方法签名。
格式化器与数据类型工具
packages/basic/src/sdk/Formatters/DateFormatter.ts, packages/basic/src/init/dataTypes/tools.ts
将基于 date-fns/moment 的格式化与解析改为使用 Luxon DateTime,更新格式字符串并在需时使用 .setZone(...)
SDK 辅助工具调整
packages/basic/src/sdk/helper.ts
使用 Luxon 重写日期处理逻辑;更改 getAppTimezone(inputTz) 签名为 getAppTimezone(tz?: string) 并扩展输入支持与默认逻辑。
测试修正与新增用例
packages/basic/tests/init/utils/*.spec.js, packages/basic/tests/init/utils/date-time/*.spec.js, .../list/*.spec.js
大量测试将 global.sdkUtilsglobal.Utilsglobal.sdkHelpersglobal.Helpers;将 moment(-tz) 用法替换为 Luxon 并调整格式令牌与字段访问;新增大型对比测试 date-time/compact.spec.js 与旧实现夹具。

Sequence Diagram(s)

(无)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 月色里我数秒针,
换了 Luxon 换新身,
格式对齐静又真,
全局改名步并行,
测试与旧友笑相亲。

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 标题清晰准确地总结了主要变化:将日期时间工具库从 date-fns/moment 迁移到 Luxon,与代码变更内容完全对应。
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/luxon

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In `@packages/basic/src/sdk/modules/utils.ts`:
- Around line 134-137: The startOfWeek function ignores the options.weekStartsOn
argument; update startOfWeek to respect options.weekStartsOn (defaulting to the
same value used by getWeek) by computing the weekday difference and subtracting
that many days from the given date rather than calling Luxon’s startOf('week')
(which always uses Monday). Locate the startOfWeek function and replace the
Luxon startOf call with logic that reads options?.weekStartsOn, computes diff =
(currentDay - weekStartsOn + 7) % 7, subtracts diff days from the input Date
(using Date or DateTime arithmetic), and returns the resulting Date so the
function honors weekStartsOn.
- Around line 126-128: getWeek currently ignores the options parameter; if
options.weekStartsOn is provided we must compute the week number relative to
that start day instead of always using ISO weeks. Change getWeek to: create a
Luxon DateTime from the input, and if options?.weekStartsOn is set compute an
adjustment using DateTime.weekday and the desired week start (treat
weekStartsOn: 0 as Sunday which maps to ISO weekday 7), shift the DateTime
backward by that many days (so that the target week start aligns with ISO
Monday), then return the shifted DateTime.weekNumber; if options is not provided
just return DateTime.fromJSDate(date).weekNumber. Use the function name getWeek,
DateTime.fromJSDate, and DateTime.weekday to locate and implement the fix.

In `@packages/basic/tests/init/utils/date-time/fromString.spec.js`:
- Around line 15-30: The tests in the DateTime parsing suite hardcode a +08:00
offset and will fail in other timezones; update the three tests that call
fromString('...', 'nasl.core.DateTime') to assert the timezone generically
instead of a fixed string: either match the result with a regex for an ISO
datetime with any offset, or compute the expected offset at runtime (e.g.,
derive new Date().getTimezoneOffset() and format the expected suffix) and
compare that, ensuring you refer to the fromString calls in the "should parse
standard datetime string", "should parse ISO datetime string", and "should parse
date-only string to DateTime" tests when making the change.

In `@packages/basic/tests/init/utils/date-time/toString.spec.js`:
- Around line 65-89: The tests hardcode expected DateTime outputs assuming the
runner is in Asia/Shanghai (UTC+8), causing failures in other CI timezones;
update the tests that call toString (tests around toString('nasl.core.DateTime',
...)) to compute expected values dynamically or to assert using an explicit
timezone: either (A) call toString with an explicit timezone argument (e.g.,
'Asia/Shanghai' or 'UTC') and assert the known result, or (B) compute the
expected string at runtime from the input UTC value using
Intl.DateTimeFormat/Date with the same timezone logic used by toString and
compare against that computed value; apply this change to the "default timezone"
and "local datetime string" tests so they no longer rely on the test process TZ.

In `@packages/basic/tests/sdk/utils-luxon.spec.js`:
- Around line 48-59: The tests for sdkUtils.CurrDateTime are too loose (only
checking string), so update them to validate the returned value is a valid ISO
datetime and that the timezone argument actually affects the result: parse the
result with Luxon (DateTime.fromISO(result, { setZone: true })) and assert
.isValid is true for the default case; for the timezone case call
DateTime.now().setZone('Asia/Shanghai') to get the expected offset and assert
DateTime.fromISO(result, { setZone: true }).offset equals that expected offset
(or otherwise compare zone-aware offsets) to ensure the 'Asia/Shanghai' argument
is applied. Ensure both tests reference sdkUtils.CurrDateTime.
- Around line 128-144: The tests for GetSpecificDaysOfWeek use an if
(result.length > 0) guard which masks failures; replace those guards with
explicit assertions that the result is non-empty (e.g.,
expect(result.length).toBeGreaterThan(0)) and then assert the expected
value/format of result[0]; update both tests (the datetime case checking exact
'2024-01-01T00:00:00.000+08:00' and the date case checking
/^\d{4}-\d{2}-\d{2}$/) so they fail when GetSpecificDaysOfWeek returns an empty
array.
🧹 Nitpick comments (10)
packages/basic/src/sdk/Formatters/DateFormatter.ts (1)

4-6: 过时的 TODO 注释应该移除或更新。

该注释建议使用 moment 或其他库,但现在已经在使用 Luxon 了。建议更新或移除此注释以避免混淆。

🔧 建议的修改
-/**
- * `@TODO`: use moment or some other library
- */
+// Date formatting utilities using Luxon for ISO patterns
packages/basic/LUXON_MIGRATION.md (1)

94-107: 建议为代码块添加语言标识符。

静态分析工具检测到第 94-107 行的状态标记部分缺少语言标识符。虽然这些不是真正的代码块(只是状态文本),但如果保持当前格式,建议添加 text 或移除代码块格式。

markdown fenced code block best practices for plain text
packages/basic/tests/sdk/utils-date-fns-migration.spec.js (2)

267-274: 存在未使用的变量。

mondaytuesday 变量声明后未被使用,应该移除或实际使用它们进行断言。

🔧 建议的修改
 describe('date-fns 迁移到 Luxon - 辅助函数验证', () => {
   test('isMonday', () => {
-    const monday = new Date('2024-01-01'); // Monday
-    const tuesday = new Date('2024-01-02');
     // Note: isMonday 等函数在内部使用,这里通过 GetSpecificDaysOfWeek 间接测试
     const result = utils.GetSpecificDaysOfWeek('2024-01-01', '2024-01-07', [1]); // Monday
     expect(result.length).toBe(1);
   });

340-346: 性能测试的断言没有实际意义。

expect(true).toBe(true) 不验证任何实际行为。如果要保留性能测试,建议添加有意义的断言或移除这个测试用例。

🔧 建议的修改
   test('should handle multiple DateDiff calculations', () => {
+    const start = Date.now();
     for (let i = 0; i < 100; i++) {
       utils.DateDiff('2020-01-01', '2024-12-31', 'd');
     }
-    // If this test passes, performance is acceptable
-    expect(true).toBe(true);
+    const elapsed = Date.now() - start;
+    // 100 次计算应在合理时间内完成
+    expect(elapsed).toBeLessThan(500);
   });
packages/basic/src/sdk/modules/utils.ts (2)

313-326: 重复的日期解析逻辑可提取为辅助函数

JsonSerialize 中存在三处几乎相同的日期解析回退逻辑。建议提取为一个辅助函数以减少代码重复。

♻️ 建议的重构
+function parseToDateTime(v: string, zone: string): DateTime {
+  let dt = DateTime.fromISO(v, { zone });
+  if (!dt.isValid && typeof v === 'string') {
+    dt = DateTime.fromFormat(v, 'yyyy-MM-dd HH:mm:ss', { zone });
+  }
+  if (!dt.isValid && typeof v === 'string') {
+    dt = DateTime.fromFormat(v, 'yyyy-MM-dd HH:mm:ss.SSS', { zone });
+  }
+  return dt;
+}

 JsonSerialize(v, tz?) {
   if (isInputValidNaslDateTime(v)) {
     if (!tz) {
-      let dt = DateTime.fromISO(v, { zone: 'UTC' });
-      if (!dt.isValid && typeof v === 'string') {
-        dt = DateTime.fromFormat(v, 'yyyy-MM-dd HH:mm:ss', { zone: 'UTC' });
-      }
-      if (!dt.isValid && typeof v === 'string') {
-        dt = DateTime.fromFormat(v, 'yyyy-MM-dd HH:mm:ss.SSS', { zone: 'UTC' });
-      }
-      const d = dt.isValid
-        ? dt.toFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
-        : DateTime.fromJSDate(safeNewDate(v), { zone: 'UTC' }).toFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");
+      const dt = parseToDateTime(v, 'UTC');
+      const d = dt.isValid
+        ? dt.toFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
+        : DateTime.fromJSDate(safeNewDate(v), { zone: 'UTC' }).toFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");
       return JSON.stringify(d);
     }
     // ... similar refactoring for other branches
   }
 }

Also applies to: 331-341, 346-356


1575-1593: ConvertTimezone 函数现在是空操作

函数验证了输入但直接返回原值,未进行任何时区转换。注释说明这是为了兼容老版本升级脚本,但当前实现可能会让调用者困惑。建议添加更明确的文档说明或考虑抛出弃用警告。

 ConvertTimezone(dateTime, tz) {
   if (!dateTime) {
     this.helpers.throwError(`内置函数ConvertTimezone入参错误:指定日期为空`);
   }
   if (!isValid(safeNewDate(dateTime))) {
     this.helpers.throwError(`内置函数ConvertTimezone入参错误:指定日期不是合法日期类型`);
   }
   if (!isValidTimezoneIANAString(tz)) {
     this.helpers.throwError(`内置函数ConvertTimezone入参错误:传入时区${tz}不是合法时区字符`);
   }

+  // 注意:此函数为了向后兼容保留,但不再执行实际的时区转换
+  // 如需时区转换,请使用其他方法
   const result = dateTime;
   return result;
 }
packages/basic/tests/init/utils/date-time/genInitData.spec.js (1)

4-11: 建议在 afterAll 中清理模拟数据

直接修改 typeDefinitionMap 可能影响其他测试套件。建议在测试完成后恢复原始状态。

♻️ 建议的修复
 describe('genInitData function - DateTime handling', () => {
+  let originalTypeDefinition;
+
   beforeAll(() => {
+    originalTypeDefinition = typeDefinitionMap['nasl.core.DateTime'];
     // Mock typeDefinitionMap for testing
     typeDefinitionMap['nasl.core.DateTime'] = {
       typeKind: 'primitive',
       typeName: 'DateTime',
       typeNamespace: 'nasl.core',
     };
   });
+
+  afterAll(() => {
+    if (originalTypeDefinition) {
+      typeDefinitionMap['nasl.core.DateTime'] = originalTypeDefinition;
+    } else {
+      delete typeDefinitionMap['nasl.core.DateTime'];
+    }
+  });
packages/basic/tests/init/utils/date-time/toString.spec.js (2)

1-1: 变量名遮蔽了全局 toString

静态分析工具标记:导入的 toString 遮蔽了全局 toString 属性。建议使用别名导入以提高代码清晰度。

♻️ 建议的修复
-const { toString, typeDefinitionMap } = require('../../../../src/init/dataTypes/tools');
+const { toString: naslToString, typeDefinitionMap } = require('../../../../src/init/dataTypes/tools');

然后在测试中使用 naslToString 替代 toString


4-13: 建议在测试后清理模拟数据

genInitData.spec.js 相同,直接修改 typeDefinitionMap 可能影响其他测试。

packages/basic/tests/init/utils/date-time/fromString.spec.js (1)

4-13: 建议在测试后清理模拟数据

与其他测试文件相同,直接修改 typeDefinitionMap 可能影响其他测试套件。

Comment thread packages/basic/src/sdk/modules/utils.ts Outdated
Comment thread packages/basic/src/sdk/modules/utils.ts Outdated
Comment thread packages/basic/tests/init/utils/date-time/fromString.spec.js Outdated
Comment thread packages/basic/tests/init/utils/date-time/toString.spec.js Outdated
Comment thread packages/basic/tests/sdk/utils-luxon.spec.js Outdated
Comment thread packages/basic/tests/sdk/utils-luxon.spec.js Outdated
@YufJi YufJi changed the title feat: Migrate date handling utilities from date-fns to Luxon chore: 迁移 date-fns/moment 到 Luxon Feb 6, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/basic/src/sdk/modules/utils.ts (2)

306-357: 🛠️ Refactor suggestion | 🟠 Major

JsonSerialize 中 !tztz === 'UTC' 两个分支逻辑完全相同,存在重复代码。

Lines 312-322 和 Lines 324-334 的处理逻辑一模一样(都使用 { zone: 'UTC' } 并格式化为同一模式),可以合并简化:

♻️ 建议合并
     if (isInputValidNaslDateTime(v)) {
-      if (!tz) {
-        let dt =
-          v instanceof Date
-            ? DateTime.fromJSDate(v, { zone: 'UTC' })
-            : v?.includes('T')
-              ? DateTime.fromISO(v, { zone: 'UTC' })
-              : DateTime.fromFormat(v, 'yyyy-MM-dd HH:mm:ss', { zone: 'UTC' });
-
-        const d = dt.toFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");
-        return JSON.stringify(d);
-      }
-      if (tz === 'UTC') {
-        let dt =
-          v instanceof Date
-            ? DateTime.fromJSDate(v, { zone: 'UTC' })
-            : v?.includes('T')
-              ? DateTime.fromISO(v, { zone: 'UTC' })
-              : DateTime.fromFormat(v, 'yyyy-MM-dd HH:mm:ss', { zone: 'UTC' });
-
-        const d = dt.toFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");
-        return JSON.stringify(d);
-      }
-      if (tz) {
+      const targetZone = !tz || tz === 'UTC' ? 'UTC' : getAppTimezone(tz);
+      {
-        const targetZone = getAppTimezone(tz);
         let dt =
           v instanceof Date
             ? DateTime.fromJSDate(v, { zone: targetZone })
-            : v?.includes('T')
+            : typeof v === 'string' && v.includes('T')
               ? DateTime.fromISO(v, { zone: targetZone })
               : DateTime.fromFormat(v, 'yyyy-MM-dd HH:mm:ss', { zone: targetZone });

         const d = dt.toFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");
         return JSON.stringify(d);
       }

同时注意 v?.includes('T') 存在与 DateFormatter.ts 相同的类型安全问题:当 vDate 对象以外的非字符串类型时会抛出错误。不过这里 v instanceof Date 先行判断可以缓解此风险。


1565-1583: ⚠️ Potential issue | 🟠 Major

ConvertTimezone 函数是一个无操作实现,没有执行任何时区转换。

此函数作为公开 API 的一部分被导出,但第 1581 行直接返回未修改的输入值(const result = dateTime; return result;)。虽然实现了参数校验,但未执行实际的时区转换逻辑。

根据代码中的注释,这是来自 v3.1 版本升级脚本的遗留代码,其目的和必要性均有疑问。由于该函数已导出为公开 API,需要:

  1. 补充 Luxon 库的时区转换实现(参考现有的 addDaysaddMonths 等实现模式)
  2. 或明确将其标记为已弃用
  3. 或在无破坏性变更的情况下将其移除
🤖 Fix all issues with AI agents
In `@packages/basic/jest.setup.js`:
- Around line 8-12: Basic.initDataTypes 的 dataTypesMap 目前只注册了
'nasl.core.DateTime',需要补充对 'nasl.core.Date' 和 'nasl.core.Time' 的注册以保持与库中转换逻辑一致;在
jest.setup.js 中的 Basic.initDataTypes 调用里(dataTypesMap)添加条目 'nasl.core.Date' 和
'nasl.core.Time',每个条目与现有 'nasl.core.DateTime' 的结构一致(即提供 typeName 字段),确保与
packages/basic/src/init/dataTypes/tools.ts、helper.ts 和测试用例使用的类型名称匹配。

In `@packages/basic/src/sdk/Formatters/DateFormatter.ts`:
- Around line 56-63: The current branching in DateFormatter (the dt assignment
using DateTime.fromISO/fromJSDate) calls value?.includes('T') which throws when
value is a number; change the check to only call includes when value is a string
(e.g., use typeof value === 'string' && value.includes('T')) so numeric
timestamps fall through to DateTime.fromJSDate(new Date(value)); update the dt
assignment that uses DateTime.fromISO and DateTime.fromJSDate accordingly to
handle Date, string-ISO, and numeric inputs safely.

In `@packages/basic/src/sdk/helper.ts`:
- Around line 76-88: The getAppTimezone function's parameter tz is typed as
string but callers pass an optional/undefined value; update the signature of
getAppTimezone to accept undefined (e.g., tz?: string or tz: string | undefined)
so the code and TypeScript reflect that tz can be missing, and ensure any
exported type declarations involving getAppTimezone are updated; check related
callers such as GetDateCount and ToString in utils.ts to confirm their optional
tz? usage aligns with the new signature.

In `@packages/basic/src/sdk/modules/utils.ts`:
- Around line 66-112: The differenceIn* functions (differenceInYears,
differenceInQuarters, differenceInMonths, differenceInWeeks, differenceInDays,
differenceInHours, differenceInMinutes, differenceInSeconds) currently use
Math.floor which incorrectly rounds negative diffs toward -Infinity; replace
Math.floor(...) with Math.trunc(...) in each function so differences are
truncated toward zero (matching date-fns behavior) while keeping the rest of the
DateTime.fromJSDate and .diff(...) logic unchanged.
- Around line 2-3: Remove the leftover date-fns import (getWeekOfMonth) and
replace its use in GetDateCountOld with a Luxon-based calculation: create a
small helper that uses DateTime.fromJSDate(...) (or
DateTime.fromISO/DateTime.fromMillis as appropriate), get startOfMonth.weekday
and the target date.day, then compute weekOfMonth = Math.ceil((dateDay +
startOfMonth.weekday - 1) / 7); update GetDateCountOld to call this helper and
delete the getWeekOfMonth import.

In `@packages/basic/tests/init/utils/date-time/compact.spec.js`:
- Around line 19-22: The test uses new Date(datetime) with datetime =
'2026-02-05 12:00:00' which is implementation-defined and assumes a TZ (e.g.,
comment on isoDatetime expects '2026-02-05T04:00:00.000Z'); fix by making date
parsing deterministic: replace the ambiguous construction around
datetime/jsDate/isoDatetime with an explicit UTC or offset-aware creation (e.g.,
parse '2026-02-05T12:00:00Z' or construct via Date.UTC) or ensure the test sets
TZ to a fixed zone (e.g., TZ=Asia/Shanghai) in the test setup so isoDatetime is
stable across environments. Ensure references to datetime, jsDate and
isoDatetime are updated accordingly.
- Line 1: 测试文件 compact.spec.js 中的 require('./old-basic') 报找不到模块,导致 CI
失败;请在同目录新增模块文件 named old-basic.js(或创建 old-basic/index.js)并导出预期的符号(确保导出能被 const
OldBasic = require('./old-basic') 正常使用),或者修正 compact.spec.js
中的导入路径以指向正确的文件/包;确认文件名大小写与仓库一致并提交到 PR。
- Around line 58-82: The CurrDate/CurrTime tests are flaky due to time advancing
between OldUtils.CurrDate()/Utils.CurrDate() (and CurrTime) calls; update the
tests to freeze system time using Jest fake timers (e.g., jest.useFakeTimers +
jest.setSystemTime) before calling OldUtils.CurrDate/Utils.CurrDate and
OldUtils.CurrTime/Utils.CurrTime, assert equality, then restore real timers with
jest.useRealTimers()/jest.clearAllTimers; ensure both timezone and non-timezone
branches use the frozen timestamp so CurrDate, CurrTime, OldUtils.CurrDate, and
OldUtils.CurrTime return deterministic, matching values.

In `@packages/basic/tests/init/utils/date-time/old-basic/package.json`:
- Around line 1-2: Update the duplicated "name" field in the test fixture
package.json so it no longer conflicts with the root package.json: open the
package.json containing the "name": "@lcap/basic-template" entry and change it
to a unique value (e.g., "@lcap/basic-template-old-fixture") or alternatively
add this fixture directory to Jest's exclusions by updating Jest config
(modulePathIgnorePatterns) to ignore the fixture folder; ensure you only modify
the fixture package.json "name" or Jest config and keep the root package.json
unchanged.

In `@packages/basic/tests/init/utils/date-time/old-basic/README.md`:
- Around line 93-131: The fenced code blocks in README.md that show the
directory structure are missing a language identifier (markdownlint MD040);
update the opening backticks for those blocks (the directory-structure block and
the other fenced block mentioned) from ``` to ```text or ```plaintext so the
blocks declare a language (e.g., change ``` to ```text for the src/ tree block
and the similar block at the other location).

In `@packages/basic/tests/sdk/utils-date-fns-migration.spec.js`:
- Around line 240-246: The test for GetSpecificDaysOfWeek is non-deterministic
because it assumes a +08:00 local timezone; make the test deterministic by
setting the process TZ for the test or computing the expected strings in the
same zone: either (A) set process.env.TZ = 'Asia/Shanghai' (or export TZ) at the
top of packages/basic/tests/sdk/utils-date-fns-migration.spec.js before running
the test, or (B) compute the expected values using the same zone used by the
function (e.g., use DateTime.fromJSDate(date).setZone('America/New_York' or
'Asia/Shanghai').toFormat(...) inside the test) so the expected offsets match
regardless of runner environment; reference the GetSpecificDaysOfWeek call and
update the test accordingly.
🧹 Nitpick comments (14)
packages/basic/tests/init/utils/list/list-arith-functions.spec.js (1)

5-21: 预已存在的问题:.toBeNull 缺少括号,断言实际未生效

第 7、11、15、19 行的 expect(...).toBeNull 缺少 (),这只是访问了属性而非调用 matcher,因此这些断言实际上不会执行任何检查。虽然这不是本次 PR 引入的问题,但既然已经在修改此文件,建议一并修复。

♻️ 建议修复
     fns.forEach((fn) => {
-      expect(fn.call(utils, undefined)).toBeNull;
+      expect(fn.call(utils, undefined)).toBeNull();
     });

     fns.forEach((fn) => {
-      expect(fn.call(utils, null)).toBeNull;
+      expect(fn.call(utils, null)).toBeNull();
     });

     fns.forEach((fn) => {
-      expect(fn.call(utils, [])).toBeNull;
+      expect(fn.call(utils, [])).toBeNull();
     });

     fns.forEach((fn) => {
-      expect(fn.call(utils, [undefined, null, null])).toBeNull;
+      expect(fn.call(utils, [undefined, null, null])).toBeNull();
     });
packages/basic/tests/init/utils/list/list-abnormal-input.spec.js (1)

38-63: 预存问题:.toBeNull.toThrow 缺少括号,断言实际未执行。

这些行中 expect(...).toBeNullexpect(() => ...).toThrow 只是访问了属性,并没有真正调用 Jest 的 matcher。正确写法应为 .toBeNull().toThrow()。当前这些断言永远不会失败,等于没有测试。

虽然这不是本 PR 引入的问题,但既然在改动此文件,建议顺手修复。

♻️ 建议修复(示例,其他类似行同理)
       try {
-        expect(fn(undefined)).toBeNull;
+        expect(fn(undefined)).toBeNull();
       } catch (err) {
-        expect(() => fn(undefined)).toThrow;
+        expect(() => fn(undefined)).toThrow();
       }
       try {
-        expect(fn(null)).toBeNull;
+        expect(fn(null)).toBeNull();
       } catch (err) {
-        expect(() => fn(null)).toThrow;
+        expect(() => fn(null)).toThrow();
       }
       try {
-        expect(fn([])).toBeNull;
+        expect(fn([])).toBeNull();
       } catch (err) {
-        expect(() => fn([])).toThrow;
+        expect(() => fn([])).toThrow();
       }

同样适用于第 74 行的 .toBeNull

packages/basic/tests/init/utils/date-diff.spec.js (1)

8-18: 三个测试用例使用了相同的名称 'DateDiff 按日'

虽然这是已有问题而非本次变更引入,但重复的测试名称会导致测试报告难以区分失败用例。建议后续区分命名,例如 'DateDiff 按日 - 跨天不足一天''DateDiff 按日 - 完整天数' 等。

packages/basic/tests/init/utils/convert.spec.js (1)

58-78: 被注释的 ConvertDate / Time 类型的测试用例。

这些测试在 Luxon 迁移后是否仍然需要?如果 DateTime 类型转换已在新实现中支持,建议取消注释并更新断言;如果已废弃,建议清理删除以避免代码腐化。

packages/basic/tests/init/utils/date-time/old-basic/README.md (1)

1-5: 文档放置位置不寻常。

这是一份项目级的技术说明文档,但放在了 tests/init/utils/date-time/old-basic/ 目录下。如果是面向整个 @lcap/basic-template 包的文档,建议放到 packages/basic/ 根目录或 docs/ 目录下;如果仅用于说明 old-basic 测试的历史上下文,建议在标题中明确标注范围。

packages/basic/tests/sdk/utils-luxon.spec.js (1)

1-7: Utils 变量获取方式与其他测试文件不一致。

其他测试文件(如 split.spec.jsconvert.spec.js 等)均使用 const utils = global.Utils; 在模块顶层直接赋值,而此文件使用 let Utils + beforeAll 赋值。建议统一风格。

packages/basic/tests/sdk/utils-date-fns-migration.spec.js (3)

268-274: 未使用的变量 mondaytuesday

Lines 269-270 声明了 mondaytuesday 但从未在断言中使用,属于死代码。

♻️ 建议清理
   test('isMonday', () => {
-    const monday = new Date('2024-01-01'); // Monday
-    const tuesday = new Date('2024-01-02');
-    // Note: isMonday 等函数在内部使用,这里通过 GetSpecificDaysOfWeek 间接测试
     const result = utils.GetSpecificDaysOfWeek('2024-01-01', '2024-01-07', [1]); // Monday
     expect(result.length).toBe(1);
   });

340-346: 空断言 expect(true).toBe(true) 无实际校验作用。

如果目的是验证性能(循环 100 次不崩溃),可以改为验证返回值一致性,或至少添加耗时断言使测试更有意义。

♻️ 建议改进
   test('should handle multiple DateDiff calculations', () => {
+    const start = Date.now();
     for (let i = 0; i < 100; i++) {
       utils.DateDiff('2020-01-01', '2024-12-31', 'd');
     }
-    // If this test passes, performance is acceptable
-    expect(true).toBe(true);
+    const elapsed = Date.now() - start;
+    expect(elapsed).toBeLessThan(1000);
   });

309-315: 无效日期测试断言过于宽松。

注释说明了 Luxon 与 date-fns 的行为差异,但仅验证 typeof result === 'number' 不够充分。建议至少断言返回值不为 NaN,以确认函数确实产生了有意义的结果。

     const result = utils.DateDiff('invalid', '2024-01-01', 'd');
-    expect(typeof result).toBe('number');
+    expect(typeof result).toBe('number');
+    expect(Number.isNaN(result)).toBe(false);
packages/basic/src/sdk/helper.ts (1)

4-5: @TODO 注释已过时。

DateFormatter.ts 第 5 行仍然有 @TODO: use moment or some other library 的注释,但该 PR 的目标正是用 Luxon 替换 moment。虽然该注释不在本文件中,但既然 helper.ts 已完成迁移,建议一并清理。(参见 packages/basic/src/sdk/Formatters/DateFormatter.ts 第 5 行)

packages/basic/src/sdk/Formatters/DateFormatter.ts (1)

4-6: @TODO: use moment or some other library 注释已过时。

Luxon 已经在本 PR 中引入使用,此 TODO 应删除。

-/**
- * `@TODO`: use moment or some other library
- */
packages/basic/src/sdk/modules/utils.ts (1)

1155-1157: startOf('month') 的结果经过多层转换,可以简化。

new Date(DateTime.fromJSDate(date).startOf('month').toFormat('yyyy-MM-dd HH:mm:ss')) 链路过长:Luxon → 格式化字符串 → Date 构造。可直接使用 .toJSDate()

-const startOfMonth = new Date(DateTime.fromJSDate(date).startOf('month').toFormat('yyyy-MM-dd HH:mm:ss'));
+const startOfMonth = DateTime.fromJSDate(date).startOf('month').toJSDate();
packages/basic/tests/init/utils/date-time/compact.spec.js (2)

282-304: DateDiff 测试中,datetime2 等常量可提到循环外。

datetime2jsDate2isoDatetime2 不依赖 calcType,可以移到 for 循环外部避免重复创建。

建议的修复
  test('DateDiff', () => {
    const calcTypes = ['y', 'q', 'M', 'w', 'd', 'h'];
+   const datetime2 = '2026-02-10 12:00:00';
+   const jsDate2 = new Date(datetime2);
+   const isoDatetime2 = jsDate2.toJSON();

    for (const calcType of calcTypes) {
-     const datetime2 = '2026-02-10 12:00:00';
-     const jsDate2 = new Date(datetime2);
-     const isoDatetime2 = jsDate2.toJSON();

232-233: 跳过的测试用例(GetSpecificDaysOfWeekFormatTimegenInitData)缺少说明。

建议添加注释或 TODO 说明跳过原因和后续计划,方便后续跟进。例如:

// TODO: GetSpecificDaysOfWeek 新旧实现行为不一致,待确认后补充
test.skip('GetSpecificDaysOfWeek', () => {});

Also applies to: 257-258, 361-362

Comment thread packages/basic/jest.setup.js
Comment thread packages/basic/src/sdk/Formatters/DateFormatter.ts
Comment thread packages/basic/src/sdk/helper.ts Outdated
Comment thread packages/basic/src/sdk/modules/utils.ts Outdated
Comment thread packages/basic/src/sdk/modules/utils.ts Outdated
Comment thread packages/basic/tests/init/utils/date-time/compact.spec.js Outdated
Comment thread packages/basic/tests/init/utils/date-time/compact.spec.js Outdated
Comment thread packages/basic/tests/init/utils/date-time/old-basic/package.json Outdated
Comment thread packages/basic/tests/init/utils/date-time/old-basic/README.md Outdated
Comment thread packages/basic/tests/sdk/utils-date-fns-migration.spec.js Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/basic/src/sdk/modules/utils/index.ts (1)

1430-1448: ⚠️ Potential issue | 🟠 Major

ConvertTimezone 返回值类型不匹配,行为已变为 noop

该函数类型定义声明返回 string(见 packages/vue2/source/types/global.d.ts:338packages/vue3/source/types/global.d.ts:338),但实现现在直接返回原始输入值而不经过格式化。从测试文件中的旧实现可以看出,原来的行为是进行时区转换并格式化输出(yyyy-MM-dd'T'HH:mm:ssxxx 格式)。

虽然在代码库中未发现活跃的调用点,但这造成了 API 契约破裂:调用方基于类型声明期望得到格式化的字符串,而现在收到的是未转换的原始值。

需要:同步更新类型定义和实现,或者添加 @deprecated 标记并提供替代方案。

🤖 Fix all issues with AI agents
In `@packages/basic/src/sdk/modules/utils/date.ts`:
- Around line 103-105: getWeek and startOfWeek accept an options.weekStartsOn
parameter but ignore it (Luxon DateTime.weekNumber is ISO-based), which is
misleading and will produce wrong results if callers pass weekStartsOn: 0;
update both functions (getWeek and startOfWeek) to validate
options.weekStartsOn: if undefined or 1 proceed as today; if any value !== 1
either throw a clear error or log a warning and document behavior; alternatively
remove the options parameter from both signatures and update callers to avoid
suggesting configurable week start—ensure the chosen approach is applied
consistently and tests/call sites are updated accordingly.
- Around line 43-89: The differenceIn* functions (differenceInYears,
differenceInQuarters, differenceInMonths, differenceInWeeks, differenceInDays,
differenceInHours, differenceInMinutes, differenceInSeconds) use Math.floor
which incorrectly rounds negative differences toward -Infinity; replace
Math.floor(...) with Math.trunc(...) in each function so negative diffs are
truncated toward zero and match date-fns behavior (preserve existing
DateTime.fromJSDate and .diff(...) usage).

In `@packages/basic/src/sdk/modules/utils/index.ts`:
- Around line 1020-1023: The code constructs startOfMonth by formatting a Luxon
DateTime to a string and passing it to new Date(), which yields engine-dependent
parsing; change that to use Luxon's native toJSDate() directly (replace the new
Date(DateTime.fromJSDate(date).startOf('month').toFormat(...)) pattern with
DateTime.fromJSDate(date).startOf('month').toJSDate()) so startOfMonth is a real
Date without relying on string parsing; update any similar uses in this module
(e.g., where startOfMonth is computed) to use toJSDate().
- Around line 178-212: Merge the duplicate branches in JsonSerialize by treating
both !tz and tz === 'UTC' as the same UTC path: compute a zone variable (e.g.,
zone = tz && tz !== 'UTC' ? getAppTimezone(tz) : 'UTC') and use it for DateTime
creation so the three cases collapse to two (UTC and non-UTC). After creating
the DateTime (using DateTime.fromJSDate, fromISO, or fromFormat as now), check
dt.isValid and handle invalid parses explicitly (e.g., return a stable value or
throw/log an error instead of letting toFormat produce "Invalid DateTime");
reference the JsonSerialize function and getAppTimezone to locate the change.
🧹 Nitpick comments (2)
packages/basic/src/sdk/modules/utils/date.ts (1)

3-4: 保留了 date-fns 依赖

getWeekOfMonth 仍从 date-fns re-export,说明 date-fns 仍然是运行时依赖。如果迁移目标是完全移除 date-fns,这一项需要后续处理。注释中已表明了这一点,建议添加一个 TODO 标注以便追踪。

packages/basic/src/sdk/modules/utils/index.ts (1)

911-922: AddDays / AddMonths / SubDays 使用 toValue.call(this, ...) 传递上下文

查看 helper.ts 中的 toValue 实现,当 typeKey === 'json' 时会调用 this.JsonSerialize(date),因此需要 this 指向 Utils 实例。使用 .call(this) 确保了这一点,逻辑正确。

但这种隐式耦合较脆弱——toValue 是一个外部 helper 函数,却通过 this 回调 Utils 实例方法。如果后续 toValue 被重构或单独使用,容易出错。可以考虑后续将 toValue 改为显式接收回调参数。

Comment thread packages/basic/src/sdk/modules/utils/date.ts
Comment thread packages/basic/src/sdk/modules/utils/date.ts Outdated
Comment thread packages/basic/src/sdk/modules/utils/index.ts Outdated
Comment thread packages/basic/src/sdk/modules/utils/index.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/basic/src/sdk/modules/utils/date.ts`:
- Around line 121-133: eachDayOfInterval currently returns an empty array when
interval.start > interval.end, which differs from date-fns behavior; update the
function (eachDayOfInterval) to validate the interval after converting to
DateTime (DateTime.fromJSDate) and throw a RangeError (e.g., "Invalid interval")
when start > end so callers get the same error behavior as date-fns; keep the
rest of the day-iteration logic unchanged.
🧹 Nitpick comments (1)
packages/basic/src/sdk/modules/utils/date.ts (1)

3-4: 确认 tree-shaking 是否有效生效,或考虑用 Luxon 自行实现 getWeekOfMonth

PR 目标是完全迁移到 Luxon 减小体积,但此处仍 re-export 了 getWeekOfMonth 来自 date-fns(在 utils/index.ts 第 939 行被实际使用)。若打包工具 tree-shaking 失效,date-fns 2.30.0 仍会被完整打入 bundle,削弱迁移收益。建议:

  1. 确认最终产物中 date-fns 的实际体积占比;
  2. 若影响较大,可用 Luxon DateTime 的 dayweek 方法自行实现 getWeekOfMonth(逻辑并不复杂)。

Comment thread packages/basic/src/sdk/modules/utils/date.ts
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@YufJi YufJi changed the title chore: 迁移 date-fns/moment 到 Luxon chore: 迁移 date-fns/moment 到 luxon Feb 6, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/basic/src/sdk/modules/utils/index.ts (1)

1420-1433: ⚠️ Potential issue | 🟠 Major

ConvertTimezone 函数已被彻底阉割,返回输入原值(无操作)

该函数原本使用 OA() 进行实际的时区转换(见旧代码 old-basic/index.js 第 20594 行),现已变为空操作:const result = dateTime; return result;。虽然保留了输入参数校验,但核心转换逻辑被完全移除。

同时存在两个问题:

  1. 类型签名不符:global.d.ts 声明返回类型为 string,但实现返回的是原始输入值
  2. 公开 API 行为变化:该函数是全局可用的公开接口,任何依赖它进行时区转换的应用代码会在升级后静默获得未转换的结果

建议选择以下方案之一:

  • 使用现有的 convertJSDateInTargetTimeZone 辅助函数恢复实现(其他日期函数都在使用)
  • 如果确认该函数不再需要,应添加 @deprecated 标记并明确通知用户停用
🤖 Fix all issues with AI agents
In `@packages/basic/src/sdk/modules/utils/index.ts`:
- Around line 1069-1072: Replace Luxon format tokens that use "ZZ" with "ZZZ" in
the four places to maintain moment.js-compatible timezone offset without a
colon: change the format string "yyyy-MM-dd'T'HH:mm:ss.SSSZZ" to use "ZZZ" in
the JsonSerialize, AlterDateTime, GetSpecificDaysOfWeek, and Convert
implementations so they output offsets like +0530; update those format calls
accordingly (or confirm downstream accepts coloned offsets before changing).
🧹 Nitpick comments (3)
packages/basic/src/sdk/modules/utils/index.ts (3)

887-907: 标记废弃合理,建议补充迁移指引。

@deprecated 注解是好做法。建议在 JSDoc 中补充 @see 或替代方案说明(例如推荐使用 AlterDateTime),方便下游开发者迁移。


933-965: switch (metric1)case 缺少 break,存在 fall-through 风险

这是既有问题而非本 PR 引入的,但迁移过程中值得注意:当 metric2 不匹配内层任何 case 时,外层 case 'day' 会穿透到 case 'week',依此类推,可能产生意外返回值。建议在后续迭代中补充 break 或改写为 if-else

Also applies to: 991-1035


1038-1072: AlterDateTimeaddDateunit 不匹配时为 undefined

如果 unit 参数不在已处理的枚举值中,addDate 保持 undefined,后续 format(addDate, ...) 调用会导致 DateTime.fromJSDate(undefined) 返回 invalid DateTime,最终输出 "Invalid DateTime" 字符串。这是既有问题,但迁移后表现可能有差异(date-fns 对无效输入的容错方式不同)。

建议在 switch 后增加兜底处理
      case 'year':
        addDate = addYears(date, amount);
        break;
+     default:
+       return dateString;
    }

Comment thread packages/basic/src/sdk/modules/utils/index.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/basic/src/sdk/helper.ts`:
- Around line 76-88: getAppTimezone may return undefined when
window?.appInfo?.appTimeZone is not set and tz === 'global'; update
getAppTimezone so that globalTimeZone is validated and, if falsy, falls back to
userTimeZone before returning. Concretely, in getAppTimezone compute
userTimeZone via Intl.DateTimeFormat().resolvedOptions().timeZone, compute
globalTimeZone from window?.appInfo?.appTimeZone, then when tz === 'global'
return globalTimeZone || userTimeZone; keep existing behavior for 'user' and
default cases.

Comment thread packages/basic/src/sdk/helper.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@packages/basic/src/sdk/modules/utils/date.ts`:
- Around line 16-17: The comment says the week starts on Monday but the default
weekStartsOn is 0 (Sunday); update the default to match the comment by changing
the default to 1 (Monday) in getWeekOfMonth (and likewise for the other
occurrences in this file that initialize weekStartsOn), or alternatively update
the comment to reflect Sunday—choose one consistent approach and apply it to all
instances that set weekStartsOn so the comment and default value match.

In `@packages/basic/tests/init/utils/date-time/date.spec.js`:
- Around line 22-25: The test uses new Date(datetime) where datetime is a
non‑ISO string with a space separator; update the test to use a deterministic
ISO 8601 string or build the Date explicitly: locate the loop using dates, the
variables jsDate and isoDatetime, and either replace the input datetime with an
ISO string (e.g. '2026-02-05T04:00:00.000Z') or transform the string before
parsing (e.g. datetime.replace(' ', 'T')) or parse components and call new
Date(year, month-1, day, hour, minute, second) so parsing is consistent across
environments; also update the comment for isoDatetime to reflect the actual
expected value.
- Around line 11-20: Remove the duplicate entry in the dates array (the repeated
'2023-01-01 00:00:00' in the dates constant) so each test date is unique, and
remove the unused import of format (the unused symbol format) from the test file
to eliminate the lint warning; update the dates array definition and delete the
import statement referencing format.
🧹 Nitpick comments (1)
packages/basic/src/sdk/modules/utils/date.ts (1)

145-168: getWeekYear 使用原生 date.getFullYear()Date 构造,与模块其余部分使用 Luxon 的风格不一致。

虽然功能上正确,但混用原生 Date 操作和 Luxon 会降低可维护性。考虑后续统一为 Luxon API。这不阻塞合并。

Comment thread packages/basic/src/sdk/modules/utils/date.ts Outdated
Comment thread packages/basic/tests/init/utils/date-time/date.spec.js
Comment thread packages/basic/tests/init/utils/date-time/date.spec.js
@YufJi YufJi changed the title chore: 迁移 date-fns/moment 到 luxon chore: 迁移日期时间工具库至luxon Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants