java时间格式转换时时区相关的坑

先上结论:
1、yyyy-MM-dd'T'HH:mm:ss.SSSXXX的XXX表示时区,Z零时区,+08:00东八区(大致)
2、java 8,时间字符串不带时区,使用LocalDateTime,因为即使字符串附带了时区信息也不被使用;如果要使用字符串附带的时区信息则使用ZonedDateTime
3、使用DateTimeFormatter,毫秒都是右补零;SimpleDateFormat是左补零;解决方式使用三位毫秒格式,如:2023-02-13T00:00:00.12Z改为2023-02-13T00:00:00.120Z或2023-02-13T00:00:00.012Z再做数据类型转换

看代码和注释

package com.rootcloud;

import org.apache.commons.lang.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;

public class Main21DateTest {
    public static void main(String[] args) throws ParseException {
        //结论:java 8,时间字符串不带时区,使用LocalDateTime,因为即使字符串附带了时区信息也不被使用;如果要使用字符串附带的时区信息则使用ZonedDateTime
        //使用DateTimeFormatter,毫秒都是右补零;SimpleDateFormat是左补零;解决方式永远使用三位毫秒格式

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); //基准
        System.out.println("2023-02-13T00:00:00.120");
        System.out.println(sdf.parse("2023-02-13T00:00:00.120").getTime());

        System.out.println("DateTimeFormatter+LocalDateTime");
        //XXX代表时区
        final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
        //8SH 输入 东八区时间,选择上海时间所在时区,输出 0时区时间,时间戳和基准时间相同(毫秒自动右补零)
        final Instant instant8SH = LocalDateTime.parse("2023-02-13T00:00:00.12+08:00", dateTimeFormatter).atZone(ZoneId.of("Asia/Shanghai")).toInstant();
        System.out.println("8SH");
        System.out.println(instant8SH.toString());
        System.out.println(instant8SH.toEpochMilli());
        //8UTC与8SH相比,输入zoneId变成UTC,输出时间为 0时区2023-02-13T00:00:00.120Z,时间戳比8SH小8H,结论UTC会覆盖+08:00的效果
        final Instant instant8UTC = LocalDateTime.parse("2023-02-13T00:00:00.12+08:00", dateTimeFormatter).atZone(ZoneId.of("UTC")).toInstant();
        System.out.println("8UTC");
        System.out.println(instant8UTC.toString());
        System.out.println(instant8UTC.toEpochMilli());
        //8Default输出和8SH相同,猜测1:覆盖成Asia/Shanghai所在时区,猜测2:不覆盖,使用字符串的时区
        final Instant instant8Default = LocalDateTime.parse("2023-02-13T00:00:00.12+08:00", dateTimeFormatter).atZone(ZoneId.systemDefault()).toInstant();
        System.out.println("8Default");
        System.out.println(instant8Default.toString());
        System.out.println(instant8Default.toEpochMilli());
        //ZUTC 输入Z和UTC同为零时区,输出与基准差8H 符合预期
        final Instant instantZUTC = LocalDateTime.parse("2023-02-13T00:00:00.12Z", dateTimeFormatter).atZone(ZoneId.of("UTC")).toInstant();
        System.out.println("ZUTC");
        System.out.println(instantZUTC.toString());
        System.out.println(instantZUTC.toEpochMilli());
        //ZDefault 与 8Default输出相等,default
        final Instant instantZDefault = LocalDateTime.parse("2023-02-13T00:00:00.12Z", dateTimeFormatter).atZone(ZoneId.systemDefault()).toInstant();
        System.out.println("ZDefault");
        System.out.println(instantZDefault.toString());
        System.out.println(instantZDefault.toEpochMilli());
        //java.time.LocalDateTime.from 里看到有TemporalAccessor有多种实现,其中包括ZonedDateTime,自带时区
        final Instant instant = ZonedDateTime.parse("2023-02-13T00:00:00.12Z", dateTimeFormatter).toInstant();
        System.out.println("ZonedDateTime");
        System.out.println(instant.toString());
        System.out.println(instant.toEpochMilli());
//        报错,必须定义XXX
//        final DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
//        final Instant instant2 = ZonedDateTime.parse("2023-02-13T00:00:00.120", dateTimeFormatter2).toInstant();
//        System.out.println("ZonedDateTime");
//        System.out.println(instant2.toString());
//        System.out.println(instant2.toEpochMilli());

        System.out.println("\nSimpleDateFormat+Date");
        final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
        Date parse = simpleDateFormat.parse("2023-02-13T00:00:00.12Z");
        System.out.println(parse.getTime());
        parse = simpleDateFormat.parse("2023-02-13T00:00:00.12+08:00");
        System.out.println(parse.getTime());
    }

}



结果:

2023-02-13T00:00:00.120
1676217600120
DateTimeFormatter+LocalDateTime
8SH
2023-02-12T16:00:00.120Z
1676217600120
8UTC
2023-02-13T00:00:00.120Z
1676246400120
8Default
2023-02-12T16:00:00.120Z
1676217600120
ZUTC
2023-02-13T00:00:00.120Z
1676246400120
ZDefault
2023-02-12T16:00:00.120Z
1676217600120
ZonedDateTime
2023-02-13T00:00:00.120Z
1676246400120
ZonedDateTime
2023-02-13T00:00:00.120Z
1676246400120

SimpleDateFormat+Date
1676246400012
1676217600012