日期处理
日期处理是Java中处理日期、时间、时间戳及时区转换的重要功能。
时间戳(Timestamp)是一个表示特定时间点的数值,通常是从某个固定时间点(如1970年1月1日UTC)开始计算的毫秒数。
不得不说的时区
时区的概念
时区(Time Zone)是地球上按照经度划分的区域,每个区域采用统一的标准时间。全球共划分为24个时区,通常以格林威治标准时间(GMT/UTC)为基准。
计算机系统的时区常识
- 操作系统通常有默认时区(如中国大陆为 Asia/Shanghai,欧美常用 UTC 或本地时区)。
 - 程序运行时若未指定时区,往往采用系统默认时区,容易导致跨时区部署或数据交互时出现时间偏差。
 - Java 推荐使用 
ZoneId明确指定时区,避免隐式依赖。 - 常见时区标识如:
UTC、Asia/Shanghai、America/New_York、Asia/Tokyo。 
标准时间
- UTC(Coordinated Universal Time,协调世界时):全球通用的时间标准,替代了早期的 GMT。
 - GMT(Greenwich Mean Time,格林威治标准时间):历史上广泛使用,现多用 UTC 替代。
 - 本地时间(Local Time):指当前时区下的时间。
 - 夏令时(Daylight Saving Time, DST):部分国家/地区会在夏季调整时间,需注意时间换算。
 
例子
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.Month;
import java.time.Duration;
import java.time.Period;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.sql.Timestamp;
public class DateTimeDemo {
    public static void main(String[] args) {
        // 获取当前日期时间
        LocalDate today = LocalDate.now();
        LocalTime now = LocalTime.now();
        LocalDateTime current = LocalDateTime.now();
        System.out.println("今天: " + today);
        System.out.println("现在时间: " + now);
        System.out.println("当前日期时间: " + current);
        // 时间戳相关操作
        // 1. 获取当前时间戳
        long currentTimestamp = System.currentTimeMillis();
        System.out.println("当前时间戳(毫秒): " + currentTimestamp);
        // 2. 使用Instant处理时间戳
        Instant instant = Instant.now();
        System.out.println("当前Instant: " + instant);
        System.out.println("Instant转时间戳: " + instant.toEpochMilli());
        // 3. 时间戳转日期时间
        LocalDateTime fromTimestamp = LocalDateTime.ofInstant(
            Instant.ofEpochMilli(currentTimestamp), 
            ZoneId.systemDefault()
        );
        System.out.println("时间戳转日期时间: " + fromTimestamp);
        // 4. 使用SQL Timestamp
        Timestamp sqlTimestamp = new Timestamp(currentTimestamp);
        System.out.println("SQL Timestamp: " + sqlTimestamp);
        // 5. 时间戳与时区转换
        ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(
            Instant.ofEpochMilli(currentTimestamp),
            ZoneId.of("Asia/Shanghai")
        );
        System.out.println("上海时区时间: " + zonedDateTime);
        // 创建特定日期时间
        LocalDate birthday = LocalDate.of(1990, Month.MAY, 15);
        LocalTime meetingTime = LocalTime.of(14, 30);
        LocalDateTime event = LocalDateTime.of(2023, 12, 31, 23, 59, 59);
        System.out.println("生日: " + birthday);
        System.out.println("会议时间: " + meetingTime);
        System.out.println("事件时间: " + event);
        // 日期时间计算
        LocalDate nextWeek = today.plusWeeks(1);
        LocalTime afterTwoHours = now.plusHours(2);
        LocalDateTime nextMonth = current.plusMonths(1);
        System.out.println("下周今天: " + nextWeek);
        System.out.println("两小时后: " + afterTwoHours);
        System.out.println("下个月: " + nextMonth);
        // 时区处理
        ZonedDateTime shanghaiTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
        ZonedDateTime newYorkTime = shanghaiTime.withZoneSameInstant(ZoneId.of("America/New_York"));
        System.out.println("上海时间: " + shanghaiTime);
        System.out.println("纽约时间: " + newYorkTime);
        // 格式化
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String formatted = current.format(formatter);
        System.out.println("格式化后: " + formatted);
        // 解析
        LocalDateTime parsed = LocalDateTime.parse("2023-01-01 12:00:00", formatter);
        System.out.println("解析后: " + parsed);
        // 与传统API互转
        Date oldDate = new Date();
        Instant instant2 = oldDate.toInstant();
        LocalDateTime newDateTime = LocalDateTime.ofInstant(instant2, ZoneId.systemDefault());
        System.out.println("转换后: " + newDateTime);
        // 时间差计算
        LocalDateTime start = LocalDateTime.of(2023, 1, 1, 0, 0);
        LocalDateTime end = LocalDateTime.of(2023, 1, 2, 12, 30);
        Duration duration = Duration.between(start, end);
        System.out.println("相差小时: " + duration.toHours());
        Period period = Period.between(LocalDate.of(2022, 1, 1), LocalDate.of(2023, 1, 1));
        System.out.println("相差年月: " + period.getYears() + "年" + period.getMonths() + "月");
        // 日期时间比较
        System.out.println("是否在之前: " + start.isBefore(end));
        System.out.println("是否在之后: " + end.isAfter(start));
        System.out.println("是否相等: " + start.isEqual(end));
    }
}
时间戳使用注意事项
- 
时间戳精度
- Java中的时间戳精度为毫秒级
 - 如果需要更高精度,可以使用
Instant类的getNano()方法获取纳秒部分 
 - 
时区转换
- 时间戳本身是UTC时间,不包含时区信息
 - 转换为本地时间时,需要明确指定时区
 - 建议使用
ZoneId.systemDefault()获取系统默认时区 
 - 
数据库存储
- 使用
java.sql.Timestamp存储数据库时间戳 - 注意数据库时区设置与应用程序时区的一致性
 
 - 使用
 - 
跨系统传输
- 时间戳是跨系统传输时间的最佳选择
 - 建议使用毫秒级时间戳,避免精度损失
 - 接收方需要知道时间戳的基准时间(通常是1970-01-01 UTC)
 
 
提醒
假如你做了一个国际支付业务,涉及实时汇率,请记住:
- 所有汇率计算必须基于 UTC 时间
 - 不同时区的用户看到同一笔交易的时间可能不同
 - 跨日交易时,汇率可能发生变化,需要特别注意 UTC 时间点
 - 时间戳是记录交易时间的最佳选择,因为它不依赖于时区