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

时间:2014-07-17 14:22:40

标签: java date datetime calendar timezone

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

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

我只看到每个人都建议使用Timezone。即,

dateFormatter.setTimezone("any_arbitary_timezone");

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

4 个答案:

答案 0 :(得分:47)

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`.

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

ZonedDateTime.now()          // Capture the current moment in the JVM’s current default time zone.

将您的字符串解析为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.

避免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" ) ;
  

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

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

OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ;
  

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

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

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

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

ZoneId z = ZoneId.systemDefault() ;
ZonedDateTime zdt = odt.atZone( z ) ;
  

点是我的应用程序将在不同的地理位置使用。

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

continent/region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用诸如ESTIST之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

另一个提示:以UTC工作,思考,存储和交换。忘记你自己的狭隘时区,因为来回翻译到你的家乡区域会让你感到沮丧。将UTC视为 One True Time ,其他区域/偏移仅仅是变化。

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 ) ;

我们有四种方式(instantzdtAucklandzdtKolkatazdtCasablanca)查看同一时刻,即时间轴上的相同点。

  

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及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和& SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

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

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore


约达时间

更新: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.

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

DateTime dateTime = DateTime.now( DateTimeZone.getDefault() ); // Explicitly using default time zone.

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

DateTime dateTimeKolkata = DateTime.now( DateTimeZone.forID( "Asia/Kolkata" ) ); // Specify a time zone.

对于服务器端工作,最佳做法是在UTC中进行业务逻辑和数据库存储。

DateTime dateTimeUtc = DateTime.now( DateTimeZone.UTC ); // Assign UTC (GMT) time zone.

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

DateTime dateTime = dateTimeUtc.withZone( DateTimeZone.getDefault() );

不可变

对于线程安全,Joda-Time使用immutable objectswithZone等方法不是修改对象,而是根据原始方法创建新实例。

解析字符串

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

在您的问题中,您说字符串代表UTCGMT)中的日期时间。

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

解析后,您可以根据需要指定另一个时区。宇宙时间线上的相同时刻,但显示不同的Wall-Clock time

DateTime dateTimeDefaultZone = dateTimeUtc.withZone( DateTimeZone.getDefault() );

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

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

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

请注意,此DateTime构造函数与上面的构造函数类似,但实际上却完全不同。这个时区参数在解析后应用,而不是在解析期间应用。这里时区参数用于调整已解析的DateTime。最后Z会让世界变得不同。

默认时区的来源

JVM最初从主机操作系统获取其默认时区。但请注意,程序员可以通过以下方式覆盖它:

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

答案 1 :(得分:10)

以下是获取与您的本地系统时钟偏移相匹配的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);

答案 2 :(得分:1)

output = model(inp) 

答案 3 :(得分:0)

我相信你要找的是Joda库。它的功能比答案here中指定的Calendar或Date类更好。

This function应该特别有用。