【问题标题】:java.util.Date to java.util.Calendar weird timezone changejava.util.Date 到 java.util.Calendar 奇怪的时区变化
【发布时间】:2021-02-10 08:35:08
【问题描述】:

我想将 java.util.Date 对象设置为 java.util.Calendar 对象以更改 Date 对象的年份,如下所示:

TimezoneId 等于“Europe/Istanbul”。年份是 2018。

日期对象是 1969-12-31 22:00:00.0 (1969-12-31T22:00:00.000Z),zoneinfo 如下:

"sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]"

欧洲/伊斯坦布尔时区是 UTC+3,但是当我使用此日期对象运行代码时,新的 GregorianCalendar 将日期对象的时间更改为 -1 小时。出现问题,我找不到为什么不将 UTC 转换为 UTC+3 .Returned Date 对象等于 Sun Dec 31 21:00:00 UTC 2017.UTC-1 对于欧洲/伊斯坦布尔不正确。我该如何解决这个问题?

public static Date setYearOfDate(Date date, int year, String timeZoneId)
    {
        Calendar customCalendar = new GregorianCalendar(TimeZone.getTimeZone(timeZoneId));
        customCalendar.setTime(date);
        customCalendar.set(Calendar.YEAR, year);

        return customCalendar.getTime();
    }

注意:customCalendar zoneinfo 如下: "sun.util.calendar.ZoneInfo[id="Europe/Istanbul",offset=10800000,dstSavings=0,useDaylight=false,transitions=130,lastRule=null]"

【问题讨论】:

  • 您使用的是哪个 Java 版本?
  • @RafaelGuillen 版本是 Java8
  • 伊斯坦布尔现在可能在 UTC+3,但过去它要么在 UTC+2,要么在 UTC+2 和 UTC+3 之间交替进行夏令时.在 1969 年和 1970 年初,它是 UTC+2。见timeanddate.com/time/zone/turkey/…
  • 我建议你不要使用DateCalendar。这些课程设计不佳且早已过时。如果您真的想要一个没有时间的日期,请使用java.time, the modern Java date and time API 中的LocalDate。如果要包含时间和时区,请使用同一 API 中的ZonedDateTime
  • @MattJohnson-Pint 的评论暗示你得到的结果是正确的。

标签: java date timezone simpledateformat java-time


【解决方案1】:

java.time

我建议你使用 java.time API。

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

class Main {
    public static void main(String[] args) {
        String dateTimeString = "1969-12-31T22:00:00.000Z";
        System.out.println(setYearOfDate(dateTimeString, 2018, "Europe/Istanbul"));
    }

    public static String setYearOfDate(String dateTimeString, int year, String zoneId) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX", Locale.ENGLISH)
                                                    .withZone(ZoneId.of(zoneId));

        ZonedDateTime zdt = ZonedDateTime.parse(dateTimeString);
        zdt = zdt.withYear(year);

        return dtf.format(zdt);
    }
}

输出:

2019-01-01T01:00:00.000+03

java.util 日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用,转用modern date-time API

SimpleDateFormat 错误行为的一个很好的例子如下所示:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;

class Main {
    public static void main(String[] args) throws ParseException {
        String dateTimeString = "1969-12-31T22:00:00.000Z";
        String timeZoneId = "Europe/Istanbul";
        System.out.println(setYearOfDate(dateTimeString, 2018, timeZoneId));
    }

    public static String setYearOfDate(String dateTimeString, int year, String timeZoneId) throws ParseException {
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.ENGLISH);
        calendar.setTime(sdf.parse(dateTimeString));
        calendar.set(Calendar.YEAR, year);
        sdf.setTimeZone(TimeZone.getTimeZone(timeZoneId));
        return sdf.format(calendar.getTime());
    }
}

输出:

2019-01-01T02:00:00.000+03

您将在 1968 年和 1970 年观察到 SimpleDateFormat 的这种行为。但是,如果您将输入字符串中的年份更改为其他年份,它会以预期的方式运行,例如相同的代码将为输入字符串1979-12-31T22:00:00.000Z 输出2019-01-01T01:00:00.000+03

【讨论】:

  • 但我看到小时 UTC+1 的输出对于欧洲/伊斯坦布尔时区不正确。它应该是 UTC+3
  • @volkan - 当我发布答案时,我正忙于开会。我已经更新了它,它显示了您期望的输出。我还在忙着一整天的会议。如果您有任何问题,请随时发表评论,我会在休息时回复,就像我刚才所做的那样。
  • @Live_and_Let_Live 感谢您的友好回应,但我不想设置 UTC+3.Only 问题是为什么欧洲/伊斯坦布尔(UTC+3)会影响小时 -1 小时?通常我希望这个 setYearOfDate 方法应该更改为 UTC+3,而不是 UTC-1。
  • @volkan - SimpleDateFormat 已知容易出错。我建议您切换到现代日期时间 API。使用SimpleDateFormat,当您更改输入字符串中的年份(例如相同的代码将为输入字符串1979-12-31T22:00:00.000Z 输出2019-01-01T01:00:00.000+03
  • 1965 年至 1969 年之间的年份没有变化,欧洲/伊斯坦布尔时区的整个期间 UTC +2 小时。所以当我们更改 1979 年时它正在工作。谢谢。
【解决方案2】:

我认为@Live_and_Let_Live 的答案是正确的,要打印Date 你需要提供TimeZoneSimpleDateFotmat,实际上你不需要使用SimpleDateFotmat 来设置年份;使用您的代码并正确应用TimeZone,您将获得预期的结果:

public static void main (String[] args) throws java.text.ParseException
{
    String initialDate = "1969-12-31T22:00:00.000Z";
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
    
    Date startDate = sdf.parse(initialDate);
            
    System.out.println("Start date: " + startDate);
    System.out.println("Start date formatted: " + sdf.format(startDate));
    
    Date resultDate = setYearOfDate(startDate, 2018, "Europe/Istanbul");
    System.out.println("Result date: " + resultDate);
    System.out.println("Result date formatted: " + sdf.format(resultDate));
        
    SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
    sdf2.setTimeZone(TimeZone.getTimeZone("Europe/Istanbul"));
    System.out.println("Result date formatted with TimeZone: " + sdf2.format(resultDate));
}

输出:

Start date: Wed Dec 31 22:00:00 GMT 1969
Start date formatted: 1969-12-31T22:00:00.000Z
Result date: Sun Dec 31 21:00:00 GMT 2017
Result date formatted: 2017-12-31T21:00:00.000Z
Result date formatted with TimeZone: 2018-01-01T00:00:00.000+03

编辑: 这是 Java 8 日期和时间 API 替代方案:

public static void main(String[] args) {
    String initialDate = "1969-12-31T22:00:00.000Z";
    ZoneId zone = ZoneId.of("Europe/Istanbul");
    DateTimeFormatter formatter = DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(zone);
    
    ZonedDateTime startDate = ZonedDateTime.parse(initialDate, formatter);

    System.out.println("Start date: " + startDate);
    System.out.println("Start date formatted with TimeZone: " + formatter.format(startDate));

    ZonedDateTime resultDate = setYearOfDate(startDate, 2018, "Europe/Istanbul");
    System.out.println("Result date: " + resultDate);
    System.out.println("Result date formatted with TimeZone: " + formatter.format(resultDate));
}

static ZonedDateTime setYearOfDate(ZonedDateTime date, int year, String timeZoneId)
{
    GregorianCalendar calendar = GregorianCalendar.from(date);
    calendar.set(GregorianCalendar.YEAR, year); 
    return calendar.toZonedDateTime();
}

输出:

Start date: 1970-01-01T00:00+02:00[Europe/Istanbul]
Start date formatted with TimeZone: 1970-01-01T00:00:00+02:00[Europe/Istanbul]
Result date: 2018-01-01T00:00+03:00[Europe/Istanbul]
Result date formatted with TimeZone: 2018-01-01T00:00:00+03:00[Europe/Istanbul]

【讨论】:

  • @rafael_guillen 谢谢,但问题是结果日期应该是:Sun Dec 31 01:00:00 (UTC+3) 问题是为什么欧洲/伊斯坦布尔 (UTC+3) 会影响小时 UTC-1小时?
  • 开始日期(1969-12-31T22:00:00.000Z)实际上是1970-01-01T00:00:00.000+02,所以我认为结果(`2018-01-01T00:00:00.000+03)是正确的。
猜你喜欢
  • 2013-08-25
  • 2013-01-14
  • 2018-05-15
  • 2018-07-19
  • 1970-01-01
  • 1970-01-01
  • 2022-01-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多