首页 文章

如何设置java.util.Date的时区?

提问于
浏览
168

我已经从 String 解析了 java.util.Date ,但它将本地时区设置为 date 对象的时区 .

时间区域未在 String 中指定,从中解析 Date . 我想设置 date 对象的特定时区 .

我怎样才能做到这一点?

8 回答

  • 144

    使用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");
    
  • 0

    请注意 java.util.Date 对象本身不包含任何时区信息 - 您无法在 Date 对象上设置时区 . Date 对象包含的唯一内容是自"epoch" - 1970年1月1日00:00:00 UTC以来的毫秒数 .

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

  • 259

    tl;博士

    ...解析...来自String ...时区未指定...我想设置一个特定的时区

    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.
    

    j.u.Date中没有时区

    正如其他正确答案所述,java.util.Date没有时区† . 它代表UTC / GMT(没有时区偏移) . 非常混乱,因为它的 toString 方法在生成String表示时应用JVM的默认时区 .

    避免j.u.Date

    出于这个原因和其他许多原因,您应该避免使用内置的java.util.Date和.Calendar&java.text.SimpleDateFormat . 众所周知,它们很麻烦 .

    而是使用与Java 8捆绑在一起的java.time package .

    java.time

    java.time类可以通过三种方式表示时间轴上的时刻:

    • UTC( Instant

    • 带偏移量( OffsetDateTime ,带 ZoneOffset

    • 带时区( ZonedDateTime ,带 ZoneId

    瞬发

    java.time中,基本构建块是Instant,是UTC时间线上的一个时刻 . 将 Instant 对象用于大部分业务逻辑 .

    Instant instant = Instant.now();
    

    OffsetDateTime

    应用offset-from-UTC调整到某个地区的wall-clock time .

    应用ZoneOffset获取OffsetDateTime .

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

    ZonedDateTime

    更好的是应用time zone,偏移加上处理异常的规则,例如Daylight Saving Time (DST) .

    ZoneId应用于 Instant 以获得ZonedDateTime . 始终指定proper time zone name . 切勿使用 ESTIST 等3-4个缩写,既不是唯一的也不是标准化的 .

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

    LocalDateTime

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

    如果您确定预期的时区,请指定 ZoneId 以生成 ZonedDateTime . 请参阅顶部的tl; dr部分中的上面的代码示例 .

    格式化字符串

    在这三个类中的任何一个上调用 toString 方法,以生成一个表示标准ISO 8601格式的日期时间值的String . 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.time框架内置于Java 8及更高版本中 . 这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat .

    Joda-Time项目,现在在maintenance 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的试验场 . 你可能会在这里找到一些有用的类,如IntervalYearWeekYearQuarter,和more .


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

    Joda-Time还提供丰富的功能来生成各种其他String格式 .

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

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

    在“joda date”中搜索StackOverflow以查找更多示例,其中一些非常详细 .


    †实际上java.util.Date中嵌入了一个时区,用于某些内部函数(请参阅本答案中的注释) . 但是此内部时区不作为属性公开,并且无法设置 . 此内部时区不是toString方法在生成日期时间值的字符串表示形式时使用的时区;相反,JVM的当前默认时区是即时应用的 . 所以,作为简写,我们经常说“j.u.Date没有时区” . 混乱?是 . 避免这些疲惫的旧课程的另一个原因 .

  • 62

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

    java.util.Calendar是使用JDK类处理时区的常用方法 . Apache Commons还有一些可能有用的替代品/实用工具 . Edit Spong 's note reminded me that I'听说Joda-Time真的很好吃(虽然我自己没有用过它) .

  • 6

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

    归功于post .

  • 65

    如果有人需要这个,如果你需要将 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
    
  • 6

    将Date转换为String并使用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);
    

相关问题