本文用 app_install_date 0.1.5 在 Flutter 鸿蒙应用中读取系统记录的安装时间,展示本地时间和 epoch 毫秒,并说明覆盖安装、时区转换及异常状态的处理边界。
三方库仓库: https://atomgit.com/oh-flutter/app_install_date
本文锁定版本:
3fb5bd5e568784783be69a46adbfc2b7c2185646完整 Demo: app_install_date/example
一、最终真机效果

图 1:CHZ-AL00 / HarmonyOS 7.0.0.105(SP10) 上展示安装本地时间和 epoch 毫秒。
受测应用两轮、每轮连续读取 5 次,均得到 1788703057807 毫秒,对应本地时间 2026-09-06 21:57:37.807 +08:00。公开 API、宿主直接调用 bundleManager 和外部 bm dump 三方结果一致。
| 验证点 | 实测结果 |
|---|---|
installDate |
返回 DateTime |
| epoch 精度 | 保留系统毫秒值 |
| 重复读取 | 两轮共 10 次一致 |
| 依赖与构建 | 固定远程 SHA,签名/无签名 HAP 均完成 |
| 自动化 | 25 项 Dart/Widget/ArkTS 功能测试通过 |
二、安装时间能解决什么问题
它可以辅助新手引导、安装后权益、迁移提示和客服诊断。例如产品只想在安装后的前三天展示一次入口,可以将当前时间与安装时间比较。但这个值不应作为安全身份、设备唯一标识或不可篡改凭证。
OHOS 端读取的是当前宿主的 BundleInfo.installTime,不是目录创建时间,也不是应用第一次启动时自己写入的缓存。卸载后重装通常会形成新的安装记录;覆盖升级是否保持原值则取决于系统安装语义。本文验证期间只做覆盖安装,没有专门完成完整升级矩阵,因此业务若依赖"首次安装且升级永不变化",还要针对发布链路单独测试。
三、环境和依赖
| 组件 | 实测版本 |
|---|---|
| Flutter OH | 3.41.10-ohos-1.0.1 |
| Dart | 3.11.5 |
| DevEco Studio | 26.0.0 Release |
| HarmonyOS SDK | API 26,示例兼容 API 18 |
| 测试设备 | CHZ-AL00 / HarmonyOS 7.0.0.105(SP10C00E105R2P4) |
| app_install_date | 0.1.5 / 上述受测提交 |
预览版 3.44.9+ohos-0.0.1-canary1 未用于本文回归。当前没有 OHOS 稳定 TAG:
yaml
dependencies:
app_install_date:
git:
url: https://atomgit.com/oh-flutter/app_install_date.git
ref: 3fb5bd5e568784783be69a46adbfc2b7c2185646
shell
flutter pub get
在 pubspec.lock 确认 resolved-ref。读取自身 BundleInfo 不需要新增权限。

图 2:AtomGit 仓库、适配分支和受测提交。
四、核心 API 和时间语义
dart
import 'package:app_install_date/app_install_date.dart';
final DateTime installedAt = await AppInstallDate().installDate;
final int epochMilliseconds = installedAt.millisecondsSinceEpoch;
final DateTime localTime = installedAt.toLocal();
final DateTime utcTime = installedAt.toUtc();
epoch 毫秒表示绝对时刻,不带"东八区数值"。Dart 的 toLocal() 和 toUtc() 只改变展示时区,不应在 ArkTS 或 Dart 中手工加减 8 小时。与服务端比较时优先传 epoch 或 ISO 8601 UTC 字符串;本地格式化文本只用于界面。
五、带重试的页面示例
dart
import 'package:app_install_date/app_install_date.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class InstallDatePage extends StatefulWidget {
const InstallDatePage({super.key});
@override
State<InstallDatePage> createState() => _InstallDatePageState();
}
class _InstallDatePageState extends State<InstallDatePage> {
DateTime? _installedAt;
String? _error;
bool _loading = false;
@override
void initState() {
super.initState();
_refresh();
}
Future<void> _refresh() async {
if (_loading) return;
setState(() {
_loading = true;
_error = null;
});
try {
final value = await AppInstallDate().installDate;
if (mounted) setState(() => _installedAt = value);
} on PlatformException catch (error) {
if (mounted) {
setState(() => _error = '${error.code}: ${error.message ?? '读取失败'}');
}
} catch (error) {
if (mounted) setState(() => _error = '$error');
} finally {
if (mounted) setState(() => _loading = false);
}
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('安装时间'),
actions: [
IconButton(
tooltip: '刷新',
onPressed: _loading ? null : _refresh,
icon: const Icon(Icons.refresh),
),
],
),
body: ListView(
padding: const EdgeInsets.all(24),
children: [
SelectableText(
_installedAt?.toLocal().toString() ?? '暂不可用',
),
const SizedBox(height: 8),
Text(
'Epoch milliseconds: '
'${_installedAt?.millisecondsSinceEpoch ?? '-'}',
),
if (_loading) const LinearProgressIndicator(),
if (_error != null) Text('读取失败:$_error'),
],
),
);
}
这个值通常不需要高频刷新。页面初始化读取一次即可,诊断页可以提供手动刷新。平台返回异常时保留错误状态,不要退回当前时间;把"现在"当作安装时间会让新手权益和统计悄悄出错。

图 3:OHOS 端读取并校验 BundleInfo.installTime 的毫秒值。
六、业务中的时间差判断
dart
bool installedWithin(DateTime installedAt, Duration window) {
final elapsed = DateTime.now().difference(installedAt);
return !elapsed.isNegative && elapsed <= window;
}
系统时钟可能被用户调整,极端情况下 elapsed 会变成负数。示例明确拒绝负时间差;涉及奖励或风控时,应由服务端时间和账号状态决定,不能只信任客户端时钟与安装元数据。
七、测试、构建与真机对照
shell
flutter analyze --no-fatal-infos
flutter test
node --test ohos/test/app_install_date.test.cjs
cd example
flutter test
flutter build hap --debug --no-codesign

图 4:25 项功能测试通过;保留上游 1 条 analyze info。

图 5:HAP 构建结果和远程依赖 resolved-ref。

图 6:公开 API、独立 bundleManager 与 bm dump 的 epoch 毫秒一致。
应用内重复读取

图 7:首次读取完整展示本地时间、ISO 8601、epoch 毫秒和时区偏移。

图 8:刷新到第 2 次读取后,安装 epoch 保持一致,本次读取时间更新。
八、常见问题
Q1:显示时间为什么和 epoch 转换工具差八小时
通常是工具按 UTC 展示,而页面按本地时区展示。比较两边的 epoch 毫秒,且不要手工加减时区。
Q2:为什么不用应用目录创建时间
目录可能被迁移、恢复或重新创建,语义不稳定。OHOS 受测实现直接读取系统 bundle 安装元数据。
Q3:覆盖升级后值一定不变吗
本文没有覆盖所有升级方式和系统版本。业务依赖这个性质时,必须使用真实发布包完成升级矩阵。
九、总结
app_install_date 为 Flutter 鸿蒙应用提供了直接的 DateTime 安装时间。可靠使用要锁定受测提交、按 epoch 绝对时刻处理时区、失败时不伪造当前时间,并把它限制在引导和诊断等辅助场景。本文已完成两轮重复读取及三方系统对照。
十、参考链接
欢迎加入CPF-Flutter 鸿蒙社区:https://atomgit.com/CPF-Flutter