我从一个字符串中解析了一个java.util.Date,但它将本地时区设置为date对象的时区。

在解析Date的字符串中没有指定时区。我想设置date对象的特定时区。

我该怎么做呢?


calendar是使用JDK类处理时区的常用方法。Apache Commons还有一些可能有用的替代方案/实用程序。Edit Spong的留言提醒我,我听说Joda-Time真的很不错(尽管我自己没有用过)。


DateFormat使用。例如,

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

请注意,java.util.Date对象本身不包含任何时区信息—您不能在Date对象上设置时区。Date对象所包含的唯一内容是从“epoch”开始的毫秒数——1970年1月1日00:00:00 UTC。

如ZZ Coder所示,您可以在DateFormat对象上设置时区,以告诉它您希望在哪个时区显示日期和时间。


您还可以在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

博士tl;

time zone is not specified .我想设置一个特定的时区

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方法在生成String表示时应用JVM的默认时区。

避免j.u.Date

出于这个原因和许多其他原因,您应该避免使用内置的java.util.Date & . calendar & java.text.SimpleDateFormat。它们是出了名的麻烦。

相反,请使用java。Java 8捆绑的时间包。

java.time

java。时间类可以用三种方式表示时间轴上的某个时刻:

UTC(即时) 使用偏移量(OffsetDateTime With ZoneOffset) 带时区(zoneeddatetime With ZoneId)

即时

在java中。时间,基本的构建块是即时,在UTC时间线上的一个时刻。在大部分业务逻辑中使用Instant对象。

Instant instant = Instant.now();

OffsetDateTime

应用utc的偏移量来调整到某个地方的时钟时间。

应用一个ZoneOffset来获得一个OffsetDateTime。

ZoneOffset zoneOffset = ZoneOffset.of( "-04:00" );
OffsetDateTime odt = OffsetDateTime.ofInstant( instant , zoneOffset );

分区日期时间

更好的方法是应用一个时区,一个偏移量加上处理异常的规则,如日光节约时间(DST)。

应用一个ZoneId到一个即时,以获得一个zoneeddatetime。始终指定正确的时区名称。不要使用3-4个既不独特也不标准化的缩写词,如EST或IST。

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

LocalDateTime

如果输入字符串缺少任何偏移量或区域指示符,则解析为LocalDateTime。

如果您确定预期的时区,则分配一个ZoneId来生成一个zoneeddatetime。参见上面tl;dr部分的代码示例。

格式化的字符串

对这三个类中的任何一个调用toString方法,以生成以标准ISO 8601格式表示日期-时间值的String。zoneeddatetime类通过在括号中添加时区名称扩展了标准格式。

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。时间框架内置于Java 8及更高版本中。这些类取代了麻烦的旧遗留日期-时间类,如java.util。日期,日历和简单日期格式。

要了解更多,请参阅Oracle教程。搜索Stack Overflow可以找到很多例子和解释。规范是JSR 310。

Joda-Time项目现在处于维护模式,建议迁移到java。时间类。

你可以交换java。Time对象直接使用数据库。使用符合JDBC 4.2或更高版本的JDBC驱动程序。不需要字符串,不需要java。sql。*类。Hibernate 5和JPA 2.2支持java.time。

从哪里获取java。时间类?

Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation. Java 9 brought some minor features and fixes. Java SE 6 and Java SE 7 Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport. Android Later versions of Android (26+) bundle implementations of the java.time classes. For earlier Android (<26), a process known as API desugaring brings a subset of the java.time functionality not originally built into Android. If the desugaring does not offer what you need, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above) to Android. See How to use ThreeTenABP….


乔达时间

虽然Joda-Time仍在积极维护,但它的制造商已经告诉我们迁移到java。时间越方便越好。我保留这一节作为参考,但我建议使用java。时间部分改为上面。

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

Joda-Time还提供了生成各种其他字符串格式的丰富功能。

如果需要,您可以将Joda-Time DateTime转换为java.util.Date。

Java.util.Date date = dateTimeIndia.toDate();

在StackOverflow中搜索“joda date”可以找到更多的例子,其中一些非常详细。


†Actually there is a time zone embedded in a java.util.Date, used for some internal functions (see comments on this Answer). But this internal time zone is not exposed as a property, and cannot be set. This internal time zone is not the one used by the toString method in generating a string representation of the date-time value; instead the JVM’s current default time zone is applied on-the-fly. So, as shorthand, we often say “j.u.Date has no time zone”. Confusing? Yes. Yet another reason to avoid these tired old classes.


如果你必须只使用标准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;
}

功劳归于这篇文章。


如果有人需要这样做,如果您需要将XMLGregorianCalendar时区从UTC转换为当前时区,那么您所需要做的就是将时区设置为0,然后调用toGregorianCalendar()—它将保持相同的时区,但Date知道如何将其转换为您的时区,因此您可以从那里获得数据。

XMLGregorianCalendar xmlStartTime = DatatypeFactory.newInstance()
    .newXMLGregorianCalendar(
        ((GregorianCalendar)GregorianCalendar.getInstance());
xmlStartTime.setTimezone(0);
GregorianCalendar startCalendar = xmlStartTime.toGregorianCalendar();
Date startDate = startCalendar.getTime();
XMLGregorianCalendar xmlStartTime = DatatypeFactory.newInstance()
    .newXMLGregorianCalendar(startCalendar);
xmlStartTime.setHour(startDate.getHours());
xmlStartTime.setDay(startDate.getDate());
xmlStartTime.setMinute(startDate.getMinutes());
xmlStartTime.setMonth(startDate.getMonth()+1);
xmlStartTime.setTimezone(-startDate.getTimezoneOffset());
xmlStartTime.setSecond(startDate.getSeconds());
xmlStartTime.setYear(startDate.getYear() + 1900);
System.out.println(xmlStartTime.toString());

结果:

2015-08-26T12:02:27.183Z
2015-08-26T14:02:27.183+02:00

将日期转换为字符串,并使用SimpleDateFormat。

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

这段代码在我正在开发的应用程序中很有帮助:

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

在这里,您可以获得日期如“2020-03-11T20:16:17”并返回“11/Mar/2020 - 20:16”

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

这个答案可能是最短的,它只使用Date类:

long current = new Date().getTime() + 3_600_000;              //e.g. your JVM time zone +1 hour (3600000 milliseconds)
System.out.printf("%1$td.%1$tm.%1$tY %1$tH:%1$tM\n", current);//european time format

但是,如果可以的话,用更现代的方式来做同样的事情。


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