system.currenttimemillis() 返回自1970-01-01 00:00:00 utc起的毫秒数,是纯utc时间戳,不依赖时区;其数值恒定,格式化或计算零点时间时才需显式处理时区偏移。

System.currentTimeMillis() 返回的是自 1970-01-01 00:00:00 UTC(即 Unix 纪元)起经过的毫秒数,它本身不依赖也不携带时区信息,是一个纯 UTC 时间戳。所谓“与时区偏移有关”,其实是后续格式化或计算时引入的误解——时间戳数值恒定,但用它生成本地时间、零点时间、或参与跨时区业务逻辑时,必须显式处理时区偏移。
理解 currentTimeMillis 的本质
该方法只读取操作系统底层的挂钟(如 Linux 的 CLOCK_REALTIME),返回一个绝对时间点的毫秒值。无论服务器设为 Asia/Shanghai 还是 Etc/UTC,System.currentTimeMillis() 的数值都代表同一时刻的 UTC 毫秒数。你看到的“不同”只是因 LocalDateTime.now() 或 SimpleDateFormat 等工具默认使用系统时区解释该时间戳所致。
- 它不是“本地时间毫秒数”,而是“UTC 时间毫秒数”
- 打印日志、生成 JWT、比对超时等场景,应直接使用该值,无需加减偏移
- 只有在转换为可读的年月日时分秒(例如显示给用户)时,才需指定时区
正确获取指定时区的当天零点时间戳
常见错误写法:currentTimeMillis() - (currentTimeMillis() % 86400000) ——这算出的是系统默认时区的零点,不是目标时区的零点,尤其在 Android 上易得“早上 8 点”。
- 先用
ZonedDateTime构造目标时区的当日零点(如 Asia/Shanghai) - 再转为 Instant,最后调用
toEpochMilli() - 示例代码:
ZoneId shanghai = ZoneId.of("Asia/Shanghai"); ZonedDateTime todayStart = LocalDate.now(shanghai).atStartOfDay(shanghai); long shanghaiMidnight = todayStart.toInstant().toEpochMilli();
动态计算并应用时区偏移(仅当必须手动调整时)
若受限于旧代码无法用 java.time,需手动加减偏移,务必注意:偏移量随夏令时变化,不能硬编码。
- 获取当前偏移(毫秒):
TimeZone.getTimeZone("Asia/Shanghai").getOffset(System.currentTimeMillis()) - 将 UTC 时间戳转为目标时区毫秒值:
utcMillis + offset(注意:这是用于构造该时区下的“本地毫秒数”,仅作展示或兼容逻辑,非标准做法) - 反向转换(已知某时区本地时间,求其对应 UTC 时间戳):
localMillis - offset
微服务与移动端中规避时区误用的关键实践
分布式系统中,时间戳统一用 UTC 存储和传输;展示层再按用户所在时区渲染。避免在中间环节做“本地化时间戳”转换。
- 日志打点、数据库写入、消息体中的时间字段,一律使用
System.currentTimeMillis()原值 - 前端接收毫秒数后,由浏览器或客户端 SDK 根据本地时区自动格式化(JavaScript
new Date(ms)默认用本地时区) - Android 应用若需对抗用户手动改时,应结合
SystemClock.elapsedRealtime()与可信服务同步结果,而非依赖currentTimeMillis()的绝对值











