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

Yat*_*oel 205 java timezone date java.util.date

我已经解析了java.util.Datea,String但是它将本地时区设置为date对象的时区.

时区没有指定String从中Date解析.我想设置date对象的特定时区.

我怎样才能做到这一点?

ZZ *_*der 293

使用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");
Run Code Online (Sandbox Code Playgroud)

  • @ lwpro2该陈述具有误导性; 您可以为Calendar对象设置时区,但使用getTime()方法从中获取Date对象将返回带有主机计算机时区的Date对象. (17认同)
  • 如果日期是从Calendar类创建的,则可以设置Calendar的时区. (6认同)
  • @BrDaHa 是正确的。在调用 `getTime()` 之前,您需要 `TimeZone.setDefault()` ,以便新的日期对象位于您想要的时区。在 JDK 1.8 中,“Calendar.getTime()”调用“return new Date(getTimeInMillis());”。 (2认同)

Jes*_*per 168

请注意,java.util.Date对象本身不包含任何时区信息 - 您无法在Date对象上设置时区.Date对象包含的唯一内容是自"epoch" - 1970年1月1日00:00:00 UTC以来的毫秒数.

正如ZZ Coder所示,您可以在DateFormat对象上设置时区,告诉它您要在哪个时区显示日期和时间.

  • 在谷歌搜索,实验和删除之后,我意识到这是对答案的精确和有用的补充 - 值得强调:**日期_only_包含毫秒值**.如果你看一下源代码,那里几乎就是一个名为`fastTime`的`long`字段.`Date.toString()`实际上使用`Calendar`来解释这个毫秒时间.所以打印出一个"Date"会使_appear_有一个(默认的)时区,从而导致有关如何设置该时区的可理解的问题. (71认同)
  • @Jim,[this](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/Date.java)是java的源代码. util.Date.它包含fastTime作为unix时期以及用于支持fastTime的BaseCalendar.Date cdate(如果已定义).那个包含时区信息.我理解它,以便Date实例可以包含时区信息,但它可能不包含. (6认同)
  • Date对象中有时区信息.但它可能是真的,你无法改变它. (3认同)
  • @ Iwpro2,Jesper声称(我同意)Date对象不存储时区.如果您声称时区存储在java.util.Date中,请提供参考. (3认同)
  • @Jim我没有说你可以设置它,我说它包含这些信息.我也没有看到任何将其设置为用户的方法.我认为OP是正确的,因为它似乎总是用户/系统默认时区. (2认同)

Bas*_*que 78

TL;博士

...解析...从字符串...时区未指定...我想设置一个特定的时区

LocalDateTime.parse( "2018-01-23T01:23:45.123456789" )  // Parse string, lacking an offset-from-UTC and lacking a time zone, as a `LocalDateTime`.
    .atZone( ZoneId.of( "Africa/Tunis" ) )              // Assign the time zone for which you are certain this date-time was intended. Instantiates a `ZonedDateTime` object.
Run Code Online (Sandbox Code Playgroud)

juDate没有时区

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

避免juDate

出于这个原因和其他许多原因,您应该避免使用内置的java.util.Date和.Calendar&java.text.SimpleDateFormat.众所周知,它们很麻烦.

而是使用与Java 8捆绑在一起的java.time包.

java.time

java.time类可以通过三种方式表示时间轴上的时刻:

  • UTC(Instant)
  • 带偏移量(OffsetDateTimeZoneOffset)
  • 带时区(ZonedDateTimeZoneId)

Instant

java.time中,基本构建块是InstantUTC时间线上的一个时刻.将Instant对象用于大部分业务逻辑.

Instant instant = Instant.now();
Run Code Online (Sandbox Code Playgroud)

OffsetDateTime

应用UTC偏移量以调整到某个地区的挂钟时间.

申请一个ZoneOffset来获得一个OffsetDateTime.

ZoneOffset zoneOffset = ZoneOffset.of( "-04:00" );
OffsetDateTime odt = OffsetDateTime.ofInstant( instant , zoneOffset );
Run Code Online (Sandbox Code Playgroud)

ZonedDateTime

更好的是应用时区,偏移量以及处理夏令时(DST)等异常的规则.

申请一个ZoneIdInstant获得一个ZonedDateTime.始终指定适当的时区名称.切勿使用3-4的缩写,如ESTIST既不是唯一的也不是标准化的.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
Run Code Online (Sandbox Code Playgroud)

LocalDateTime

如果输入字符串缺少偏移量或区域的任何指示符,则解析为a LocalDateTime.

如果您确定预期的时区,请指定a ZoneId来生成a ZonedDateTime.请参阅顶部的tl; dr部分中的上面的代码示例.

格式化字符串

toString在这三个类中的任何一个上调用方法以生成表示标准ISO 8601格式的日期时间值的String .该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]
Run Code Online (Sandbox Code Playgroud)

对于其他格式,请使用DateTimeFormatter该类.通常最好让该类使用用户期望的人类语言和文化规范生成本地化格式.或者您可以指定特定格式.


关于java.time

java.time框架是建立在Java 8和更高版本.这些类取代麻烦的老传统日期时间类,如java.util.Date,Calendar,和SimpleDateFormat.

现在处于维护模式Joda-Time项目建议迁移到java.time类.

要了解更多信息,请参阅Oracle教程.并搜索Stack Overflow以获取许多示例和解释.规范是JSR 310.

您可以直接与数据库交换java.time对象.使用符合JDBC 4.2或更高版本的JDBC驱动程序.不需要字符串,不需要课程.java.sql.*

从哪里获取java.time类?

ThreeTen-额外项目与其他类扩展java.time.该项目是未来可能添加到java.time的试验场.您可以在此比如找到一些有用的类Interval,YearWeek,YearQuarter,和更多.


乔达时间

虽然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 );
Run Code Online (Sandbox Code Playgroud)

调用该toString方法以生成ISO 8601格式的String .

String output = dateTimeIndia.toString();
Run Code Online (Sandbox Code Playgroud)

Joda-Time还提供丰富的功能来生成各种其他String格式.

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

Java.util.Date date = dateTimeIndia.toDate();
Run Code Online (Sandbox Code Playgroud)

在"joda date"中搜索StackOverflow以查找更多示例,其中一些非常详细.


其实有嵌入在java.util.Date一个时区,用于一些内部功能(请参阅本答案的评论).但是此内部时区不作为属性公开,并且无法设置.这个内部时区是被使用的一个toString在生成日期时间值的字符串表示方法; 相反,JVM的当前默认时区是即时应用的.因此,作为简写,我们经常说"juDate没有时区".混乱?是.避免这些疲惫的旧课程的另一个原因.

  • 该时区经常被使用(用于`equals`,`hashcode`,`getTime` ..)如果你看一下`equals`方法,它会调用`getTime()`来调用`getTimeImpl()` ,如果`cdate`属性未规范化,则调用`normalize()`.在`normalize()`方法中,如果`cdate`的时区与它运行的当前JVM环境的时区不同,则最后一个if条件根据其存储的时区信息重新计算自1/1/70以来的毫秒数. .(看看`sun.util.calendar.AbstractCalendar getCalendarDate(long millis,CalendarDate date)`) (5认同)
  • @blquythai正确,你做了你的功课.和我一样,之前看过那个源代码.那里*是*埋在那里的时区.但是出于所有实际目的,时区被忽略了.java.util.Date在没有任何时区的情况下工作,实际上是UTC,而忽略了隐藏的时区.除了应用JVM当前默认时区的`toString`方法之外; 再次无视埋藏的时区.所以为简洁起见,我们说java.util.Date没有时区.[喜欢艺术](https://www.goodreads.com/quotes/67884-we-all-know-that-art-is-not-truth-art-is),说谎是谎言. (3认同)
  • "juDate没有时区"是错误的.如果设置,juDate中的时区信息存储在其"BaseCalendar.Date cdate"属性中.看一下源代码[这里](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/util/Date.java).您不能设置juDate对象的时区,除非通过调用`TimeZone.setDefault(TimeZone.getTimeZone("NEW_TIME_ZONE"))更改JVM的默认时区;`.因此,存在时区偏移量,您可以通过调用弃用方法juDate.getTimezoneOffset()来获取偏移量 (2认同)
  • @MichalM根据你的建议,前段时间我添加了关于嵌入式区域的后记. (2认同)

小智 71

您还可以在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);
Run Code Online (Sandbox Code Playgroud)

输出:

Thu Sep 05 10:11:12 EDT 2013
Thu Sep 05 14:11:12 UTC 2013
Run Code Online (Sandbox Code Playgroud)

  • 注意:调用`TimeZone.setDefault`是相当激烈的,因为它影响整个JVM,影响所有其他对象和线程.有关详细信息,请参阅[此答案](http://stackoverflow.com/a/9891971/642706),如果您使用的是SecurityManager,则会出现更多复杂情况.添加更多复杂性:在[本问题](http://stackoverflow.com/q/2176784/642706)中讨论过,此行为在各种Java版本中已更改. (21认同)

小智 9

如果必须只使用标准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;
}
Run Code Online (Sandbox Code Playgroud)

信用转到这篇文章.

  • 时区的偏移量并不总是恒定的。事实上,它们可能会因地缘政治原因而改变。TimeZone.getDSTSavings() 没有考虑到这一点,并且总是返回 _current_ 偏移量。这意味着,如果您正在处理具有 fromTimeZone/toTimeZone 的历史日期,而该日期的偏移量自该日期起已更改,则您可能会得到错误的转换。 (4认同)

T.J*_*der 6

java.util.Calendar是使用JDK类处理时区的常用方法.Apache Commons还有一些可能有用的替代/实用工具.编辑 Spong的笔记提醒我,我听说Joda-Time真的很棒(虽然我自己没有用过它).

  • @JoshuaHutchison Joda-Time拥有*吨*的附加功能.示例:使用类"Period","Duration"和[`Interval`]表示时间跨度(http://www.joda.org/joda-time/apidocs/org/joda/time/Interval.html) .这些跨度包括比较方法,如`contains`,`abuts`,`overlap`和`gap`.并且[`PeriodFormatterBuilder`](http://www.joda.org/joda-time/apidocs/org/joda/time/format/PeriodFormatterBuilder.html)可以构建描述性短语,例如"15年和8个月". (2认同)