如何在Java中转换UTC和本地时区

我对Java中的时区感到好奇。 我希望从设备获得UTC时间(以毫秒为单位)并发送到服务器。 当服务器向用户显示时间时,服务器会将其转换为本地时区。 我的系统中的时区是澳大利亚/悉尼(UTC + 11:00),当我测试时区时,我得到了以下结果:

int year = 2014; int month = 0; int date = 14; int hourOfDay = 11; int minute = 12; int second = 0; Calendar c1 = Calendar.getInstance(); c1.set(year, month, date, hourOfDay, minute, second); SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss z"); System.out.println(sdf.format(c1.getTime())); Calendar c2 = Calendar.getInstance(TimeZone.getTimeZone("UTC")); c2.set(year, month, date, hourOfDay, minute, second); System.out.println(sdf.format(c2.getTime())); 

输出:

 14/01/2014 11:12:00 EST 14/01/2014 22:12:00 EST 

我以为我可以在13/01/2014 00:12:00为c2,因为UTC时间晚于我的11小时。 日历不按我期望的方式工作吗?

非常感谢您的帮助。

编辑

添加了z以显示时区。 这让我更加困惑,因为Mac说它的时区是(AEDT)澳大利亚东部夏令时,但Java是EST。 无论如何,结果仍然不同,因为EST是UTC-5小时。

您可能打算在格式化程序上设置时区,而不是日历(或者除了日历之外,它不是100%明确您要完成的内容)! 用于创建人工表示的时区来自SimpleDateFormat。 当您通过调用getTime()将其转换回java.util.Date时,所有“时区”信息都会从日历中丢失。

代码:

 Calendar c2 = Calendar.getInstance(TimeZone.getTimeZone("UTC")); c2.set(year, month, date, hourOfDay, minute, second); System.out.println(sdf.format(c2.getTime())); 

正在打印14/01/2014 10:12:00因为在Syndey(格式化程序的时区)显示的上午11点UTC是晚上10点! (在格式中使用HH 24小时)

这将打印出您打算做的事情:

 SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss z"); System.out.println(sdf.format(c1.getTime())); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); System.out.println(sdf.format(c1.getTime())); 

“UTC毫秒”的概念毫无意义。 毫秒数量只是历史中的一个固定点,它没有与之相关的时区。 我们为它添加一个时区,将其转换为人类可读的表示forms。

编辑:是的,从(美国)东部时间和(澳大利亚)东部时间使用’EST’的模糊性一直是Java的陷阱。

三字母代码

您应该避免使用3或4个字母的时区代码,例如ESTIST 。 它们既不标准也不独特。

使用适当的时区名称 ,主要是Continent/CityOrRegion例如America/MontrealAsia/Kolkata

乔达时间

众所周知,java.util.Date / Calendar类很糟糕。 避免使用它们。 使用Joda-Time或Java 8中由JSR 310定义并受Joda-Time启发的新java.time。*类 。

请注意下面显示的Joda-Time代码更简单,更明显。 Joda-Time甚至知道如何计算 – 一月是1,而不是0!

时区

在Joda-Time中, DateTime实例知道自己的时区。

悉尼澳大利亚的标准时间比UTC / GMT提前10小时,夏令时(DST)提前11小时。 DST适用于问题指定的日期。

提示:不要这样想……

UTC时间比我的晚11个小时

想这样……

悉尼DST比UTC / GMT早11个小时。

如果您以UTC / GMT思考,工作和存储,日期时间工作变得更容易,并且更不容易出错。 仅转换为本地化的日期时间,以便在用户界面中进行演示。 全球思考,在本地展示。 您的用户和服务器可以轻松移动到其他时区,因此请忘记您自己的时区。 始终指定时区,永远不要假设或依赖默认值。

示例代码

下面是一些使用Joda-Time 2.3和Java 8的示例代码。

 // Better to specify a time zone explicitly than rely on default. // Use time zone names, not 3-letter codes. // This list is not quite up-to-date (read page for details): http://joda-time.sourceforge.net/timezones.html DateTimeZone timeZone = DateTimeZone.forID("Australia/Sydney"); DateTime dateTime = new DateTime(2014, 1, 14, 11, 12, 0, timeZone); DateTime dateTimeUtc = dateTime.toDateTime(DateTimeZone.UTC); // Built-in constant for UTC (no time zone offset). 

转储到控制台……

 System.out.println("dateTime: " + dateTime); System.out.println("dateTimeUtc: " + dateTimeUtc); 

跑的时候……

 dateTime: 2014-01-14T11:12:00.000+11:00 dateTime in UTC: 2014-01-14T00:12:00.000Z