- 桌面应用
- 跨平台
【免费下载链接】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
本文基于 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.h | QDateTimeWrap类与方法声明 |
| 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): QDateTimeTypeScript 实现 实际上支持三种调用形态,按以下优先级分发:
- 两参数(
arg与time同时存在):native = new addon.QDateTime(arg.native, time.native),即用一对日期与时间的原生对象构造。传入的通常是 QDate 与 QTime 实例,它们各自携带native属性; - 单参数且为 NativeElement(通过
checkIfNativeElement判断):直接复用传入的原生对象,不做转换; - 其他情况(无参数或参数不合法):
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 个静态方法。为避免信息缺失,下表先给出全部实例方法签名(与文档一一对应),后续小节再按功能分组展开。
| 方法 | 参数 | 返回值 |
|---|---|---|
addDays | ndays: number | QDateTime |
addMSecs | msecs: number | QDateTime |
addMonths | nmonths: number | QDateTime |
addSecs | s: number | QDateTime |
addYears | nyears: number | QDateTime |
date | — | QDate |
daysTo | other:QDateTime | number |
isDaylightTime | — | boolean |
isNull | — | boolean |
isValid | — | boolean |
msecsTo | other:QDateTime | number |
offsetFromUtc | — | number |
secsTo | other:QDateTime | number |
setDate | date: QDate | void |
setMSecsSinceEpoch | msecs: number | void |
setOffsetFromUtc | offsetSeconds: number | void |
setSecsSinceEpoch | secs: number | void |
setTime | time: QTime | void |
setTimeSpec | spec: TimeSpec | void |
time | — | QTime |
timeSpec | — | TimeSpec |
toLocalTime | — | QDateTime |
toMSecsSinceEpoch | — | number |
toOffsetFromUtc | offsetSeconds: number | QDateTime |
toSecsSinceEpoch | — | number |
toString | format: string | DateFormat | string |
toTimeSpec | spec: TimeSpec | QDateTime |
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):
| 成员 | 值 |
|---|---|
LocalTime | 0 |
UTC | 1 |
OffsetFromUTC | 2 |
TimeZone | 3 |
DateFormat(src/lib/QtEnums/DateFormat/index.ts):
| 成员 | 值 |
|---|---|
TextDate | 0 |
ISODate | 1 |
SystemLocaleDate | 2 |
LocaleDate | 3 |
SystemLocaleShortDate | 4 |
SystemLocaleLongDate | 5 |
DefaultLocaleShortDate | 6 |
DefaultLocaleLongDate | 7 |
RFC2822Date | 8 |
ISODateWithMs | 9 |
在 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 封装的跨层机制可以归纳为以下几点:
- 注册:
QDateTimeWrap::init用DefineClass一次性注册全部实例方法(InstanceMethod)与 6 个静态方法(StaticMethod),并挂接COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE宏提供组件通用能力; - 对象传递:返回
QDateTime/QDate/QTime的方法统一采用“堆分配拷贝 →Napi::External<T>::New包装 → 类构造器constructor.New生成新 JS 对象”的链路,每个 JS 对象独立持有一份 C++ 实例; - 入参解包:接收其他封装类的方法统一用
Napi::ObjectWrap<TWrap>::Unwrap取内部实例,因此参数必须是 NodeGui 的对应类型实例; - 枚举即整数:
TimeSpec/DateFormat在边界处是 int,C++ 侧负责static_cast回 Qt 枚举; - 命名规避冲突:与 JS 原型方法重名的绑定统一加
$后缀(如toString$),TS 层再以正常名称重新导出; - 参数个数严格校验:构造函数 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
相关推荐
为 AirSim 添加新 API:从实现、RPC 封装到测试的完整指南
为 AirSim 添加新 API:从实现、RPC 封装到测试的完整指南 导读 :AirSim 提供覆盖多旋翼、汽车、仿真环境与物理引擎的上百个 RPC API,
自动驾驶人工智能深度学习强化学习计算机视觉科研如何快速找回比特币钱包密码:面向新手的完整恢复指南
如何快速找回比特币钱包密码:面向新手的完整恢复指南 你是否还记得大部分比特币钱包密码或助记词,却因为一些拼写错误或记忆偏差而无法访问你的数字资产?btcreco
桌面应用跨平台Advanced-Deep-Learning-with-Keras项目架构:模块化设计与扩展性分析
Advanced Deep Learning with Keras项目架构:模块化设计与扩展性分析 Advanced Deep Learning with Ke
桌面应用跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考