将 UTC 日期转换为其他时区

Posted

技术标签:

【中文标题】将 UTC 日期转换为其他时区【英文标题】:Converting UTC dates to other timezones 【发布时间】:2011-08-30 15:12:56 【问题描述】:

我正在使用这种方法将 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,但是这个(结果的值)的输出是2011-03-01 05:40:37。虽然它似乎关闭了,但根据this link,它应该是2011-03-01 09:10:37

我做错了什么?

【问题讨论】:

【参考方案1】:

事实证明代码几乎是正确的,我没有考虑到最初解析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);

【讨论】:

不要忘记try catch (ParseException e) 周围的sourceFormat.parse()。此外,要获取手机所在的本地时区,请使用 TimeZone.getDefault() 以便您可以将 UTC 转换为本地时区 @hadi Eskandari 伙计,你刚刚救了我!我正在撕扯我的头发试图解决这个问题......!在显示到屏幕之前,我从我们的服务器中提取值并存储到数据库中。服务器日期存储在 AEST(澳大利亚东部标准)中,我遇到了您使用默认系统时区的确切问题,因此解释时间错误。当我尝试更改为另一个日期时,它总是错误的:) 我只需要在解析行之前formater.setTimeZone(TimeZone.getTimeZone(RBApplication.adjustTimezoneAEST));。干杯我在这个问题上花了几个小时! @wired00 很高兴听到这对伴侣有帮助:) SimpleDateFormat 中的“HH”是 24 小时而不是 12 小时,这占用了我 2 小时的调试时间【参考方案2】:

将格式为“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;

【讨论】:

【参考方案3】:

以下代码对我来说可以很好地将日期从一个 tz 更改为另一个。它也考虑了 DayLightSaving。

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】:

您可以根据需要解析日期格式和时区。 试试这个sn-p的代码,希望对你有帮助。

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;

【讨论】:

【参考方案5】:

您需要考虑夏令时。我通过以毫秒为单位计算出偏移量(来自 UTC)来做到这一点。这样的事情应该可以工作。

int currentOffsetFromUTC = tz.getRawOffset() + (tz.inDaylightTime(parsed) ? tz.getDSTSavings() : 0);
String result = format.format(parsed.getTime() + currentOffsetFromUTC);

inDayLightTime(...) 方法返回一个布尔值,并且必须传递一个 Date 对象,以便它决定该“日期”是否代表 DST 期间的日期。

【讨论】:

【参考方案6】:

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,这是现代类的后向端口(对于 JSR 310,ThreeTen;请参阅底部的链接)。 在较旧的 Android 上,请使用脱糖或 Android 版本的 ThreeTen Backport。它被称为 ThreeTenABP。在后一种情况下,请确保使用子包从 org.threeten.bp 导入日期和时间类。

链接

Oracle tutorial: Date Time 解释如何使用 java.time。 Java Specification Request (JSR) 310,其中首次描述了 java.time。 ThreeTen Backport project,java.time 的反向移植到 Java 6 和 7(JSR-310 的 ThreeTen)。 Java 8+ APIs available through desugaring ThreeTenABP,Android 版 ThreeTen Backport Question: How to use ThreeTenABP in Android Project,解释得很透彻。

【讨论】:

【参考方案7】:

我选择的简单顺序是将 UTC 时间解析为 LocalDateTime,然后创建一个带有所需时区的 ZonedDateTime。生成的ZonedDateTime 可用于获取Instant 以构造新的Date 对象。

在下面的示例 sn-p 中,可以将 ZoneId.systemDefault() 替换为所需的时区 id。


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);

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

【讨论】:

以上是关于将 UTC 日期转换为其他时区的主要内容,如果未能解决你的问题,请参考以下文章

将 UTC 中的 SQL Server 日期时间转换为特定时区

如何在Javascript中将日期转换为其他时区[重复]

将 UTC 日期和时间转换为 UNIX 时间会给出错误的(时区不同)值。为啥?

Spring MVC - 将用户时区的日期转换为 UTC

将输入字符串日期时间从不同时区转换为UTC

Pandas 使用单独的时区列转换日期时间