关于时区:在Java中将字符串转换为Timestamp时添加夏令时

Daylight savings time being added when string is converted to Timestamp in Java

我的应用程序以字符串格式接收时间戳,我们在SimpleDateFormat类中使用parse()方法将该String转换为Timestamp对象。我的应用程序在America / New_York时区运行,因此我们在3月9日凌晨2点到2点29分之间遇到时间戳的夏令时问题。我们通过修改默认时区属性来修复此问题,以确保将所有夏令时字段重置为零。但是当我们在夏令时更改(3月22日)之后使用java.util.Date类创建当前时间戳对象时,它显示为比实际当前时间戳小1小时。这是因为更改了默认时区的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class DateTest {
    static TimeZone defaultTimeZone = TimeZone.getDefault();
    Timestamp timestamp1;

    public static void main(String[] args) throws Exception {
        DateTest dateTest = new DateTest();
        System.out.println("Current date before changing Timezone:" + new java.util.Date());
        //Adds one hour extra to the actual timestamp
        System.out.println("Converted Timestamp with Daylight:" + convertStringToTimestamp("2014030902101900"));
        System.out.println("=====================================================================");
        //Displays as the actual timestamp
        dateTest.setTimestamp1(convertStringToTimestampWithoutDaylight("2014030902101900"));
        System.out.println("Converted Timestamp with Daylight savings:" + dateTest.getTimestamp1());
        //1 hour is reduced compared to current timestamp as Daylight savings time is removed
        System.out.println("Current date after changing Timezone:" + new java.util.Date());
        System.out.println("=====================================================================");
        //Reset back to the original timezone
        TimeZone.setDefault(defaultTimeZone);
        Displays current timestamp
        System.out.println("Current date after Timezone reset:" + new java.util.Date());

        //Adds one hour again
        System.out.println("Converted Timestamp after Timezone reset:" + dateTest.getTimestamp1());
        System.out.println("=====================================================================");
    }
}

我们在项目中使用当前时间戳对象和字符串来进行时间戳转换。我也尝试过Joda-Time,但最后我们需要Timestamp对象,当Joda-Time转换为Timestamp时,正在添加夏令时。

1
2
3
4
5
6
7
8
   DateTime dateTime = DateTime.now();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(dateTime.toDate());
        System.out.println("Current Timestamp:" + new Timestamp(calendar.getTimeInMillis()));
        LocalDateTime localdateTime = LocalDateTime.parse("2014030902101100", DateTimeFormat.forPattern("yyyyMMddHHmmssSS"));
        Calendar calendar2 = Calendar.getInstance();
        calendar2.setTime(localdateTime.toDate());
        System.out.println("Converted Timestamp:" + new Timestamp(calendar2.getTimeInMillis()));

输出:
当前时间戳:2014-03-22 09:15:09.478

转换时间戳:2014-03-09 03:10:11.0


在LinkedIn上的并行线程中,您声明您不知道时间戳对应的时区。在大多数情况下,这很糟糕。但如果确实存在这种情况,您可以自由地将它们视为您选择的任何时区中的时间戳。如果您不知道时间戳所对应的时区是否具有夏令时,以及它们遵循的规则是什么,您最安全的选择是将它们视为属于没有夏令时的时区。我建议使用UTC。因此,在解析或格式化时间戳之前,您需要将SimpleDateFormat的时区设置为UTC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    public static void main(String[] args) throws Exception {
    Date date = parseTimestamp("2014030902101100");
    System.out.println("Parsed date:" + formatDate(date));
    date = parseTimestamp("2014032202101100");
    System.out.println("Parsed date:" + formatDate(date));
}

private static Date parseTimestamp(String timestamp) throws ParseException {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSS");
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date date = sdf.parse(timestamp);
    return date;
}

private static String formatDate(Date date) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS z");
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    return sdf.format(date);
}

输出是:

1
2
Parsed date: 2014-03-09 02:10:11.00 UTC
Parsed date: 2014-03-22 02:10:11.00 UTC


您可以尝试使用SimpleDateFormat解析字符串,
就像是

1
2
SimpleDateFormat sd  = new SimpleDateFormat("yyyyMMddHHmmssSSz");
Date date = sd.parse("2014030902101100GMT-05:00");

对于z(时区)参数,您可以将GMT-05:00(纽约偏离GMT / UTC)添加到您手头的时间戳字符串中。
在此之后,您可以转换为您想要的。

simpledateforamt.parse将为您提供日期(java.util.date)。
从日期到时间戳的转换是直截了当的。

1
2
long ms = date.getTime();
Timestamp t =  new Timestamp(ms);


1
2
3
4
5
6
7
DateTime dateTime = DateTime.now();
Calendar calendar = Calendar.getInstance();
calendar.setTime(dateTime.toDate());
System.out.println("Current Timestamp:" + new Timestamp(calendar.getTimeInMillis()));
LocalDateTime localdateTime = LocalDateTime.parse("2014030902101100",
DateTimeFormat.forPattern("yyyyMMddHHmmssSS"));
System.out.println("Converted Timestamp:" + Timestamp.valueOf(localdateTime.toString("yyyy-mm-dd hh:mm:ss")));