GetSystemDateAndTime 返回三个时间:UTC、本地、夏令时标志,只有 UTC 是全球一致的。
「录像时间对不上」这个经典故障,根源几乎都是「一边存 UTC、一边显示本地」。
前五节讲的是「怎么找到设备、它是什么」,这一节进入「怎么管它」。
设备管理是 device 这组 service 的地盘,
也是接一台陌生设备时问得最多的一组(07.01.03 那张表里它占 5 个操作)。
一、三个时间,一个可靠
| 字段 | 本例的值 | 说明 |
|---|---|---|
DateTimeType |
Manual |
手动设的;NTP 表示对过时 |
DaylightSavings |
false |
夏令时是否生效 |
UTCDateTime |
2026-10-24T09:47:26 |
UTC 时间,唯一可靠的 |
第三行是重点:UTCDateTime 是唯一「全球一致」的字段,
本地时间和夏令时都是「给人看的」。
所以代码里做时间比较,一律用这个字段,别碰本地时间。
第一行那个 DateTimeType 值得一看:
Manual 说明这台设备的时间是人手动设的——手动设的时间会漂,一个月能差几分钟。
NTP 才是对过时的。
所以「录像时间戳不准」有时不是时区,是设备压根没对时,
这两个原因的排查方法完全不同:一个改配置,一个查 NTP 服务器。
二、实测:设备报 9:47,本机报 17:47
Demo 把设备报的 UTC 和本机本地时间摆在一起,差 8 小时:
| 来源 | 时刻 | 时区 |
|---|---|---|
设备 UTCDateTime |
09:47:26 | UTC |
| 本机本地时间 | 17:47:26 | UTC+8 |
这个差不是「设备时间错了」,是时区—— 设备报的是 UTC,本机报的是本地时间,两者本来就该差 8 小时。 「录像时间对不上」的故障就是这么来的: NVR 按 UTC 时间戳存,回放界面按本地时间显示,中间差了几个小时,找录像就找不到。 所以排查这类问题的第一件事,是把两边都换成 UTC 再比。 这个「先把口径统一,再比数值」的习惯,比任何工具都管用。 我在这门课里已经踩过好几次口径的坑(04 章的时间基、05 章的存储容量), 每次都是「数没错,口径错了」。
三、夏令时什么时候必须管
| 场景 | 只看 UTC 够不够 | 要不要结合夏令时 |
|---|---|---|
| 比较两个时间戳的先后 | 够 | 不用管 |
| 按「本地 8 点」触发录像 | 不够 | 要算时区偏移 |
| 给用户显示「录于几点」 | 不够 | 要按用户所在时区换算 |
第二行的坑在于:「本地 8 点」在夏令时切换那天可能不存在,也可能出现两次。
严格的做法是三条:存 UTC、显示时换算、触发用 UTC 绝对时刻。
一旦把「本地时间」当存储格式,夏令时就一定会咬人。
国内不用夏令时,所以很多人觉得这条无所谓——但设备是卖给全世界的,
固件里那句 DaylightSavings 就是为出口准备的。
写客户端时如果只考虑国内,到了有夏令时的地区就会出错,而这种错一年只出现两次,最难复现。
四、设备管理这一组还有什么
device 这组 service 的操作不止时间一个,常用的还有这些:
| 操作 | 干什么 | 什么时候用 |
|---|---|---|
GetDeviceInformation |
厂商、型号、固件、序列号 | 接设备第一件事 |
GetSystemDateAndTime |
三个时间 | 算摘要、对时 |
GetNetworkInterfaces |
网口配置、IP、网关 | 查「为什么连不上」 |
SystemReboot |
重启设备 | 远程维护的最后手段 |
GetHostname |
设备主机名 | 多设备时区分 |
第一行那个信息在「报故障」时最有用:厂家第一句一定问「固件版本多少」,
没有它,后面所有沟通都要重来一遍。
所以接设备的第一步就该把它存下来,别等出问题再问。
第四行的 SystemReboot 要慎用:它没有「优雅关闭」的概念,重启期间录像会断。
工程上的做法是「先停录像任务、再重启、再恢复」,顺序错了就是一段丢失的录像。
五、动手 Demo:设备管理里的时间,为什么总是不对
Demo 问一次 GetSystemDateAndTime,把三个字段都打出来,
再和本机时间比一比,算出那个差。
最后把本机时间也换成 UTC,验证「换成同一口径后差为零」。
_a, b, _n = S.call(port, "device", "GetSystemDateAndTime",
"<tds:GetSystemDateAndTime/>")
r = ET.fromstring(b)
ty = r.find(".//{%s}DateTimeType" % S.NS_TT)
ds = r.find(".//{%s}DaylightSavings" % S.NS_TT)
ut = r.find(".//{%s}UTCDateTime" % S.NS_TT)
print(ty.text, ds.text)
# UTCDateTime 是复合结构,要拼出时分秒
h = ut.find("{%s}Hour" % S.NS_TT).text
mi = ut.find("{%s}Minute" % S.NS_TT).text
sc = ut.find("{%s}Second" % S.NS_TT).text
print("设备 UTC %s:%s:%s" % (h, mi, sc))
# 和本机比:先都换成 UTC
now_local = datetime.datetime.now()
now_utc = datetime.datetime.utcnow()
print("本机本地", now_local.strftime("%H:%M:%S"))
print("本机 UTC ", now_utc.strftime("%H:%M:%S"))
print("时区差 %.0f 小时" % ((now_local - now_utc).total_seconds() / 3600))
实跑输出:
--------------------------------------------------------------------
设备管理里的时间,为什么总是不对
--------------------------------------------------------------------
**`GetSystemDateAndTime` 是所有 ONVIF 操作里最容易踩时区坑的一个。**
它返回三个时间:**UTC 时间、本地时间、夏令时标志**。
**响应里的字段:**
字段 值 说明
----------------------------------------------------------------------------------------------
`DateTimeType` Manual **`Manual` 表示手动设的,`NTP` 表示对过时**
`DaylightSavings` false **夏令时是否生效**
`UTCDateTime` 2026-10-24 9:47:26 **UTC 时间,这是唯一可靠的**
**第三行是重点**: **`UTCDateTime` 是唯一「全球一致」的字段**,
**本地时间和夏令时都是「给人看的」。**
**所以代码里做时间比较,一律用这个字段,别碰本地时间。**
**实测这台设备和本机的时间差:**
**设备报的 UTC 时刻**:09:47:26
**本机本地时刻**:17:47:26
**两者差 +8 小时**(本机在 UTC+8)
**这个差不是「设备时间错了」,是时区**—— **设备报的是 UTC,本机报的是本地时间。**
**很多「录像时间对不上」的故障就是这么来的**:
**NVR 按 UTC 时间戳存,回放界面按本地时间显示, 中间差了几个小时,找录像就找不到。**
**所以排查这类问题时,第一件事是把两边都换成 UTC 再比。**
**夏令时那个字段为什么不能忽略:**
场景 只看 UTC 要结合夏令时
--------------------------------------------------------------------------------
比较两个时间戳的先后 **够** 不用管
按「本地 8 点」触发录像 不够 **要算时区偏移**
给用户显示「录于几点」 不够 **要按用户所在时区换算**
**第二行的坑在于**: **「本地 8 点」在夏令时切换那天可能不存在,也可能出现两次。**
**严格的做法是:存 UTC,显示时换算,触发用 UTC 绝对时刻。**
**一旦把「本地时间」当存储格式,夏令时就一定会咬人。**
**ONVIF 里还有个细节**: **`GetSystemDateAndTime` 允许匿名调用**(07.01.06 讲过),
**所以「先问时间再鉴权」是可行的**——
**这正是 WS-Security 摘要算法需要的**(它要算时间戳的摘要)。
UTCDateTime 不是一个字符串,是一个复合结构:
里面分 Hour/Minute/Second 三个子元素,所以要一个个取再拼。
这一点和很多协议不一样(它们直接给 ISO 8601 字符串)——ONVIF 这里用的是结构化的时间,
别想当然地把 .text 当整串用。
最后那个「时区差」是本机算的,不是设备给的:
datetime.now() 和 utcnow() 相减就是本机的时区偏移。
本例是 +8 小时,所以设备报的 09:47 和本机的 17:47 是「同一个时刻的两种写法」。
这个 Demo 里我一次都没碰「本地时间」这个字段,
只用了 UTC 和本机自己的时区——
这是刻意的:设备给的本地时间不可靠(取决于设备上设的时区),而 UTC 和本机时区都是确定的。
30 秒实验
- 把
utcnow()换成now(timezone.utc),看打印出来的时刻变不变。 - 算一下「设备时间和本机时间差几秒」——本例应该只差秒级,如果差很多,说明设备没对时。
- 把三个时间字段都打印出来(含
LocalDateTime),看它和 UTC 差多少。
常见误区
- 以为设备时间错了。 多半是时区:设备报 UTC,本机显示本地时间,差 8 小时是正常的。
- 以为
UTCDateTime是个字符串。 它是复合结构,要分别取Hour/Minute/Second。 - 以为国内不用夏令时就不用管它。 设备是出口的,
DaylightSavings那一行在别的地区会咬人,一年只错两次最难查。
自测三问(点开对答案)
1. `GetSystemDateAndTime` 返回哪三个时间相关的字段?哪个可靠? —— **`UTCDateTime`、本地时间、`DaylightSavings`。** **只有 UTC 是全球一致的**,比较时间一律用它。 2. 「录像时间对不上」最常见的原因是什么? —— **一边存 UTC、一边显示本地时间。** **排查第一步是把两边都换成 UTC 再比。** 3. `Manual` 和 `NTP` 的区别有什么实际影响? —— **`Manual` 是人手动设的,会漂**;`NTP` 是对过时的。 **前者要查配置,后者要查 NTP 服务器**,排查方向完全不同。下一节讲设备的输入输出:视频以外还有继电器和音频,而继电器在亮化工程里能直接控灯。
- ☐ (本节完,下一节 07.02.07 设备输入/输出)