将UTC日期转换为其他时区

40

我正在将UTC时间转换为另一个时区,使用以下方法:

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date parsed = format.parse("2011-03-01 15:10:37");
TimeZone tz = TimeZone.getTimeZone("America/Chicago");
format.setTimeZone(tz);

String result = format.format(parsed);

输入是2011-03-01 15:10:37,但这个函数的输出结果(即result的值)是2011-03-01 05:40:37。虽然看起来不对劲,但根据此链接,答案应该是2011-03-01 09:10:37

我做错了什么?

7个回答

75

事实证明,代码几乎是正确的,我没有考虑到的是,在最初解析 String 以获取 Date 对象时,它使用默认系统 TimeZone,因此源日期不在我预期的 UTC 中。

诀窍在于在解析日期时将时区设置为 UTC,然后将其设置为目标 TimeZone。就像这样:

SimpleDateFormat sourceFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sourceFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date parsed = sourceFormat.parse("2011-03-01 15:10:37"); // => Date is in UTC now

TimeZone tz = TimeZone.getTimeZone("America/Chicago");
SimpleDateFormat destFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
destFormat.setTimeZone(tz);

String result = destFormat.format(parsed);

3
别忘了在sourceFormat.parse()周围加上 try{} catch (ParseException e){}。此外,为了获取手机所在地区的本地时区,请使用TimeZone.getDefault(),以便将UTC转换为本地时区。 - Someone Somewhere
@hadi Eskandari,你真是救了我!我一直在为解决这个问题而苦恼...!我从我们的服务器中拉取值并将其存储到数据库中,然后再显示到屏幕上。服务器日期存储在AEST(澳大利亚东部标准时间)中,我遇到了与你完全相同的问题,即它使用默认系统时区来解释时间,因此时间解释错误。当我尝试更改到另一个日期时,它总是错误的 :) 在我的解析行之前,我只需要添加 formater.setTimeZone(TimeZone.getTimeZone(RBApplication.adjustTimezoneAEST)); 即可。谢谢,我已经为这个问题奋斗了几个小时! - wired00
SimpleDateFormat中的“HH”表示24小时制而不是12小时制,这让我调试了2个小时。 - Khurram Shehzad

14
将格式为“2011-06-23T15:11:32”的日期字符串转换为我们的时区。
 private String getDate(String dateString) {
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
    formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date value = null;
    try {
        value = formatter.parse(dateString);
    } catch (ParseException e) {
        e.printStackTrace();
    }
    SimpleDateFormat dateFormatter = new SimpleDateFormat("dd/MM/yyyy hh:mmaa");
    dateFormatter.setTimeZone(TimeZone.getDefault());
    String dt = dateFormatter.format(value);

    return dt;
}

10

以下的代码对我来说可以很好地将一个时区的日期转换为另一个时区。它还考虑了夏令时。

public static Calendar changeTimezoneOfDate(Date date, TimeZone fromTZ, TimeZone toTZ) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    long millis = calendar.getTimeInMillis();
    long fromOffset = fromTZ.getOffset(millis);
    long toOffset = toTZ.getOffset(millis);
    long convertedTime = millis - (fromOffset - toOffset);
    Calendar c = Calendar.getInstance();
    c.setTimeInMillis(convertedTime);
    return c;
}

4
你可以根据你的需求解析日期格式和时区。 尝试使用以下代码段,希望对你有所帮助。
private String getFormattedDate(String OurDate) {
    try {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); // According to your Server TimeStamp
        formatter.setTimeZone(TimeZone.getTimeZone("UTC")); //your Server Time Zone
        Date value = formatter.parse(OurDate); // Parse your date

        SimpleDateFormat dateFormatter = new SimpleDateFormat("MM-dd-yyyy"); //this format changeable according to your choice
        dateFormatter.setTimeZone(TimeZone.getDefault());
        OurDate = dateFormatter.format(value);

    } catch (Exception e) {
        OurDate = "00-00-0000 00:00";

    }
    return OurDate;
}

2

你需要考虑夏令时。我通过计算与UTC的偏移量(以毫秒为单位)来完成这项工作。类似以下代码应该可以胜任。

int currentOffsetFromUTC = tz.getRawOffset() + (tz.inDaylightTime(parsed) ? tz.getDSTSavings() : 0);
String result = format.format(parsed.getTime() + currentOffsetFromUTC);
inDayLightTime(...)方法返回一个布尔值,必须传递一个日期对象才能确定该“日期”是否表示夏令时期间。

2

java.time

建议在处理日期和时间时使用现代的 Java 日期和时间 API - java.time。

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
    ZoneId targetZone = ZoneId.of("America/Chicago");
    
    LocalDateTime parsed = LocalDateTime.parse("2011-03-01 15:10:37", formatter);
    ZonedDateTime converted = parsed.atOffset(ZoneOffset.UTC).atZoneSameInstant(targetZone);

    String result = converted.format(formatter);
    System.out.println(result);

输出结果如下:

2011-03-01 09:10:37

问题:Java.time需要Android API级别26吗?

Java.time在较旧和较新的Android设备上都可以很好地工作。它只需要至少Java 6

  • 在Java 8及更高版本以及较新的Android设备(从API级别26开始),现代API已内置。
  • 在非Android Java 6和7中,使用ThreeTen Backport,即现代类的后移版本(ThreeTen用于JSR 310;请参见底部链接)。
  • 在旧版Android上,可以使用desugaring或ThreeTen Backport的Android版本。它称为ThreeTenABP。在后一种情况下,请确保从org.threeten.bp导入日期和时间类及其子包。

链接


0
我选择的简单序列是将UTC时间解析为LocalDateTime,然后使用所需的时区创建一个ZonedDateTime。生成的ZonedDateTime可用于获取Instant以构造新的Date对象。
在下面的示例片段中,可以使用所需的时区ID替换ZoneId.systemDefault()

var instant = LocalDateTime.parse(serverDateTime, DateTimeFormatter.ISO_DATE_TIME)
                .atZone(ZoneId.of("UTC")) //the current zone is UTC
                .withZoneSameInstant(ZoneId.systemDefault()) //the new time zone
                .toInstant();

var date = Date.from(instant);


注意:如果要在 Android API 级别低于 26 的情况下使用 LocalDateTime,您可以将 Android desugaring lib 添加到您的代码库中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接