将 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 日期时间转换为特定时区