☰
NodeGui QDateTime 封装详解:从 TypeScript API 到 N-API 原生实现的完整指南
2026/9/25 4:54:07 网站建设 项目流程
  • 桌面应用
  • 跨平台

【免费下载链接】nodegui

A library for building cross-platform native desktop applications with Node.js and CSS 🚀. React NodeGui : https://react.nodegui.org and Vue NodeGui: https://vue.nodegui.org

项目地址:https://gitcode.com/gh_mirrors/no/nodegui
点击查看免费下载

本文基于 NodeGui 仓库中自动生成的 QDateTime 类参考文档(website/docs/api/generated/classes/qdatetime.md),系统梳理该类在 NodeGui 中的完整 API 面——构造函数、27 个实例方法与 6 个静态方法的签名、参数与返回值,并结合仓库中 TypeScript 封装层 与 N-API 原生封装实现 的源码,深入讲解其构造分发、可变与不可变方法的区分、时区/格式枚举以及跨层数据传递机制。读完本文,你不仅能直接使用 QDateTime 完成时间计算、解析与格式化,还能理解每个方法在 JS 层与 C++ 层之间的完整调用链。

类定位与继承关系

QDateTime 是 NodeGui 对 Qt 同名类的绑定,属于 QtCore 模块,在仓库中由以下文件共同构成:

层次文件职责
TypeScript 封装src/lib/QtCore/QDateTime.ts对外暴露的QDateTime类,参数校验与包装
N-API 绑定实现src/cpp/lib/QtCore/QDateTime/qdatetime_wrap.cpp将QDateTimeC++ 对象方法注册为 JS 可调用方法
N-API 绑定声明src/cpp/include/nodegui/QtCore/QDateTime/qdatetime_wrap.hQDateTimeWrap类与方法声明
API 参考文档website/docs/api/generated/classes/qdatetime.md本文所依据的生成式 API 文档

从参考文档的 Hierarchy 一节和 TS 源码可以确认,QDateTime继承自 Component 基类,并对外暴露一个继承来的native属性(类型为 NativeElement | null),用于持有底层 C++ 对象。类通过 src/index.ts 中的export { QDateTime } from './lib/QtCore/QDateTime'从包入口导出。

在 C++ 侧,QDateTimeWrap继承Napi::ObjectWrap<QDateTimeWrap>,以std::unique_ptr<QDateTime>持有内部 Qt 实例(见 qdatetime_wrap.h),并在构造时调用extrautils::configureComponent配置组件元数据。当 JS 侧包装对象被垃圾回收时,析构函数释放该QDateTime实例,即内存生命周期由 JS GC 管理。

构造 QDateTime

参考文档给出的构造函数签名为:

new QDateTime(arg?: NativeElement, time?: NativeElement): QDateTime

TypeScript 实现 实际上支持三种调用形态,按以下优先级分发:

  1. 两参数(arg与time同时存在):native = new addon.QDateTime(arg.native, time.native),即用一对日期与时间的原生对象构造。传入的通常是 QDate 与 QTime 实例,它们各自携带native属性;
  2. 单参数且为 NativeElement(通过checkIfNativeElement判断):直接复用传入的原生对象,不做转换;
  3. 其他情况(无参数或参数不合法):new addon.QDateTime(),构造一个默认的(空)QDateTime。

C++ 侧构造函数(qdatetime_wrap.cpp)对参数个数有更严格的校验,这一行为值得注意:

if (info.Length() == 2) { Napi::Object dateObject = info[0].As<Napi::Object>(); Napi::Object timeObject = info[1].As<Napi::Object>(); QDateWrap* dateWrap = Napi::ObjectWrap<QDateWrap>::Unwrap(dateObject); QTimeWrap* timeWrap = Napi::ObjectWrap<QTimeWrap>::Unwrap(timeObject); this->instance = std::make_unique<QDateTime>( *dateWrap->getInternalInstance(), *timeWrap->getInternalInstance()); } else if (info.Length() == 1) { this->instance = std::unique_ptr<QDateTime>( info[0].As<Napi::External<QDateTime>>().Data()); } else if (info.Length() == 0) { this->instance = std::make_unique<QDateTime>(); } else { Napi::TypeError::New(env, "Wrong number of arguments") .ThrowAsJavaScriptException(); }

即两参数时从两个包装对象中解出内部QDate/QTime实例构造;单参数时从Napi::External<QDateTime>指针接管已有实例;0 参数默认构造;其余情况抛出TypeError: Wrong number of arguments。这也意味着new QDateTime()得到的是一个无效的QDateTime(Qt 中空构造的对象isValid()为false、isNull()为true),使用时应先用状态查询方法确认有效性。

基础用法示例

const { QDate, QTime, QDateTime, TimeSpec, DateFormat } = require('nodegui'); // 1. 用当前时间 const now = QDateTime.currentDateTime(); // 2. 由 QDate + QTime 组合构造(对应两参数构造路径) const date = new QDate(2024, 1, 15); // QDate(year, month, day) const time = new QTime(10, 30, 0, 0); // QTime(hour, minute, second, msec) const dt = new QDateTime(date, time); // 3. 解析字符串 const parsed = QDateTime.fromString('2024-01-15 10:30:00', 'yyyy-MM-dd hh:mm:ss'); // 4. 默认构造 —— 注意此时对象无效,isNull() 为 true const empty = new QDateTime(); console.log(empty.isValid(), empty.isNull());

实例方法总览

参考文档的 Methods 一节完整列出了 27 个实例方法与 6 个静态方法。为避免信息缺失,下表先给出全部实例方法签名(与文档一一对应),后续小节再按功能分组展开。

方法参数返回值
addDaysndays: numberQDateTime
addMSecsmsecs: numberQDateTime
addMonthsnmonths: numberQDateTime
addSecss: numberQDateTime
addYearsnyears: numberQDateTime
date—QDate
daysToother:QDateTimenumber
isDaylightTime—boolean
isNull—boolean
isValid—boolean
msecsToother:QDateTimenumber
offsetFromUtc—number
secsToother:QDateTimenumber
setDatedate: QDatevoid
setMSecsSinceEpochmsecs: numbervoid
setOffsetFromUtcoffsetSeconds: numbervoid
setSecsSinceEpochsecs: numbervoid
setTimetime: QTimevoid
setTimeSpecspec: TimeSpecvoid
time—QTime
timeSpec—TimeSpec
toLocalTime—QDateTime
toMSecsSinceEpoch—number
toOffsetFromUtcoffsetSeconds: numberQDateTime
toSecsSinceEpoch—number
toStringformat: string | DateFormatstring
toTimeSpecspec: TimeSpecQDateTime
toUTC—QDateTime

时间加减运算:add* 系列返回新对象

addDays(ndays)、addMSecs(msecs)、addMonths(nmonths)、addSecs(s)、addYears(nyears)五个方法都返回一个新的QDateTime,不修改原对象。从 C++ 实现可以印证这一点(qdatetime_wrap.cpp):

Napi::Value QDateTimeWrap::addDays(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); qint64 ndays = info[0].As<Napi::Number>().Int64Value(); QDateTime result = this->instance->addDays(ndays); auto instance = QDateTimeWrap::constructor.New( {Napi::External<QDateTime>::New(env, new QDateTime(result))}); return instance; }

模式是固定的:取出结果后new QDateTime(result)堆分配一份拷贝,再经Napi::External<QDateTime>传回 JS 新建一个包装对象。TS 层则统一写作return new QDateTime(this.native.addDays(ndays))(见 src/lib/QtCore/QDateTime.ts)。

参数精度上有一个源码级细节:addDays/addMSecs/addSecs在 C++ 侧按Int64Value()取 64 位整数(qint64),而addMonths/addYears按Int32Value()取 32 位整数,与 Qt 原生 API 的签名一致。

const deadline = parsed.addDays(7).addMonths(1).addSecs(3600);

比较与差值:daysTo / msecsTo / secsTo

三个差值方法都接收另一个QDateTime,返回number(可能为负)。C++ 侧通过Napi::ObjectWrap<QDateTimeWrap>::Unwrap(otherObject)从传入的 JS 对象解包出内部指针后调用this->instance->daysTo(...)等方法(qdatetime_wrap.cpp)。因此参数必须也是本封装的QDateTime实例,传入普通 JS 对象会导致解包失败。

const daysBetween = now.daysTo(deadline); // 相差的天数 const msBetween = now.msecsTo(deadline); // 相差的毫秒数

状态与分量查询

  • date():返回 QDate。注意 C++ 实现并非直接透传指针,而是重建:new QDate(date.year(), date.month(), date.day())(qdatetime_wrap.cpp);
  • time():同理返回由hour/minute/second/msec重建的 QTime;
  • isValid()/isNull():有效性判定的首选手段,尤其是经过默认构造或解析失败后;
  • isDaylightTime():当前时间点是否处于夏令时;
  • offsetFromUtc():距 UTC 的偏移(秒);
  • timeSpec():返回 TimeSpec 枚举值。

原地修改:set* 系列

与 add* 系列相反,set前缀方法原地修改当前对象并返回null(C++ 侧return env.Null()):

  • setDate(date)/setTime(time):分别替换日期部分与时间部分,参数必须是QDate/QTime包装对象(C++ 侧同样通过Unwrap取出内部实例);
  • setSecsSinceEpoch(secs)/setMSecsSinceEpoch(msecs):以距 Unix 纪元(1970-01-01T00:00:00 UTC)的秒数/毫秒数整体设定时间值,均为 64 位整数入参。这是构造精确时间点的常用技巧;
  • setTimeSpec(spec):修改时间的规格类型(本地时间/UTC 等),入参为 TimeSpec 数值,C++ 侧static_cast<Qt::TimeSpec>(spec)转换;
  • setOffsetFromUtc(offsetSeconds):仅对OffsetFromUTC规格生效,设置与 UTC 的偏移秒数(32 位整数)。
const t = new QDateTime(); t.setMSecsSinceEpoch(QDateTime.currentMSecsSinceEpoch()); // 以纪元毫秒填充当前时间

转换与格式化

  • toUTC()/toLocalTime()/toTimeSpec(spec)/toOffsetFromUtc(offsetSeconds):均返回新的QDateTime(add* 系列同款“拷贝 + External 回传”模式),原对象不变。四个方法与 TimeSpec 共同覆盖 Qt 的时间规格转换路径;
  • toSecsSinceEpoch()/toMSecsSinceEpoch():读出距 Unix 纪元的秒/毫秒数(64 位),与上文setSecsSinceEpoch/setMSecsSinceEpoch构成读写对;
  • toString(format):按格式输出字符串,format接受解析格式字符串(如'yyyy-MM-dd hh:mm:ss')或 DateFormat 枚举。

toString有一个值得留意的命名细节:原生绑定中该方法被注册为toString$(见 qdatetime_wrap.cpp 中的InstanceMethod("toString$", ...)),TS 层toString内部调用this.native.toString$(format)(src/lib/QtCore/QDateTime.ts)。加$后缀是为了避免与 JS 对象原型链上已有的Object.prototype.toString冲突——这是 NodeGui 全部带toString的封装类的统一做法(如QDate、QTime)。C++ 侧对format做了类型分发:字符串走QDateTime::toString(QString),数值走static_cast<Qt::DateFormat>(qdatetime_wrap.cpp)。

静态方法

参考文档的 Static 一节列出 6 个静态方法,TS 侧全部委托给addon.QDateTime上的同名静态方法:

静态方法签名说明
currentDateTime(): QDateTime当前本地时间
currentDateTimeUtc(): QDateTime当前 UTC 时间
currentMSecsSinceEpoch(): number当前时间的纪元毫秒数
currentSecsSinceEpoch(): number当前时间的纪元秒数
fromString(dateTimeString: string, format: string \| DateFormat): QDateTime按格式解析字符串
fromQVariant(variant: [QVariant](https://link.gitcode.com/i/6808c31f67b3434d8cc8b5b44e84f7e1)): QDateTime从 QVariant 提取 QDateTime

fromString的 C++ 实现(qdatetime_wrap.cpp)与toString一样按参数类型分发:format为字符串时走QDateTime::fromString(QString, QString)解析格式;为数值时走QDateTime::fromString(QString, Qt::DateFormat)预设格式。解析失败时 Qt 返回空QDateTime,即得到的对象isValid()为false,调用方应自行检查。

fromQVariant通过解包传入的 QVariant 包装对象并执行variant->value<QDateTime>()完成提取(qdatetime_wrap.cpp),适用于 QVariant 中携带 datetime 数据的场景。

TimeSpec 与 DateFormat 枚举

两个被setTimeSpec/toTimeSpec/timeSpec及toString/fromString引用的枚举,其数值定义直接来自仓库源码:

TimeSpec(src/lib/QtEnums/TimeSpec/index.ts):

成员值
LocalTime0
UTC1
OffsetFromUTC2
TimeZone3

DateFormat(src/lib/QtEnums/DateFormat/index.ts):

成员值
TextDate0
ISODate1
SystemLocaleDate2
LocaleDate3
SystemLocaleShortDate4
SystemLocaleLongDate5
DefaultLocaleShortDate6
DefaultLocaleLongDate7
RFC2822Date8
ISODateWithMs9

在 C++ 绑定层这些枚举就是普通 32 位整数(info[0].As<Napi::Number>().Int32Value()+static_cast),因此也可以不导入枚举直接传对应数值,但推荐导入枚举以保证可读性与 Qt 版本间数值稳定。

const { QDateTime, DateFormat, TimeSpec } = require('nodegui'); const now = QDateTime.currentDateTime(); console.log(now.toString(DateFormat.ISODate)); // ISO 8601 形式 console.log(now.toUTC().toString(DateFormat.ISODateWithMs)); // 先转 UTC 再带毫秒输出 now.setTimeSpec(TimeSpec.UTC); // 原地修改规格

原生层实现要点小结

结合 qdatetime_wrap.cpp 与 QDateTime.ts,QDateTime 封装的跨层机制可以归纳为以下几点:

  1. 注册:QDateTimeWrap::init用DefineClass一次性注册全部实例方法(InstanceMethod)与 6 个静态方法(StaticMethod),并挂接COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE宏提供组件通用能力;
  2. 对象传递:返回QDateTime/QDate/QTime的方法统一采用“堆分配拷贝 →Napi::External<T>::New包装 → 类构造器constructor.New生成新 JS 对象”的链路,每个 JS 对象独立持有一份 C++ 实例;
  3. 入参解包:接收其他封装类的方法统一用Napi::ObjectWrap<TWrap>::Unwrap取内部实例,因此参数必须是 NodeGui 的对应类型实例;
  4. 枚举即整数:TimeSpec/DateFormat在边界处是 int,C++ 侧负责static_cast回 Qt 枚举;
  5. 命名规避冲突:与 JS 原型方法重名的绑定统一加$后缀(如toString$),TS 层再以正常名称重新导出;
  6. 参数个数严格校验:构造函数 0/1/2 参数各有语义,其他个数直接抛TypeError。

参考文档与源码索引

  • API 参考文档(本文主体依据):website/docs/api/generated/classes/qdatetime.md
  • TypeScript 封装:src/lib/QtCore/QDateTime.ts
  • N-API 实现:src/cpp/lib/QtCore/QDateTime/qdatetime_wrap.cpp
  • N-API 声明:src/cpp/include/nodegui/QtCore/QDateTime/qdatetime_wrap.h
  • 关联类型:QDate、QTime、QVariant、Component
  • 枚举定义:src/lib/QtEnums/TimeSpec/index.ts、src/lib/QtEnums/DateFormat/index.ts

需要说明的是,该 API 参考文档位于generated目录,由工具从 TS 源码自动生成,因此方法清单、参数名与 src/lib/QtCore/QDateTime.ts 的 TS 签名严格一致;而参数精度(32 位/64 位整数)、解包方式等细节则需以 C++ 实现为准,本文已在相应小节中标注了具体行号。

  • 桌面应用
  • 跨平台

【免费下载链接】nodegui

A library for building cross-platform native desktop applications with Node.js and CSS 🚀. React NodeGui : https://react.nodegui.org and Vue NodeGui: https://vue.nodegui.org

项目地址:https://gitcode.com/gh_mirrors/no/nodegui
点击查看免费下载

相关推荐

上一篇:Awesome Python图神经网络:图数据与关系学习的深度学习
下一篇:5分钟上手Ghost-Downloader-3:新一代智能下载器完全指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询