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

如何在Java中转换UTC和本地时区,java,timezone,Java,Timezone,我对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

我对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
我想我可以在2014年1月13日00:12:00进行指挥与控制,因为UTC时间比我的时间晚11小时。日历不是按我预期的方式工作吗

谢谢你的帮助

编辑
添加z以显示时区。这让我更加困惑,因为Mac说它的时区是澳大利亚东部夏时制(AEDT),而爪哇是东部夏时制(EST)。无论如何,结果还是不同的,因为EST是UTC-5小时。

您可能是想在格式化程序上设置时区,而不是日历(或者除了日历之外,还没有100%清楚您想要完成什么)!用于创建人类表示的时区来自SimpleDataFormat。通过调用
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()));
正在打印
2014年1月14日10:12:00
,因为Syndey中显示的上午11点UTC(格式化程序的时区)是晚上10点!(在24小时时间格式中使用HH)

这将打印出您似乎想要执行的操作:

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的一个陷阱。

更新:这个答案现在已经过时了。Joda时间库现在被java 8及更高版本中内置的java.Time框架所取代。看

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

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

乔达时间 众所周知,java.util.Date/Calendar类非常糟糕。避免使用它们。在Java8中,使用JSR310定义的、受Joda Time启发的新的或

注意下面显示的Joda时间代码是多么简单和明显。乔达时间甚至知道如何计数——一月是1,而不是0

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

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

小贴士:不要这样想

UTC时间比我晚11小时

像这样想

悉尼夏令时比UTC/GMT早11小时

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

示例代码 下面是一些使用JodaTime2.3和Java8的示例代码

// 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
UTC日期时间:2014-01-14T00:12:00.000Z
tl;博士 使用现代java.time类

138965832000

往另一个方向走

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" ) 
    )
)
Instant instant = Instant.ofEpochMilli( millisecondsSinceEpoch ) ;
2014年1月14日11:12:00 AEDT

java.time 您正在使用可怕的日期时间类,这些类在几年前由于采用JSR310定义现代java.time类而过时

我对Java中的时区很好奇

仅供参考,与UTC的偏差仅为小时分秒数。当我们说“UTC”或在字符串末尾加一个
Z
时,我们指的是UTC本身的零小时分秒偏移量

时区更重要。时区是特定地区人民使用的偏移量的过去、现在和未来变化的历史。世界各地的政客都有一种奇怪的倾向,那就是改变他们管辖权的偏移量

我想从设备获取UTC时间(以毫秒为单位),然后发送到服务器

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

Instant now = Instant.now() ;  // Capture current moment in UTC.
long millisecondsSinceEpoch = now.toEpochMilli() ;
往另一个方向走

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" ) 
    )
)
Instant instant = Instant.ofEpochMilli( millisecondsSinceEpoch ) ;
服务器将把它转换为本地时区

指定用户所需/预期的时区

如果未指定时区,JVM将隐式应用其当前默认时区。该默认值可能在运行时(!)期间出现,因此您的结果可能会有所不同。最好将所需/预期时区明确指定为参数。如果关键,请与用户确认区域

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

…当它向用户显示时间时

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

要本地化,请指定:

  • 确定字符串的长度或缩写
  • 确定:
    • 翻译日名、月名等的人类语言
    • 决定缩写、大写、标点、分隔符等问题的文化规范
例如:

Locale l = Locale.CANADA_FRENCH ;   // Or Locale.US, Locale.JAPAN, etc.
DateTimeFormatter f = 
    DateTimeFormatter
    .ofLocalizedDateTime( FormatStyle.FULL )
    .withLocale( l )
;
String output = zdt.format( f );
我的系统中的时区
ZoneID zAuSydney = ZoneId.of( "Australia/Sydney" ) ;
ZonedDateTime zdt = instant.atZone( zAuSydney ) ;
String output = zdt.format(
    DateTimeFormatter
    .localizedDateTime( FormatStyle.LONG )
    .withLocale( new Locale( "en" , "AU" ) ;
) ;
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 ) ;
Locale locale = new Locale ( "en" , "AU" );
ZoneId z = ZoneId.of ( "Australia/Sydney" );
ZonedDateTime zdt = ZonedDateTime.of ( 2014 , 1 , 14 , 11 , 12 , 0 , 0 , z );
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "dd/MM/uuuu HH:mm:ss z" , locale );
String output = zdt.format ( f );
Instant instant = zdt.toInstant() ;  // Adjust from time zone to UTC.
long millisecondsSinceEpoch = instant.toEpochMilli() ;
                final String time="UTC";
                int year = 2014;
                int month = 0;
                int date = 14;
                int hourOfDay = 11;
                int minute = 12;
                int second = 0;
        
               calendar.set(year, month, date, hourOfDay, minute, second);
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSz");   
                  System.out.println(sdf.format(calendar.getTime()));
        
               Calendar calendar1=Calendar.getInstance();
               Date dat= calendar.getTime();
             
                calendar1.set(year,month,date,hourOfDay,minute,second);
                sdf.setTimeZone(TimeZone.getTimeZone(time));
               System.out.println(sdf.format(calendar1.getTime()));