编程与 AI12 分钟阅读更新于 2026-07-14

Java 时间戳怎么转日期:秒、毫秒和 LocalDateTime 用法

讲清 Java 中 10 位秒级时间戳、13 位毫秒时间戳、Instant、LocalDateTime、时区转换和接口日志排查方法,避免常见 1970 年和 8 小时误差问题。

相关工具

先分清秒和毫秒

Java 时间处理里最常见的问题,是把秒级时间戳和毫秒级时间戳混用。10 位时间戳通常是秒,比如 1720953600;13 位时间戳通常是毫秒,比如 1720953600000。接口、数据库、日志系统可能使用不同单位,字段名也不一定写清楚。

如果把毫秒当秒,日期会跑到非常遥远的未来;如果把秒当毫秒,日期会落到 1970 年附近。看到转换结果不对,先不要怀疑时区,先看时间戳单位。位数判断虽然不是绝对规则,但在排查时非常有用。

正式代码里最好不要靠猜。字段名可以明确写 createdAtMillis、expireAtSeconds,接口文档里也要说明单位。临时排查时,可以把原始值放到时间戳工具里转换,对照 Java 代码输出是否一致。

毫秒时间戳转日期

Java 8 以后推荐使用 java.time 包。毫秒时间戳可以先转成 Instant,再按时区转成 LocalDateTime:

java代码示例
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

long timestampMillis = 1720953600000L;
LocalDateTime dateTime = LocalDateTime.ofInstant(
    Instant.ofEpochMilli(timestampMillis),
    ZoneId.of("Asia/Shanghai")
);

String text = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(text);

Instant 表示时间线上的一个点,不带具体地区的日历含义。LocalDateTime 表示本地日期时间,不自带时区。ZoneId 决定用哪个时区把时间点解释成人能读的日期。北京时间通常使用 Asia/Shanghai。

不要随便使用系统默认时区,尤其是服务器可能部署在不同地区或容器环境里。代码里明确 ZoneId,能减少线上和本地结果不一致的问题。

秒级时间戳转日期

秒级时间戳用 Instant.ofEpochSecond:

java代码示例
long timestampSeconds = 1720953600L;
LocalDateTime dateTime = LocalDateTime.ofInstant(
    Instant.ofEpochSecond(timestampSeconds),
    ZoneId.of("Asia/Shanghai")
);
System.out.println(dateTime);

如果你不确定单位,可以写一个简单判断函数:

java代码示例
static Instant parseTimestamp(long value) {
    if (value > 10_000_000_000L) {
        return Instant.ofEpochMilli(value);
    }
    return Instant.ofEpochSecond(value);
}

这个函数适合调试和兼容历史数据,但正式业务最好不要长期依赖这种猜测。因为某些特殊时间范围或非 Unix 时间可能会误判。最好的方式仍然是让接口字段和文档明确单位。

日期字符串转时间戳

如果用户输入或配置文件里是日期字符串,可以先解析成 LocalDateTime,再结合时区转 Instant:

java代码示例
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse("2026-07-14 09:30:00", formatter);
long millis = dateTime.atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli();
long seconds = millis / 1000;

System.out.println(seconds);
System.out.println(millis);

LocalDateTime 本身没有时区,所以必须通过 atZone 指定时区后才能得到准确时间戳。如果你跳过时区概念,代码可能在本地和服务器上得到不同结果,尤其是系统默认时区不一致时。

解析字符串时,格式必须完全匹配。2026-07-14 09:30:00 用 yyyy-MM-dd HH:mm:ss;如果是 2026/07/14,就要换成 yyyy/MM/dd。格式不匹配会抛 DateTimeParseException。

8 小时误差和 1970 年问题怎么排查

如果转换结果差 8 小时,通常是 UTC 和北京时间混用。比如数据库存的是 UTC,页面按北京时间展示;或者接口返回 ISO 字符串带 Z,表示 UTC,但代码按本地时间解析。排查时要同时打印原始值、Instant、目标时区下的 LocalDateTime。

如果转换结果跑到 1970 年附近,通常是把秒当成毫秒处理了。比如 1720953600 作为毫秒转换,会接近 1970 年。反过来,如果结果跑到很远的未来,通常是把毫秒当秒处理。单位问题比时区问题更基础,要先排除。

定时任务、优惠券过期、订单关闭、Token 失效这类逻辑都容易踩时间坑。建议日志里保留原始时间戳和格式化后的时间,必要时也记录时区。只记录一个展示时间,后续排查会很被动。

接口字段设计建议

如果你能设计接口,建议时间字段保持一致。要么统一使用毫秒时间戳,要么统一使用 ISO 8601 字符串。不要同一个接口里 createdAt 是字符串,expireAt 是秒,updatedAt 又是毫秒。字段越混乱,客户端越容易写错。

字段名最好带语义,比如 createdAtMillis、expiredAt、createdAtText。对于给前端展示的场景,可以同时返回时间戳和可读字符串,但要说明哪个用于计算,哪个用于展示。计算逻辑最好基于时间戳或标准时间字符串,不要基于已经格式化的中文日期。

如果项目里既有老接口又有新接口,迁移时不要悄悄改变原字段含义。比如原来的 createdAt 是秒,新版本突然改成毫秒,会让老客户端全部出错。更稳的做法是新增字段,例如 createdAtMillis,同时保留旧字段一段时间,并在文档里写清楚废弃计划。

测试用例也要覆盖边界时间,比如当天零点、月底、年底、闰年、夏令时地区、过期时间刚好等于当前时间。很多时间问题平时看不出来,只有到活动结束、订单关闭或跨天统计时才暴露。

如果是数据报表,还要确认统计窗口。当天数据到底按自然日、业务日还是 UTC 日切分,会直接影响结果。很多看似时间转换的问题,本质是统计口径没有说清楚。

工具帮的时间戳工具适合在开发和测试时快速核对。Java 输出一个时间戳后,可以复制到工具里看结果;接口返回日期不对时,也可以先用工具判断是单位问题还是时区问题。

常见问题

Java 里 Instant 和 LocalDateTime 有什么区别?

Instant 是时间线上的时间点,LocalDateTime 是本地日期时间,不包含时区。两者转换时需要 ZoneId。

为什么时间差 8 小时?

通常是 UTC 和北京时间混用。检查解析和格式化时使用的时区。

13 位时间戳怎么转?

13 位通常是毫秒,用 Instant.ofEpochMilli;10 位通常是秒,用 Instant.ofEpochSecond。

Java 实用

继续阅读

返回专题