当前位置: 首页 > 知识库问答 >
问题:

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

朱承载
2023-03-14

我对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小时。

共有3个答案

逄嘉禧
2023-03-14

使用现代 java.time 类。

ZonedDateTime
.of( 2014 , 1 , 14 , 11 , 12 , 0 , 0 , ZoneId.of( "Australia/Sydney" ) )
.toInstant()
.toEpochMilli() 

1389658320000

走向另一个方向。

Instant
.ofEpochMilli( 1_389_658_320_000L )  // .toString(): 2014-01-14T00:12:00Z
.atZone( 
    ZoneId.of( "Australia/Sydney" ) 
)                                    // .toString(): 2014-01-14T11:12+11:00[Australia/Sydney]
.format(
    DateTimeFormatter
    .ofPattern ( 
        "dd/MM/uuuu HH:mm:ss z" , 
        new Locale( "en" , "AU" ) 
    )
)

14/01/2014 11:12:00 AEDT

您正在使用糟糕的日期-时间类,由于采用了定义现代java.time类的JSR 310,这些类已经过时了。

我对Java的时区很好奇。

仅供参考,UTC 的偏移量只是小时-分钟-秒的数量。当我们说“UTC”或在字符串末尾放置 Z 时,我们的意思是 UTC 本身的零小时-分钟-秒的偏移量。

时区远不止如此。时区是特定地区的人们使用的时差的过去、现在和未来变化的历史。世界各地的政治家们都有一种奇怪的偏好,那就是改变他们管辖权的偏移。

我想从一个设备获取UTC时间(以毫秒为单位),并发送给服务器。

对于当前时刻,请使用即时。内部的Instant是自1970 UTC第一时刻的历元参考起的整秒秒数,加上以纳秒为单位的几分之一秒。

Instant now = Instant.now() ;  // Capture current moment in UTC.
long millisecondsSinceEpoch = now.toEpochMilli() ;

走向另一个方向。

Instant instant = Instant.ofEpochMilli( millisecondsSinceEpoch ) ;

服务器将其转换为本地时区…

指定用户期望的时区。

如果没有指定时区,JVM将隐式应用其当前的默认时区。该默认值可能会在运行时的任何时刻发生变化。),因此您的结果可能会有所不同。最好将您想要/预期的时区明确指定为一个参数。如果非常重要,请与您的用户确认该区域。

以大陆/地区格式指定正确的时区名称,例如美洲/蒙特利尔、非洲/卡萨布兰卡太平洋/奥克兰切勿使用 2-4 个字母的缩写,例如 ESTIST,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Montreal" ) ;  
ZonedDateTime zdt = instant.atZone( z ) ;

…当它向用户显示时间时

针对用户的语言和文化自动本地化。

要本地化,请指定:

    < li> FormatStyle确定字符串的长度或缩写。 < li> 区域设置来确定: < ul > < li >用于翻译日期名称、月份名称等的人类语言。 < li >决定缩写、大写、标点、分隔符等问题的文化规范。

例:

Locale l = Locale.CANADA_FRENCH ;   // Or Locale.US, Locale.JAPAN, etc.
DateTimeFormatter f = 
    DateTimeFormatter
    .ofLocalizedDateTime( FormatStyle.FULL )
    .withLocale( l )
;
String output = zdt.format( f );

我的系统中的时区是澳大利亚/悉尼(UTC 11:00)

服务器的当前默认时区应该与程序无关。始终指定所需/预期的时区。坦率地说,使各种日期-时间方法的时区(和Locale)参数可选是java.time框架中为数不多的设计缺陷之一。

提示:通常最好将您的服务器设置为UTC作为其当前默认时区。

顺便说一句,要清楚时区和地区是没有任何关系的。您可能希望用日语显示在< code >非洲/突尼斯时区看到的时刻。

ZoneID zAuSydney = ZoneId.of( "Australia/Sydney" ) ;
ZonedDateTime zdt = instant.atZone( zAuSydney ) ;
String output = zdt.format(
    DateTimeFormatter
    .localizedDateTime( FormatStyle.LONG )
    .withLocale( new Locale( "en" , "AU" ) ;
) ;

int year = 2014…

请注意java。与传统类不同,time使用理智的编号。一月至十二月的月份为1-12,周一至周日的工作日为1-7。

LocalDate ld = LocalDate.of( 2014 , 1 , 14 ) ;
LocalTime lt = LocalTime.of( 11 , 12 ) ;
ZoneId z = ZoneId.of( "Australia/Sydney" ) ;
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;

zdt.toString() = 2014-01-14T11:12 11:00[澳大利亚/悉尼]

如上所述,通常最好自动本地化显示。但如果你坚持,你可以硬编码一个格式化模式。

Locale locale = new Locale ( "en" , "AU" );
ZoneId z = ZoneId.of ( "Australia/Sydney" );
ZonedDateTime zdt = ZonedDateTime.of ( 2014 , 1 , 14 , 11 , 12 , 0 , 0 , z );

zdt。toString():2014-01-14T11:12 11:00[澳大利亚/悉尼]

指定您的格式模式。

DateTimeFormatter f = DateTimeFormatter.ofPattern ( "dd/MM/uuuu HH:mm:ss z" , locale );
String output = zdt.format ( f );

输出=2014年1月14日11:12:00 AEDT

你的问题感兴趣的是从1970-01-01T00:00:00Z开始的毫秒计数。因此,请从澳大利亚时区调整为UTC。同一时刻,时间线上的同一点,不同的挂钟时间。

Instant instant = zdt.toInstant() ;  // Adjust from time zone to UTC.

instant . tostring():2014-01-14t 00:12:00Z

请注意即时zdt

我想我可以有13/01/2014 00:12:00为c2因为UTC时间是11小时晚于我的。

➥ 正如您所要求的,悉尼地区上午11点后12分钟与UTC午夜12分钟相同,因为澳大利亚/悉尼在该日期比UTC提前11小时。

计算自纪元以来的毫秒数。

long millisecondsSinceEpoch = instant.toEpochMilli() ;
云凌
2023-03-14

你可能想在你的格式化程序上设置时区,而不是日历(或者除了日历之外,并不是100%清楚你想完成什么)!用于创建人类表示的时区来自SimpleDateFormat。当您通过调用< code>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(格式化程序的时区)中显示的 11AM 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 毫秒”的概念毫无意义。毫秒的数量只是历史中的一个固定点,它没有与之关联的时区。我们为其添加一个时区,以将其转换为人类可读的表示形式。

编辑:是的,对(美国)东部时间和(澳大利亚)东部时间同时使用“EST”的歧义一直是Java的一个陷阱。

隆功
2023-03-14

更新:此答案现已过时。Joda-Time 库现在被 Java 8 及更高版本中内置的 java.time 框架所取代。请参阅此新答案。

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

使用正确的时区名称,主要是大陆/城市或地区,如美洲/蒙特利尔亚洲/加尔各答

java.util.日期/日历类是出了名的糟糕。避免使用它们。使用Joda-Time,或者在Java8中,使用由JSR 310定义并受Joda-Time启发的新java.time.*类。

请注意,下面显示的Joda Time代码更简单、更明显。Joda Time甚至知道如何计数——

在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
 类似资料:
  • 问题内容: 我对Java中的时区感到好奇。我想从设备获取UTC时间(以毫秒为单位)并发送到服务器。当服务器向用户显示时间时,服务器会将其转换为本地时区。我系统中的时区是澳大利亚/悉尼(UTC + 11:00),测试时区时,我得到的结果如下: 输出: 我以为我可以为c2安排13/01/2014 00:12:00,因为UTC时间比我的时间晚11个小时。日历不能按我期望的方式工作吗? 您的帮助将不胜感激

  • 问题内容: 我知道这个主题已被殴打致死,但在搜寻了这个问题几个小时之后,我不得不问。 我的问题:根据客户端应用程序(iphone)的当前时区对服务器上的日期进行计算。客户端应用程序以秒为单位告诉服务器,其时区距GMT的距离。然后,我想使用此信息对服务器中的日期进行计算。服务器上的日期都存储为UTC时间。 因此,在将UTC日期对象转换为该本地时区之后,我想获取其HOUR。 我目前的尝试: 变量小时和

  • 问题内容: 我有一个UTC时区,例如: 还有一个pytz时区对象: 转换为给定时区的正确方法是什么? 问题答案: 我想我明白了: 这行代码首先将朴素的(不了解时区的) 对象转换为包含时区(UTC)的对象。然后,它使用该功能根据请求的时区调整时间。

  • 问题内容: 如何将UTC时间转换为 当地时间 ? 我已经为我需要当地时间的所有国家/地区创建了具有UTC时差的地图。然后,我将该差异作为持续时间添加到当前时间(UTC),并打印结果希望是该特定国家/地区的当地时间。 由于某些原因,结果是错误的。例如,在匈牙利,相差一小时。知道为什么我得到的结果不正确吗? 问题答案: 请记住,操场的时间设置为,因此它正在工作。 正确的方法是使用,这是一个示例:

  • 问题内容: 我有一个毫秒本地本地时间戳,我想将其转换为毫秒本地UTC时间戳。快速浏览一下文档,看起来像这样工作: 有一个更好的方法吗? 问题答案: 使用a 来获取本地纪元处的偏移量,然后将其添加到本地纪元时间戳中。

  • 我们在本地时区(如EST、PST、CST等)的sql中有多列。我们希望将UTC时区中的所有这些时区转换为考虑到日光节省并将其保存到另一列。 我尝试了下面的查询,但它将UTC转换为当地时区(EST ),但我正在寻找EST到UTC时区。 选择CONVERT(日期时间,SWITCHOFFSET(日期时间字段,日期部分(TZOFFSET,时间区域“东部标准时间”的日期时间字段))