如何设置java.util.Date的时区?

我已经从String分析了一个java.util.Date ,但它将本地时区设置为date对象的时区。

从中解析DateString未指定时区。 我想设置date对象的特定时区。

我怎样才能做到这一点?

使用DateFormat。 例如,

 SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); isoFormat.setTimeZone(TimeZone.getTimeZone("UTC")); Date date = isoFormat.parse("2010-05-23T09:01:02"); 

请注意, java.util.Date对象本身不包含任何时区信息 – 您无法在Date对象上设置时区。 Date对象包含的唯一东西是从1970年1月1日00:00:00 UTC开始的“纪元”以来的毫秒数。

正如ZZ编码器显示的那样,您可以在DateFormat对象上设置时区,告诉它在哪个时区显示日期和时间。

您也可以在JVM级别设置时区

 Date date1 = new Date(); System.out.println(date1); TimeZone.setDefault(TimeZone.getTimeZone("UTC")); // or pass in a command line arg: -Duser.timezone="UTC" Date date2 = new Date(); System.out.println(date2); 

输出:

 Thu Sep 05 10:11:12 EDT 2013 Thu Sep 05 14:11:12 UTC 2013 

没有时区

正如其他正确答案所述,java.util.Date没有时区 。 它代表UTC / GMT(没有时区偏移量)。 非常混乱,因为它的toString方法在生成String表示时应用JVM的默认时区。

避免juDate

由于这个和其他许多原因,你应该避免使用内置的java.util.Date&.Calendar&java.text.SimpleDateFormat。 他们是非常麻烦的。

而是使用Java 8捆绑的java.time包 。 这些新课程受JSR 310定义的Joda-Time的启发,并由ThreeTen-Extra项目扩展。 对于Java 6和7,请使用后端项目ThreeTen-Backport 。 对于Android,这个后端端口ThreeTenABP的改写 。 请参阅Oracle教程 。

java.time

java.time类可以用三种方式在时间线上表示一个片刻:

  • UTC( Instant
  • 用偏移量( OffsetDateTimeZoneOffset
  • 随着时区( ZonedDateTimeZoneId

Instant

在java.time中 ,基本构建块是“ Instant ,即UTC中的时间线。 使用Instant对象的大部分业务逻辑。

 Instant instant = Instant.now(); 

OffsetDateTime

应用UTC的偏移量来调整某些地点的挂钟时间 。

应用ZoneOffset获得OffsetDateTime

 ZoneOffset zoneOffset = ZoneOffset.of( "-04:00" ); OffsetDateTime odt = OffsetDateTime.ofInstant( instant , zoneOffset ); 

ZonedDateTime

更好的办法是应用时区 ,偏移加上处理夏令时(DST)等异常的规则。

ZoneId应用于Instant以获取ZonedDateTime 。 总是指定一个适当的时区名称 。 切勿使用3-4既不是独特也不标准的缩写,如ESTIST

 ZoneId zoneId = ZoneId.of( "America/Montreal" ); ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId ); 

格式化的字符串

调用这三个类中的任何一个的toString方法,以标准ISO 8601格式生成一个代表日期时间值的字符串。 ZonedDateTime类通过在括号中附加时区的名称来扩展标准格式。

 String outputInstant = instant.toString(); // Ex: 2011-12-03T10:15:30Z String outputOdt = odt.toString(); // Ex: 2007-12-03T10:15:30+01:00 String outputZdt = zdt.toString(); // Ex: 2007-12-03T10:15:30+01:00[Europe/Paris] 

对于其他格式使用DateTimeFormatter类。 通常最好让这个类使用用户期望的人类语言和文化规范来生成本地化的格式。 或者你可以指定一个特定的格式。


乔达时间

虽然Joda-Time仍然在积极维护,但它的制造商已经告诉我们尽快迁移到java.time。 我把这个部分保留作为参考,但我建议使用上面的java.time部分代替。

在Joda-Time中 ,日期时间对象( DateTime )确实知道指定的时区。 这意味着与UTC 该时区的夏令时(DST)和其他此类异常的规则和历史的偏移。

 String input = "2014-01-02T03:04:05"; DateTimeZone timeZone = DateTimeZone.forID( "Asia/Kolkata" ); DateTime dateTimeIndia = new DateTime( input, timeZone ); DateTime dateTimeUtcGmt = dateTimeIndia.withZone( DateTimeZone.UTC ); 

调用toString方法以生成ISO 8601格式的字符串。

 String output = dateTimeIndia.toString(); 

Joda-Time还提供了丰富的功能来生成各种其他字符串格式。

如果需要,可以将Joda-Time DateTime转换为java.util.Date。

 Java.util.Date date = dateTimeIndia.toDate(); 

搜索StackOverflow中的“joda date”可以找到更多的例子,有些相当详细。


实际上在java.util.Date中嵌入了一个时区,用于某些内部函数(请参阅此答复的评论)。 但是这个内部时区并不作为财产暴露,也不能设置。 此内部时区不是toString方法用于生成日期时间值的字符串表示形式; 相反,JVM的当前默认时区是即时应用的。 所以,作为速记,我们经常说“juDate没有时区”。 混乱? 是。 还有一个原因是要避免这些疲惫的老班。

java.util.Calendar是仅使用JDK类来处理时区的常用方法。 Apache Commons有一些其他的替代品/实用程序可能会有所帮助。 编辑 Spong的笔记提醒我,我已经听说了关于Joda-Time的非常好的事情(尽管我自己并没有使用它)。

如果你只需要使用标准的JDK类,你可以使用这个:

 /** * Converts the given <code>date</code> from the <code>fromTimeZone</code> to the * <code>toTimeZone</code>. Since java.util.Date has does not really store time zome * information, this actually converts the date to the date that it would be in the * other time zone. * @param date * @param fromTimeZone * @param toTimeZone * @return */ public static Date convertTimeZone(Date date, TimeZone fromTimeZone, TimeZone toTimeZone) { long fromTimeZoneOffset = getTimeZoneUTCAndDSTOffset(date, fromTimeZone); long toTimeZoneOffset = getTimeZoneUTCAndDSTOffset(date, toTimeZone); return new Date(date.getTime() + (toTimeZoneOffset - fromTimeZoneOffset)); } /** * Calculates the offset of the <code>timeZone</code> from UTC, factoring in any * additional offset due to the time zone being in daylight savings time as of * the given <code>date</code>. * @param date * @param timeZone * @return */ private static long getTimeZoneUTCAndDSTOffset(Date date, TimeZone timeZone) { long timeZoneDSTOffset = 0; if(timeZone.inDaylightTime(date)) { timeZoneDSTOffset = timeZone.getDSTSavings(); } return timeZone.getRawOffset() + timeZoneDSTOffset; } 

信贷转到这个职位 。

如果有人需要这个,如果你需要从UTC转换一个XMLGregorianCalendar时区到你当前的时区,那么你所需要做的就是将时区设置为0 ,然后调用toGregorianCalendar() – 它将保持相同的时区,但Date知道如何将它转换为你的,所以你可以从那里获取数据。

 XMLGregorianCalendar xmlStartTime = DatatypeFactory.newInstance() .newXMLGregorianCalendar( ((GregorianCalendar)GregorianCalendar.getInstance()); xmlStartTime.setTimezone(0); GregorianCalendar startCalendar = xmlStartTime.toGregorianCalendar(); Date startDate = startCalendar.getTime(); XMLGregorianCalendar xmlStartTime = DatatypeFactory.newInstance() .newXMLGregorianCalendar(startCalendar); xmlStartTime.setHour(startDate.getHours()); xmlStartTime.setDay(startDate.getDate()); xmlStartTime.setMinute(startDate.getMinutes()); xmlStartTime.setMonth(startDate.getMonth()+1); xmlStartTime.setTimezone(-startDate.getTimezoneOffset()); xmlStartTime.setSecond(startDate.getSeconds()); xmlStartTime.setYear(startDate.getYear() + 1900); System.out.println(xmlStartTime.toString()); 

结果:

 2015-08-26T12:02:27.183Z 2015-08-26T14:02:27.183+02:00