Calendar.getInstance()。getTime()以“GMT”而不是Default TimeZone返回日期

时间:2017-09-12 07:16:37

标签: java calendar gettime

Calendar c = Calendar.getInstance();
System.out.println(c.getTime());
c.set(2007, 0, 1);
System.out.println(c.getTime());

输出:

  

Tue Sep 12 12:36:24 IST 2017

     
    

Mon Jan 01 12:36:24 IST 2007

  

但是,当我在不同的环境中使用相同的代码时,输​​出更改为:

输出:

  

Tue Sep 12 12:36:24 IST 2017

     
    

Mon Jan 01 12:36:24 GMT 2007

  

仅供参考,我尝试在设置值之前和之后打印日历实例的时区,两者都在“IST”中。

我想知道这个的根本原因。

3 个答案:

答案 0 :(得分:7)

您问题中的第二个输出是运行爱尔兰时间(欧洲/都柏林)的JVM上的正确和预期行为。 2017年9月12日爱尔兰是夏季时间(夏令时)。虽然没有明确记录,Date.toString()(在打印Date时从c.getTime()打印时隐式调用)会在JVM的时区中打印日期和时间,该时区在9月份会被渲染作为爱尔兰夏令时的IST。

当您使用爱尔兰时间在Calendar对象上设置日期时,会保留一天中的小时数;在你的情况下,你得到爱尔兰标准时间2007年1月1日12:36:24。现在想象一下,如果将爱尔兰夏令时和爱尔兰标准时间都呈现为IST,那么会产生混淆。你无法区分。相反,由于爱尔兰标准时间与格林尼治标准时间一致,因此Date.toString()在年​​度夏季时间(1月份不是)的日期时会打印出来。

我的猜测是你的第一个输出来自运行印度时间的JVM。它也被渲染为IST,并且由于印度不使用夏令时,因此夏季和冬季都会使用相同的缩写。

java.time

在理解您观察到的行为的解释之前,我发布了关于过时和现代Java日期和时间类的评论。不过,我仍然不认为评论有点偏僻。这是代码的现代等价物:

    ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Europe/Dublin"));
    System.out.println(zdt);
    zdt = zdt.with(LocalDate.of(2007, Month.JANUARY, 1));
    System.out.println(zdt);

打印

2017-09-12T11:45:33.921+01:00[Europe/Dublin]
2007-01-01T11:45:33.921Z[Europe/Dublin]

如果要使用JVM的时区设置,请使用ZoneId.systemDefault()代替ZoneId.of("Europe/Dublin")。如名称所示,与Date相反,ZonedDateTime确实包含时区。它更多地对应旧的Calendar类。如您所见,其toString方法打印UTC(Z表示零偏移)的偏移量和明确的 region / city 格式的时区名称。我相信这样可以减少混淆的空间。如果要以特定格式打印日期,请使用DateTimeFormatter

附录:代码中的示例输出

为了完整起见,以下是运行可能呈现为IST的不同时区时代码的输出:

  • 欧洲/都柏林(同意你的第二个产出)

    Tue Sep 12 11:19:28 IST 2017
    Mon Jan 01 11:19:28 GMT 2007
    
  • 亚/ Tel_Aviv

    Tue Sep 12 13:19:28 IDT 2017
    Mon Jan 01 13:19:28 IST 2007
    
  • 亚洲/加尔各答(同意你的第一个产出)

    Tue Sep 12 15:49:28 IST 2017
    Mon Jan 01 15:49:28 IST 2007
    

答案 1 :(得分:6)

您需要设置时区,您将获得所需的结果。

TimeZone.setDefault(TimeZone.getTimeZone("IST"));

这是一个有效的代码。

import java.util.Calendar;
import java.util.TimeZone;  
public class Cal {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        TimeZone.setDefault(TimeZone.getTimeZone("IST")); // Add this before print
        Calendar c = Calendar.getInstance();    
        System.out.println(c.getTime());
        c.set(2007, 0, 1);
        System.out.println(c.getTime());
    }

}

根据Doc “通常,您使用getDefault获取TimeZone,它会根据程序运行的时区创建TimeZone。例如,对于在日本运行的程序,getDefault会创建一个TimeZone对象基于日本标准时间。“

因此,当您在不同时区运行时,它将用作默认时区。希望你现在明白。我附上了doc。请阅读。

答案 2 :(得分:0)

谈谈这个有趣的行为:

Calendar类的源代码:

public final void set(int year, int month, int date)
{
    set(YEAR, year);
    set(MONTH, month);
    set(DATE, date);
}

这导致了set方法:

public void set(int field, int value)
{
    // If the fields are partially normalized, calculate all the
    // fields before changing any fields.
    if (areFieldsSet && !areAllFieldsSet) {
        computeFields();
    }
    internalSet(field, value);
    isTimeSet = false;
    areFieldsSet = false;
    isSet[field] = true;
    stamp[field] = nextStamp++;
    if (nextStamp == Integer.MAX_VALUE) {
        adjustStamp();
    }
}

这里有趣的部分是computeFields()方法,它有两个实现(一个用于Gregorian,一个用于Japenese日历)。这些方法非常复杂,但据我所知,这是您的Calendar实例可能在您的用例中更改时区的唯一位置。