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

Posted

技术标签:

【中文标题】在Java中获取当前时区的日期【英文标题】:Get date in current timezone in Java 【发布时间】:2014-09-08 11:32:21 【问题描述】:

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

当我使用calendar.getTimezone.getDefaultName()时,它总是返回给我GMT。理想情况下,它应该返回我当前的时区,即IST

我正在尝试将 GMT 中的字符串“2014-02-14T06:04:00:00”转换为我的时区日期时间。它总是在GMT 中同时返回给我。

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

dateFormatter.setTimezone("any_arbitary_timezone");

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

【问题讨论】:

时间为您的本地系统时间。您确定您的机器未设置为 GMT? 是的。使用 Ubuntu 机器。它设置为亚洲/加尔各答.. IST 如果您的电脑确实设置为那个时区 (IST),那么在您的应用程序中的某个地方,时区会以编程方式设置为 GMT 谢谢乔纳森,这正是发生的事情。这是由于时区设置为 GMT 的 env.sh 文件之一。参考:-Duser.timezone=GMT 【参考方案1】:

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 实现在生成字符串时应用了 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。切勿使用 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 ) ;

我们有四种方式(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.DateCalendarSimpleDateFormat

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。

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

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

从哪里获取 java.time 类?

Java SE 8Java SE 9Java SE 10Java SE 11 和更高版本 - 具有捆绑实现的标准 Java API 的一部分。 Java 9 带来了一些小功能和修复。 Java SE 6Java SE 7 大部分 java.time 功能在ThreeTen-Backport 中向后移植到 Java 6 和 7。 Android java.time 类的更高版本的 android (26+) 捆绑实现。 对于早期的 Android (API desugaring 的进程带来了最初未内置于 Android 中的 subset of the java.time 功能。 如果脱糖不能满足您的需求,ThreeTenABP 项目会将ThreeTen-Backport(如上所述)适配到 Android。见How to use ThreeTenABP…

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuarter 和more。


乔达时间

更新: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 objects。 withZone 等方法不是修改对象,而是在原始实例的基础上创建一个新实例。

解析字符串

要将字符串解析为日期时间,您必须注意字符串是否包含与 UTC 和/或时区的偏移量。你的没有。因此,您必须指定解释该字符串的时区。如果不指定,解析时会使用JVM当前的默认时区。

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

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

解析后,如果需要,您可以分配另一个时区。宇宙时间线上的同一时刻,但显示不同的Wall-Clock time。

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

因此请注意,这是一个两步过程。首先,我们使用我们对该字符串的预期时区的外部知识来解析您的字符串,因为它缺少该时区或偏移的内部表示。其次,我们将时区调整为另一个时区(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 时在命令行中传递一个参数。 致电java.util.TimeZone.setDefault

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

【讨论】:

写得真棒!工作就像一个魅力。 哇,多么详尽的答案,Java 中日期和时间 API 的真正指南。谢谢! 完美运行!例如LocalDateTime.parse("2022-02-10T07:00:00").atZone(ZoneId.systemDefault()).toInstant();【参考方案2】:

这是一种获取与本地系统时钟偏移量匹配的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);

【讨论】:

它给出匹配的名字。在我的电脑上,它应该是Eastern Standard Time,但它返回America/Atikokan @JonathanDrapeau 是的。同样在这里。但是,它是东部时间,而不是格林威治标准时间。 @KiranKulkarni 看起来你的电脑没有设置在 IST 中。检查时区here,非洲与GMT相同(0偏移) 发现我的tomcat中有一些shell脚本文件设置为-Duser.timezone=GMT。我接受你的回答。如果有人有兴趣。这是我的问题的链接:***.com/questions/5565052/… 仅供参考,麻烦的旧日期时间类,如 java.util.Datejava.util.Calendarjava.text.SimpleTextFormat 现在是 legacy,被 java.time 类取代。见Tutorial by Oracle。【参考方案3】:
      private String receivedFormat = "yyyy-MM-dd'T'HH:mm:ss", expectedFormat = "dd-MM-yyyy HH:mm:ss"; //Globall variables

        //Write these three lines in your Test Class and below 2 methods

        String dateString = "2018-08-14T07:00:00:00";
        String returnString = correctDateFormat(dateString, receivedFormat, expectedFormat);
        String result = getTimeInSelectedLocale(returnString);
    Log.i("Ayaz", "Date: " +result);



     /**
         * @param receivedDate
         * @param givenFormat
         * @param expectedFormat
         * @return returns date time in expected format
         */
        public static String correctDateFormat(String receivedDate, String givenFormat, String expectedFormat) 
            if (TextUtils.isEmpty(receivedDate)) 
                return "";
            
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(givenFormat);
            Date newDate = null;
            try 
                newDate = simpleDateFormat.parse(receivedDate);
             catch (ParseException e) 
                e.printStackTrace();
            
            simpleDateFormat = new SimpleDateFormat(expectedFormat);
            receivedDate = simpleDateFormat.format(newDate);
            return receivedDate;
        



        /**
         * @param dateString
         * @return passed string date in different locale, My Db is in IST so I an converting IST in different locale
         */
        public  String getTimeInSelectedLocale(String dateString) 
            if (TextUtils.isEmpty(dateString)) 
                return dateString;
            
            SimpleDateFormat sdf = new SimpleDateFormat(expectedFormat);
            sdf.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata")); //We want Indian time to change in different locale, so this line is compulsory, you can replace with your country
            Date date1 = null;
            try 
                date1 = sdf.parse(dateString);
             catch (ParseException e) 
                e.printStackTrace();
            
            //below this line -> TimeZone.getTimeZone(TimeZone.getDefault().getID(), will return current locale for example for India "Asia/Kolkata" for UAE "Asia/Dubai"
            sdf.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getID())); //This line chooses current local in which you want time
            String localDate = sdf.format(date1);
            return localDate;
        

//I am converting the IST time "2018-08-14T07:00:00:00"  into UAE(Duabai) "14-08-2018 05:30:00" and other countries

【讨论】:

【参考方案4】:

我相信您正在寻找的是Joda 库。它具有比答案here 中指定的日历或日期类更好的功能。

This function 应该特别有用。

【讨论】:

仅供参考,Joda-Time 项目现在位于maintenance mode,团队建议迁移到java.time 类。见Tutorial by Oracle。

以上是关于在Java中获取当前时区的日期的主要内容,如果未能解决你的问题,请参考以下文章

获取时区的当前日期对象[重复]

如何在javascript中获取时区的当前日期

Calendar日期类详解SimpleDateFormat时区Date夏令时常用方法,日期差获取当前时间

如何在 Python 中获取或打印 GMT 时区的当前日期时间? [复制]

如何在 Swift 中获取另一个时区的当前日期和时间?

Java从unix时间戳获取当前日期[重复]