· engineering· 约 20 分钟精读
端侧离线台账的设备时间可信性工程:墙钟与单调时钟分离、改时留痕与法定到期判定的能力边界
端侧离线台账没有可信的服务器授时,"现在几点"本身就是一条需要被质证的假设。本文以密旋科技 TagWorks 与 SlingGuard 的离线登记册为样本,区分墙钟、单调时钟与系统运行时长三种时间源各自能担保什么,梳理手动改时、自动校时跳跃、时区夏令时、跨零点边界与备份恢复时间倒退五类失效方式,并给出把判定函数纯函数化、UTC 存储与固定时区渲染、单调锚与时间纪律状态机、恢复后对账闸门四条工程线,最后如实划出"能检测与留痕、不能阻止"的能力边界。
一、从十的负十九次方,到随手回拨的二十分钟
2026年9月下旬,新加坡国立大学的研究团队公布了一台以镥离子为基准的光学原子钟:研究人员把跃迁频率测到小数点后十九位,不确定度约 ,相当于运行两千六百亿年误差不到一秒;团队还造出两台样钟,做了长达两百小时的互比[9]。这是人类时间计量能力的一次推进。
把这条消息和一台地下变电所里的手机放在一起,落差有些刺眼。那台手机没有稳定的授时来源,蜂窝信号时有时无,系统时间可能因为自动校时而向前跳,也可能被操作者在”日期与时间”里向后拨二十分钟——二十分钟足够把一台本该逾期的电动工具改回正常。对一本法定电气安全登记册来说,这二十分钟就是合规判定本身。
密旋科技的 TagWorks 与 SlingGuard 都把台账放在设备本地:没有账户、没有服务器、没有遥测。这带来一个必须正面回答的问题:当”现在几点”这个前提本身不可信时,所有基于日期的到期判定、审计时间戳与记录顺序,还剩下多少确定性?
本文不讨论如何把原子钟塞进手机,而是讨论一个更朴素、也更容易被跳过的工程命题:在没有可信外部授时的端侧环境里,如何约束、检测并如实记录时间的不确定性,而不是假装它不存在。
二、三个时间源,各自能担保什么
端侧设备上能拿到的”时间”不是一种,而是三种来源不同的量,工程上必须分开对待。这本台账的到期字段,直接派生自自动化设备安全检测与起重作业法规中规定的周期[1][2][3],因此时间的可信度直接等同于判定的可信度。
墙钟。 Swift 里 Date() 读到的是系统墙钟:它记录”自某个历元起经过的秒数”,会被系统校时、时区设置与用户手动调整改变。它对人和标准友好——能直接与法规里的月份、日历对应,也能写进 PDF 报告;但它不保证只增不减,可能向前跳,也可能向后跳。
单调时钟。 在 Darwin 平台上,底层是 mach_continuous_time 与 mach_absolute_time 两个计数;Swift 标准库把它们暴露为 ContinuousClock 与 SuspendingClock,前者在设备休眠时继续走,后者在休眠时暂停[4][5]。它们的共同点是只增不减、不接受外部设置,因此适合度量”经过了多少时间”;它们的显著局限是每次重启都会归零,而且两台设备之间的计数没有任何可比性。
系统运行时长。 例如 ProcessInfo.systemUptime,读的是设备”醒着”的时长,语义接近 SuspendingClock,同样受重启影响[11]。
还有一种常被误当成时间源的东西:网络授时。NTP 与 PTP 能提供跨设备一致的时间基准[7][10],但在地下室、粉尘车间、远洋货舱与矿井里,这条通道通常不存在——这正是 TagWorks 与 SlingGuard 的目标工况。因此端侧软件不能把”能连上网校时”当成设计前提。
| 时间源 | 能否被改动 | 跨重启 | 跨设备可比 | 适用场景 |
|---|---|---|---|---|
| 墙钟 Date() | 可以(校时、时区、手动拨动) | 保持,带时区语义 | 可以,同一时基 | 人可读日期、报告落款、法定到期判定 |
| ContinuousClock | 不可以 | 归零 | 不可以 | 度量真实经过时长(含休眠) |
| SuspendingClock | 不可以 | 归零 | 不可以 | 度量应用实际活跃时长 |
| 网络授时 NTP/PTP | 不可以,但依赖可达性 | 不适用 | 可以 | 有网环境的跨设备同步 |
这张表给出的结论很直接:没有一个时间源同时具备”不可改、跨重启稳定、跨设备可比”三项性质。端侧工程要做的,不是寻找那个不存在的理想时钟,而是设计一套能在三个各有限制的时间源之间互相验证、并把无法验证的部分如实标注出来的机制。
三、时间会以哪五种方式出错
(一)手动改时消解逾期
最直接的一种。操作者在系统设置里把日期往后拨,逾期设备立刻显示为正常。设备本身通常不阻止这件事:iOS 上只有受监管设备才能由 MDM 下发限制载荷,把自动日期与时间锁定为不可关闭[6];普通个人设备没有这道闸门。端侧应用既没有权限阻止,也不应该假装自己阻止了。
(二)自动校时的向前跳跃
设备一旦短暂接入蜂窝或无线网络,系统可能一次性把时间校正到网络时间,形成一次跳变。这次跳变本身是好事,它让后续的绝对时间更准;但它会打断任何”用墙钟差值度量经过时间”的假设:一次跨越两小时的跳变,会让”检测耗时”这类派生量得到荒谬的数值。
(三)时区与夏令时
判定逻辑若使用设备默认日历,同一份台账在不同设备、不同系统设置下可能算出不同的到期日:设备时区不同、夏令时切换日的”这一天”长度不同、按月份相加在月末(例如 1 月 31 日加一个月)的落点依赖具体日历实现。对本就跨州作业、跨时区携带的电气与吊装台账,这是一条真实的偏差来源。
(四)跨零点与跨月边界
“到期”是日期级判断,而设备提供的是时刻级数值。当天 23:59 与次日 00:01 的记录,其日期归属取决于所在时区;跨月边界又直接改变按月相加的输入。边界处理不当,会出现”同一条记录在两次导出里归属不同月份”这种难以复现的差异。
(五)备份恢复后的时间倒退
这是离线台账独有的一种,也最容易被忽略。TagWorks 允许把整本登记册备份成单文件档案,并在任意设备上恢复。从一份旧备份恢复后,设备墙钟本身是准的,但档案里最新一条记录的时间戳可能早于设备当前时间数周。此时”记录顺序”与”时间戳顺序”出现矛盾;若不加处理,新的记录会被追加到一个逻辑上更早的时间点之上。
四、四条工程线
(一)把判定函数做成纯函数,时间一律注入
第一种做法几乎不花成本,却能消除一大类问题:凡是依赖”现在”的判定,都不许在函数体内部直接取时间,而必须由调用方作为参数传入。TagWorks 的核心策略正是如此——间隔判定函数的签名把当前时刻显式列在参数表里:
static func status(for asset: Asset, now: Date = Date()) -> TestIntervalStatus参数默认值只服务于调用便利,测试与回归可以传入任意时刻,从而把”给定某本台账、某个时刻,应当得到哪个状态”变成可复现的断言。同一组输入在任意设备、任意时区设置下都必须得到相同结果——这条要求一旦写进测试,就会自动逼出下一条工程线。
(二)时区纪律:UTC 存储,固定时区渲染
第二条线是把”存储时基”与”显示时基”分开。记录里的时刻统一按 UTC 落盘,遵循互联网时间戳的通行格式约定[8];到期计算使用一个显式的、与作业现场法定辖区绑定的时区,而不是设备当前的日历设置。渲染成人可读日期时,再转换到目标时区。
收益是确定的:同一份台账不会因为检查员换了一台设备、或设备自动改了时区,就得出不同的到期结论。日期级判定对应的是一个被明确写下来的时区,而不是随环境漂移的隐式默认值。代价是产品层面必须对”这本台账适用哪个法定时区”给出明确设定,不能留给系统猜测。
(三)单调锚:给每条记录系上一根不回头的绳
第三条线是本文的核心,也是端侧时间可信性最实际的抓手。做法是:每写入一条记录,除了墙钟时间戳之外,同时写入一个单调计数及其来源标记(例如基于 ContinuousClock 或系统运行时长)。单调计数不参与人可读显示,只作为顺序证据随记录进入审计链。
它的作用不是让时间变准,而是让墙钟的自相矛盾变得可检测。给定同一台设备上的前一条记录 与当前记录 ,检查两条不变量:
两条同时成立时记正常;墙钟回退而单调计数仍递增,说明有人在两次记录之间把系统时间向后拨了——这正是”改时消解逾期”的指纹;墙钟增量远大于单调增量,则提示发生过一次校时跳跃。
这里必须说清一个边界:单调计数在每次重启后归零,因此它不能跨重启直接比较。工程上通常需要维护一个启动会话号,只有同一会话内的两条记录才用单调计数直接对比;跨会话的记录退回到墙钟不变量,并显式标注”缺少单调锚”。
(四)时间纪律状态机:把不确定性变成可见状态
把上面几条检查组合起来,得到一个与业务状态(正常、逾期、不合格)正交的时间纪律状态机。它不参与合规判定,只回答”这本台账的时间证据是否可信”。
| 状态 | 触发条件 | 系统动作 |
|---|---|---|
| 正常 | 墙钟不回退、单调锚递增,且同处一个启动会话 | 照常记录,不打扰用户 |
| 前跳 | 墙钟增量显著大于单调增量 | 记录事件,标注疑似自动校时,不影响判定 |
| 回拨 | 墙钟回退但单调计数递增 | 记录事件,导出报告附时间可信性声明 |
| 无锚 | 跨重启,缺少可比的单调锚 | 仅用墙钟不变量检查,并如实标注缺失 |
| 未校准 | 设备从未获得过可信外部授时 | 提示绝对时间不可外部核验,不做否定结论 |
这张表的取向与 TagWorks 其余部分一致:宁可如实说不知道,也不给出一个看起来确定、实际没有依据的结论。时间纪律状态不阻断作业——现场电工不会因为设备时间存疑就停止检测——但它会随记录一路进入导出,让下游看到这本台账的时间证据到底有多硬。
(五)恢复后的时间对账闸门
针对第三节第五种威胁,恢复流程需要一个额外的人工确认点:当恢复进来的档案中最新记录时间显著早于设备当前时间时,界面应当明确提示”档案时间与设备时间存在差异”,并让操作者确认以设备当前时间继续、还是保留档案时间语义。这条确认本身作为一条记录写入,从而保证即使这个差异后来造成困惑,台账里也留下了它被知情的证据。
(六)唯一的”阻止”手段:受管设备的强制自动校时
必须承认:在个人设备上,应用无法阻止用户改系统时间。真正能阻止的只有一种情形——设备处于受监管模式,管理员通过 MDM 下发限制载荷,把强制自动日期与时间置为真,此时自动校准被强制开启且用户无法关闭[6]。这是组织级部署才具备的能力,个人版产品不该宣称它做得到。
五、这套机制能证明什么,不能证明什么
把边界写下来,比把能力写大有用:
- 能做的:检测同一设备上墙钟的自相矛盾;把”发生过改时或校时”作为一条难以抹去的事件留在审计链里;在跨时区、跨设备的场景下让日期级判定保持确定。
- 不能做的:阻止用户改系统时间;在缺少可信外部授时的情况下,单独证明某次检测在真实世界的哪个时刻发生;用单调计数跨设备或跨重启比较顺序。
- 不该做的:把墙钟时间戳当作不可质疑的事实呈现给第三方;因为时间存疑就拒绝记录一条真实的检测。
对一台个人设备上的合规台账,最诚实的一句话是:时间戳是这台设备的自述,而不是一个独立第三方的证词。端侧工程的职责,是让这句自述尽可能自洽、让不自洽之处尽可能早地暴露,并把”自述”这个属性本身写进交付物。
六、结语
原子钟的故事讲的是如何把时间测得更准;端侧离线台账的故事讲的是,当一个系统拿不到可信时间时,如何仍然保持诚实。密旋科技在 TagWorks 与 SlingGuard 上的选择很朴素:不假装端侧设备拥有它没有的授时能力,而是把墙钟与单调时钟分开、把判定函数纯函数化、把时间纪律作为一等状态暴露出来,并把每一种不确定如实写进台账与导出的报告。
在数据不出设备的前提下,时间也同样不出设备。既然它出不去,就要把它的可信区间,一并在设备内部界定清楚。
参考文献
[1] Standards Australia. AS/NZS 3760:2022 In-service safety inspection and testing of electrical equipment. https://www.standards.org.au/standards-catalogue/sa-snz/electrotechnology/el-036/as-nz-s-3760-2022 [2] UK Health and Safety Executive. Lifting Operations and Lifting Equipment Regulations 1998 (LOLER). https://www.hse.gov.uk/work-equipment-machinery/loler.htm [3] US Occupational Safety and Health Administration. 29 CFR 1910.184 Slings. https://www.osha.gov/laws-regs/regulations/standardnumber/1910/1910.184 [4] Apple Developer Documentation. ContinuousClock. https://developer.apple.com/documentation/swift/continuousclock [5] Apple Developer Documentation. SuspendingClock. https://developer.apple.com/documentation/swift/suspendingclock [6] Apple Developer Documentation. Device Management Restrictions: forceAutomaticDateAndTime. https://developer.apple.com/documentation/devicemanagement/restrictions [7] IETF RFC 5905. Network Time Protocol Version 4: Protocol and Algorithms Specification. https://www.rfc-editor.org/rfc/rfc5905 [8] IETF RFC 3339. Date and Time on the Internet: Timestamps. https://www.rfc-editor.org/rfc/rfc3339 [9] Solidot. 科学家研制出至今最精确的原子钟. https://www.solidot.org/story?sid=85485 [10] IEEE 1588-2019. Standard for a Precision Clock Synchronization Protocol for Networked Measurement and Control Systems. https://standards.ieee.org/ieee/1588/4355/ [11] Apple Developer Documentation. ProcessInfo.systemUptime. https://developer.apple.com/documentation/foundation/processinfo/uptime