在java中获取当前时区的日期

Kir*_*rni 29 java timezone datetime calendar date

我过去几个小时一直在网上搜索,以获得系统时区的日期时间.

当我使用calendar.getTimezone.getDefaultName时,它总是返回GMT.(理想情况下它应该返回我当前的时区,即IST)我正在尝试将此字符串"2014-02-14T06:04:00:00"转换为我的时区日期时间.它总是在GMT中同时返回我.

我只看到每个人都建议使用时区.即

dateFormatter.setTimezone("any_arbitary_timezone");
Run Code Online (Sandbox Code Playgroud)

Point是我的应用程序将用于不同的地理位置.我无法将其设置为特定时区.它应设置为系统时区,以便它可以显示在用户当前所在的任何时区

Bas*_*que 54

TL;博士

使用现代java.time类.

ZonedDateTime.now(           // Capture the current moment in the wall-clock time used by the people of a certain region (a time zone).
    ZoneId.systemDefault()   // Get the JVM’s current default time zone. Can change at any moment during runtime. If important, confirm with the user.
)                            // Renders a `ZonedDateTime` object. To see the same moment in UTC, extract a `Instant` object by calling `ZonedDateTime::getInstant`.
Run Code Online (Sandbox Code Playgroud)

您可以省略显式调用ZoneId.systemDefault.

ZonedDateTime.now()          // Capture the current moment in the JVM’s current default time zone.
Run Code Online (Sandbox Code Playgroud)

将您的字符串解析为a LocalDateTime,并调整到所需的时区.

LocalDateTime.parse( "2014-02-14T06:04:00:00" )    // Parse a string lacking any indicator of time zone or offset-from-UTC. *Not* a specific point on the timeline.
             .atOffset( ZoneOffset.UTC )           // Apply UTC as we are certain that offset-from-UTC of zero was intended by the supplier of that input string. Returns a `OffsetDateTime` object.
             .atZoneSameInstant(                   // Adjust into another time zone. The `sameInstant` part means the same moment, different wall-clock time. 
                 ZoneId.of( "Africa/Tunis" )       // Specify the particular zone of interest to you.
             )                                     // Returns a `ZonedDateTime` object.
Run Code Online (Sandbox Code Playgroud)

避免java.util.Date&.Calendar

众所周知,这些遗留类很麻烦.Sun/Oracle 在Java 8中添加了java.time包以取代它们.该套餐的灵感来自Joda-Time.

遗留类的问题包括这种令人困惑的行为:虽然java.util.Date没有时区信息,但它的toString实现在生成String时应用JVM的当前默认时区.因此,当它没有时,它似乎有一个时区误导你.

java.time

我想把这个字符串转换为"2014-02-14T06:04:00:00",...

您的输入字符串缺少任何时区指示符或与UTC的偏移量.所以我们解析为LocalDateTime缺少任何区域/偏移的概念.

一个LocalDateTime不会表示一下,是不是在时间轴上的一个点.这里的"本地"一词并不代表一个特定的地方.这意味着"根本没有特定的地方".没有区域/偏移的上下文,它没有实际意义.

LocalDateTime ldt = LocalDateTime.parse( "2014-02-14T06:04:00:00" ) ;
Run Code Online (Sandbox Code Playgroud)

......这是格林威治标准时间......

您说您确定该输入字符串的供应商将UTC作为上下文.我们可以应用从零开始的UTC偏移量或UTC本身来获取OffsetDateTime对象.An OffsetDateTime 时刻,是时间轴上的一个点.我们可以指定ZoneOffset使用UTC的常量ZoneOffset.UTC.

OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ;
Run Code Online (Sandbox Code Playgroud)

...到我的时区日期时间

显然,您希望将该时刻调整到另一个时区,以查看特定地区人员使用的挂钟时间.我们需要应用时区(ZoneId)来获得ZonedDateTime.

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = odt.atZone( z ) ;
Run Code Online (Sandbox Code Playgroud)

您可以向JVM询问其当前的默认时区,而不是指定时区.注意:JVM当前的默认时区可以随时由该JVM中任何应用程序的任何线程中的任何代码更改.

ZoneId z = ZoneId.systemDefault() ;
ZonedDateTime zdt = odt.atZone( z ) ;
Run Code Online (Sandbox Code Playgroud)

Point是我的应用程序将用于不同的地理位置.

只需明确指定所需/预期的时区即可.在我看来,这总是很好的做法.作为程序员,默认时区位于您的控制之外,这使得它不可靠.

指定适当的时区名称,格式continent/region,如America/Montreal,Africa/CasablancaPacific/Auckland.切勿使用3-4字母缩写,例如ESTIST因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!).

另一个提示:以UTC工作,思考,存储和交换.忘记你自己的狭隘时区,因为来回翻译到你家的区域会让你变得邋..将UTC视为单一真实时间,其他区域/偏移仅仅是变化.

Instant instant = Instant.now() ;  // Capture current moment in UTC.

ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdtAuckland = instant.atZone( zAuckland ) ;

ZoneId zKolkata = ZoneId.of( "Asia/Kolkata" ) ;  
ZonedDateTime zdtKolkata = instant.atZone( zKolkata ) ;

ZoneId zCasablanca = ZoneId.of( "Africa/Casablanca" ) ;  
ZonedDateTime zdtCasablanca = instant.atZone( zCasablanca ) ;
Run Code Online (Sandbox Code Playgroud)

在那里,我们有四种方式(instant,zdtAuckland,zdtKolkata,zdtCasablanca看着非常相同的同时时刻,线上的相同点).

instant.toString():2018-05-08T20:55:14.761721Z

zdtAuckland.toString():2018-05-09T08:55:14.761721 + 12:00 [太平洋/奥克兰]

zdtKolkata.toString():2018-05-09T02:25:14.761721 + 05:30 [亚洲/加尔各答]

zdtCasablanca.toString():2018-05-08T21:55:14.761721 + 01:00 [非洲/卡萨布兰卡]

区域与偏移

与UTC的偏移量只是小时数,分钟数和秒数.没有更多,没有更少.任何数量的时区可以在特定时刻共享特定偏移.

时区是特定地区人民使用的偏移的过去,现在和将来变化的历史.例如,夏令时(DST)是一个地区的人(莫名其妙地)决定每年两次更改其偏移量的做法.

因此,时区总是优于仅仅偏移.拥有一个区域允许我们以有意义的方式添加或减去时间,以考虑该区域历史中偏移的变化.


关于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软件包对时区有明确的支持.与java.util.Date不同,Joda-Time DateTime确实知道自己指定的时区.如果未指定时区,则会隐式分配JVM的当前默认时区.

DateTime dateTime = DateTime.now(); // Applies JVM’s default time zone implicitly.
Run Code Online (Sandbox Code Playgroud)

我建议不要隐式依赖默认时区.这样做会导致日期工作时出现混淆和错误.

DateTime dateTime = DateTime.now( DateTimeZone.getDefault() ); // Explicitly using default time zone.
Run Code Online (Sandbox Code Playgroud)

如果需要,您可以指定时区.

DateTime dateTimeKolkata = DateTime.now( DateTimeZone.forID( "Asia/Kolkata" ) ); // Specify a time zone.
Run Code Online (Sandbox Code Playgroud)

对于服务器端工作,最佳实践是以UTC格式进行业务逻辑和数据库存储.

DateTime dateTimeUtc = DateTime.now( DateTimeZone.UTC ); // Assign UTC (GMT) time zone.
Run Code Online (Sandbox Code Playgroud)

您可以从指定的时区转换为另一个时区,包括JVM的当前默认时区.

DateTime dateTime = dateTimeUtc.withZone( DateTimeZone.getDefault() );
Run Code Online (Sandbox Code Playgroud)

不可变

对于线程安全,Joda-Time使用不可变对象.而不是修改对象,方法,如withZone基于原始创建新实例.

解析字符串

要将String解析为DateTime,必须注意String是否包含与UTC和/或时区的偏移量.你的没有.因此,您必须指定用于解释该String的时区.如果未指定,则在解析期间将使用JVM的当前默认时区.

在您的问题中,您说String表示UTC(GMT)的日期时间.

DateTime dateTimeUtc = new DateTime( "2014-02-14T06:04:00:00", DateTimeZone.UTC );
Run Code Online (Sandbox Code Playgroud)

解析后,您可以根据需要分配另一个时区.宇宙时间线上的相同时刻,但显示不同的壁钟时间.

DateTime dateTimeDefaultZone = dateTimeUtc.withZone( DateTimeZone.getDefault() );
Run Code Online (Sandbox Code Playgroud)

请注意,这是一个两步过程.首先,我们使用String的预期时区的外部知识来解析您的String,因为它缺少该时区或偏移的内部表示.其次,我们将时区调整为另一个(JVM默认区域).

如果您的字符串包含偏移量+00:00或惯例Z,我们可以将这两个步骤合并为一个.

DateTime dateTimeDefaultZone = new DateTime(  "2014-02-14T06:04:00:00Z", DateTimeZone.getDefault() ); // Apply time zone adjustment *after* parsing.
Run Code Online (Sandbox Code Playgroud)

请注意,此DateTime构造函数与上面的构造函数类似,但实际上却完全不同.解析应用此时区参数,而不是解析期间应用.这里时区参数用于调整已解析的DateTime.这Z最终会带来一个与众不同的世界.

默认时区的来源

JVM最初获得它的缺省时区从主机操作系统.但要注意程序员可以通过以下方式覆盖它:

执行此覆盖会影响在该JVM中运行的所有应用程序的所有线程.因此,您应该知道JVM的默认时区通常与主机操作系统相同,但不一定相同.


Ell*_*sch 12

这是一种获取TimeZone与本地系统时钟偏移相匹配的id的方法,

Calendar cal = Calendar.getInstance();
long milliDiff = cal.get(Calendar.ZONE_OFFSET);
// Got local offset, now loop through available timezone id(s).
String [] ids = TimeZone.getAvailableIDs();
String name = null;
for (String id : ids) {
  TimeZone tz = TimeZone.getTimeZone(id);
  if (tz.getRawOffset() == milliDiff) {
    // Found a match.
    name = id;
    break;
  }
}
System.out.println(name);
Run Code Online (Sandbox Code Playgroud)

  • 发现我的tomcat中有一些shell脚本文件正在设置-Duser.timezone = GMT.我接受你的回答.如果有人感兴趣.这是我的问题的链接:http://stackoverflow.com/questions/5565052/how-can-i-change-default-timezone-in-liferay-portal-5-2 (2认同)