在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/Montreal
、Africa/Casablanca
或Pacific/Auckland
。切勿使用 3-4 个字母的缩写,例如 EST
或 IST
,因为它们不是真正的时区,没有标准化,甚至不是唯一的(!)。
另一个提示:在 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 ) ;
我们有四种方式(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 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date
、Calendar
和 SimpleDateFormat
。
要了解更多信息,请参阅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 8、Java SE 9、Java SE 10、Java SE 11 和更高版本 - 具有捆绑实现的标准 Java API 的一部分。 Java 9 带来了一些小功能和修复。 Java SE 6 和 Java 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 的试验场。您可能会在这里找到一些有用的类,例如Interval
、YearWeek
、YearQuarter
和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.Date
、java.util.Calendar
和 java.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中获取当前时区的日期的主要内容,如果未能解决你的问题,请参考以下文章
Calendar日期类详解SimpleDateFormat时区Date夏令时常用方法,日期差获取当前时间