我已经从一个String
中解析了一个java.util.Date
对象,但是它将本地时区设置为date
对象的时区。
String
中没有指定时区,我想设置date
对象的特定时区。
我该如何做到这一点?
我已经从一个String
中解析了一个java.util.Date
对象,但是它将本地时区设置为date
对象的时区。
String
中没有指定时区,我想设置date
对象的特定时区。
我该如何做到这一点?
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");
getTime()
之前,您需要使用 TimeZone.setDefault()
将新的日期对象设置为所需的时区。在 JDK 1.8 中,Calendar.getTime()
调用 return new Date(getTimeInMillis());
。 - jpllosa请注意,java.util.Date
对象本身不包含任何时区信息 - 您无法在Date
对象上设置时区。 Date
对象唯一包含的是自"时代"以来的毫秒数-1970年1月1日00:00:00 UTC。
如ZZ Coder所示,您可以在DateFormat
对象上设置时区,以告诉它您要显示日期和时间的时区。
fastTime
的long
字段。实际上,Date.toString()
使用Calendar
来解释这个毫秒时间。因此,打印出一个Date
会让它 看起来 有一个(默认)时区,从而引起人们对如何设置时区的理解上的疑问。 - David Carboni...从字符串中解析出来...没有指定时区...我想设置一个特定的时区。
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.
正如其他正确的答案所述,java.util.Date 没有时区†。它代表 UTC/GMT(没有时区偏移)。这非常令人困惑,因为它的 toString
方法在生成字符串表示时应用了 JVM 的默认时区。
出于这个原因和许多其他原因,您应该避免使用内置的 java.util.Date & .Calendar & java.text.SimpleDateFormat。它们是出了名的麻烦。
相反,请使用随 Java 8 捆绑的 java.time 包。
java.time类可以用三种方式表示时间线上的时刻:
Instant
)ZoneOffset
的OffsetDateTime
)ZoneId
的ZonedDateTime
)Instant
在java.time中,基本构建块是Instant
,它是UTC上的时间线上的一个时刻。在大多数业务逻辑中使用Instant
对象。
Instant instant = Instant.now();
OffsetDateTime
应用ZoneOffset
以获取OffsetDateTime
。
ZoneOffset zoneOffset = ZoneOffset.of( "-04:00" );
OffsetDateTime odt = OffsetDateTime.ofInstant( instant , zoneOffset );
ZonedDateTime
更好的方法是应用时区,一个偏移量加上处理异常情况的规则,例如夏令时(DST)。
将ZoneId
应用到Instant
上,以获取ZonedDateTime
。始终指定正确的时区名称。不要使用3-4个缩写,例如EST
或IST
,它们既不唯一也不标准化。
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
LocalDateTime
如果输入的字符串缺乏时区或偏移量指示符,则解析为LocalDateTime
。
如果您确定所需的时区,请分配一个ZoneId
以生成ZonedDateTime
。请参见上面“tl;dr”部分中的代码示例。
调用这三个类中的任何一个上的toString
方法,以生成表示标准ISO 8601格式的日期时间值的字符串。 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]
DateTimeFormatter
类。通常最好让该类使用用户预期的人类语言和文化规范生成本地化格式。或者您可以指定特定的格式。
java.time 框架是内置于Java 8及更高版本中的。这些类取代了令人头疼的老式legacy日期时间类,例如 java.util.Date
、Calendar
和SimpleDateFormat
。
要了解更多,请参阅Oracle教程。并在Stack Overflow上搜索许多示例和解释。规范是JSR 310。
Joda-Time项目现在处于维护模式,建议迁移到java.time类。
您可以直接使用与JDBC 4.2或更高版本兼容的JDBC驱动程序,无需字符串,也无需java.sql.*
类,即可直接与数据库交换java.time对象。Hibernate 5和JPA 2.2支持java.time。虽然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 );
toString
方法生成一个符合ISO 8601格式的字符串。String output = dateTimeIndia.toString();
Java.util.Date date = dateTimeIndia.toDate();
在StackOverflow上搜索“joda date”,可以找到更多的例子,有些非常详细。
†实际上,在java.util.Date中有一个嵌入的时区,用于一些内部功能(请参见此答案的评论)。但是,这个内部时区不作为属性公开,并且不能设置。这个内部时区不是用于生成日期时间值字符串表示形式的toString
方法所使用的时区;相反,JVM当前的默认时区是即时应用的。因此,简而言之,我们经常说“j.u.Date没有时区”。令人困惑吗?是的。这是避免使用这些老旧类的又一个原因。
BaseCalendar.Date cdate
属性中。可以在这里查看源代码。除非更改JVM的默认时区(通过调用TimeZone.setDefault(TimeZone.getTimeZone("NEW_TIME_ZONE"))
),否则无法设置j.u.Date对象的时区。因此,存在时区偏移量,可以通过调用已弃用的方法j.u.Date.getTimezoneOffset()来获取偏移量。 - Thai BuitoString
方法外,又一次忽略了那个隐藏的时区。因此为了简洁起见,我们说java.util.Date没有时区。就像艺术一样,它是告诉真相的谎言。 - Basil BourqueTimeZone.setDefault
,您不会设置java.util.Date对象的时区--Date对象仍然忽略其嵌入式时区,在UTC中有效地运行。您将影响Date的toString
方法。设置默认值会更改JVM的默认时区,该时区通常设置为主机操作系统的时区。不建议进行此调用,因为它会影响在该JVM中运行的所有应用程序的所有线程中的所有代码,并且在执行时动态进行。由于粗鲁和危险,只有在最后一种情况下才应考虑进行此调用。 - Basil Bourqueequals
,hashcode
,getTime
等方法中使用)。如果您查看equals
方法,它会调用getTime()
方法,该方法又调用getTimeImpl()
方法,如果cdate
属性未被规范化,则调用normalize()
方法。在normalize()
方法中,最后一个if语句会重新计算自 1/1/70 起的毫秒数,如果cdate
的时区与当前JVM环境的时区不同,则基于其存储的时区信息进行计算。(请参考sun.util.calendar.AbstractCalendar getCalendarDate(long millis, CalendarDate date)
) - Thai Bui你还可以在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);
输出:
Thu Sep 05 10:11:12 EDT 2013
Thu Sep 05 14:11:12 UTC 2013
TimeZone.setDefault
是一种相当极端的操作,它会影响整个 JVM,影响所有其他对象和线程。有关详细信息,请参见 此答案,包括如果您正在使用 SecurityManager 运行时甚至更多的复杂情况。更令人复杂的是:这种行为在各个版本的 Java 中都有所改变,如此问题中所讨论的那样。 - Basil BourqueDate
和TimeZone
这两个设计不良且过时的类也是如此。 - Ole V.V.如果你必须只使用标准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;
}
本文翻译参考了这篇帖子。
java.util.Calendar
是使用JDK类处理时区的常见方式。 Apache Commons提供了一些其他的选择/实用工具,可能会有所帮助。 Edit Spong的说明让我想起了我听说过Joda-Time非常好(尽管我自己没有使用过)。
Period
,Duration
和Interval
表示时间间隔。这些时间间隔包括比较方法,如contains
,abuts
,overlap
和gap
。而PeriodFormatterBuilder
可以构建描述性短语,如“15年8个月”。 - Basil Bourque SimpleDateFormat readFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
readFormat.setTimeZone(TimeZone.getTimeZone("GMT" + timezoneOffset));
String dateStr = readFormat.format(date);
SimpleDateFormat writeFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date date = writeFormat.parse(dateStr);
This code was helpful in an app I'm working on:
Instant date = null;
Date sdf = null;
String formatTemplate = "EEE MMM dd yyyy HH:mm:ss";
try {
SimpleDateFormat isoFormat = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss");
isoFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.of("US/Pacific")));
sdf = isoFormat.parse(timeAtWhichToMakeAvailable);
date = sdf.toInstant();
} catch (Exception e) {
System.out.println("did not parse: " + timeAtWhichToMakeAvailable);
}
LOGGER.info("timeAtWhichToMakeAvailable: " + timeAtWhichToMakeAvailable);
LOGGER.info("sdf: " + sdf);
LOGGER.info("parsed to: " + date);
private String transformLocalDateTimeBrazillianUTC(String dateJson) throws ParseException {
String localDateTimeFormat = "yyyy-MM-dd'T'HH:mm:ss";
SimpleDateFormat formatInput = new SimpleDateFormat(localDateTimeFormat);
//Here is will set the time zone
formatInput.setTimeZone(TimeZone.getTimeZone("UTC-03"));
String brazilianFormat = "dd/MMM/yyyy - HH:mm";
SimpleDateFormat formatOutput = new SimpleDateFormat(brazilianFormat);
Date date = formatInput.parse(dateJson);
return formatOutput.format(date);
}
TimeZone.getTimeZone("UTC-03")
返回 GMT。我怀疑这不是你想要的。这不是你的问题,而是 TimeZone
类的问题。我建议我们不要使用 Date
、SimpleDateFormat
或 TimeZone
,而是使用 Java.time,正如 Basil Bourque 的答案 中所解释的那样。 - Ole V.V.11/mar./2020 - 21:16
。请注意,时间是 21:16,这是错误的。我建议使用 LocalDateTime.parse("2020-03-11T20:16:17").atZone(ZoneId.of("America/Bahia"))
。它将输出 2020-03-11T20:16:17-03:00[America/Bahia]
。当然,你可以按照自己的方式进行格式化。为此,请使用 DateTimeFormatter
。 - Ole V.V.package org.example;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class time {
public static void main(String[] args) {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd HH:mm");
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Jakarta"));
Date date=new Date();
sdf.format(date);
System.out.println(sdf.format(date));
}
}
SimpleDateFormat
和 Date
。这些类设计得很差,而且已经过时了,特别是前者非常麻烦。相反,使用来自 java.time,现代 Java 日期和时间 API 的 ZonedDateTime
、ZoneId
和 DateTimeFormatter
。 - Ole V.V.