1. 方法定义

  • DateFormat.setTimeZone(TimeZone zone)
    设置日期格式化器的时区(非 Date 类方法)

    // 方法签名
    public void setTimeZone(TimeZone zone)
    
  • DateFormat.setLocale(Locale locale) 的替代方案
    Java 原生 DateFormat 无直接 setLocale 方法,需通过创建新实例实现:

    // 通过构造器设置 Locale
    DateFormat formatter = DateFormat.getDateTimeInstance(style, style, locale);
    

2. 功能说明

方法/操作 作用 关键点
setTimeZone() 控制日期格式化的时区显示 不改变 Date 对象的 UTC 时间戳
构造器设置 Locale 定义日期格式、语言和地域规则 影响月份/星期名称和日期顺序

3. 示例代码

传统 API (java.text.DateFormat)
import java.text.DateFormat;
import java.util.*;

public class TraditionalExample {
    public static void main(String[] args) {
        Date now = new Date(); // UTC 时间戳
        
        // 设置时区:纽约时区
        DateFormat formatter = DateFormat.getDateTimeInstance();
        formatter.setTimeZone(TimeZone.getTimeZone("America/New_York"));
        System.out.println("NY Time: " + formatter.format(now)); 
        // 输出:August 16, 2025 1:30:45 AM EDT
        
        // 设置区域:中国格式
        DateFormat chinaFormatter = DateFormat.getDateTimeInstance(
            DateFormat.FULL, 
            DateFormat.FULL, 
            Locale.CHINA
        );
        chinaFormatter.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        System.out.println("上海时间: " + chinaFormatter.format(now));
        // 输出:2025年8月16日 星期六 中国标准时间 13:30:45
    }
}
现代 API (java.time)
import java.time.*;
import java.time.format.*;

public class ModernExample {
    public static void main(String[] args) {
        ZonedDateTime now = ZonedDateTime.now(ZoneId.of("UTC"));
        
        // 设置时区+区域:日本格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss zzzz", Locale.JAPAN)
                                                      .withZone(ZoneId.of("Asia/Tokyo"));
        System.out.println("東京時間: " + formatter.format(now)); 
        // 输出:2025-08-16 22:30:45 日本標準時
        
        // 本地化长格式
        DateTimeFormatter localizedFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL)
                                                                .withLocale(Locale.FRANCE)
                                                                .withZone(ZoneId.of("Europe/Paris"));
        System.out.println("Heure Paris: " + localizedFormatter.format(now));
        // 输出:samedi 16 août 2025 à 15:30:45 heure d’été d’Europe centrale
    }
}

4. 使用技巧

  1. 时区选择原则

    • 优先使用 IANA 时区 ID(如 Asia/Shanghai),避免 GMT+8 等缩写
    • 关键操作前显式设置时区:formatter.setTimeZone(...)
  2. 区域设置技巧

    • 使用标准区域标识:Locale.CHINA 而非 new Locale("zh", "CN")
    • 缓存格式化对象提升性能(尤其高并发场景)

5. 常见错误

错误类型 后果 解决方案
误认为 Date 有时区信息 时区转换错误 理解 Date 只是 UTC 时间戳
混淆 Locale 和时区 格式正确但时间值错误 分开处理:时区控制时间值,区域控制显示
使用已废弃的 Date 构造函数 时区偏移计算错误 改用 java.time.ZonedDateTime
线程共享 SimpleDateFormat 并发格式化结果错乱 ThreadLocalDateTimeFormatter

6. 注意事项

  1. 不可变性

    • java.time 对象不可变:formatter.withZone() 返回新对象
    • SimpleDateFormat 可变:操作后状态变化
  2. 夏令时处理

    // 错误:手动计算偏移
    TimeZone.getTimeZone("GMT-5"); // 忽略夏令时
    
    // 正确:使用完整时区ID
    ZoneId.of("America/New_York"); // 自动处理夏令时跳变
    
  3. 区域兼容性

    • 某些区域可能缺失翻译:Locale.TAIWANnew Locale("zh", "TW") 更可靠
    • 测试罕见区域:如 Locale.forLanguageTag("ar-EG")

7. 最佳实践与性能优化

  1. 存储与计算分离

    // 数据库存储 UTC 时间
    Instant dbTime = Instant.now(); 
    
    // 显示时按需转换
    ZonedDateTime userTime = dbTime.atZone(userZoneId);
    
  2. 高性能格式化

    // 缓存 DateTimeFormatter (线程安全)
    private static final DateTimeFormatter CACHED_FORMATTER = 
        DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.systemDefault());
    
    // 避免重复创建 SimpleDateFormat
    private static final ThreadLocal<DateFormat> threadLocalFormatter = 
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
    
  3. 时区传递策略

    • 前端传递时区 ID:Intl.DateTimeFormat().resolvedOptions().timeZone
    • REST API 使用标准头:Accept-Language: zh-CN + Time-Zone: Asia/Shanghai

总结

关键点 行动指南
时区处理 ZoneId 替代 TimeZone,存储用 Instant/ZonedDateTime
区域设置 构造 DateTimeFormatter 时指定 Locale,避免硬编码格式
API 选择 弃用 Date/SimpleDateFormat,全面转向 java.time (Java 8+)
性能优化 缓存线程安全对象(DateTimeFormatter),或用 ThreadLocal 包装非线程安全对象
错误预防 永远显式设置时区,测试 DST 边界用例