如何使用DateTimeFormatter可移植格式化java.util.Date?

时间:2017-04-20 18:43:21

标签: java date localdate

如何使用java.util.Date便携式格式化DateTimeFormatter

我无法使用

Date in = readMyDateFrom3rdPartySource();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
ldt.format(dateTimeFormatter);

因为我害怕使用ZoneId.systemDefault()会引入一些变化。

我需要准确格式化我拥有的对象。

更新

注意:时间就是时间。不是空间。时区是经度(即空间)的非常粗略的度量。我不需要它。只有时间(和日期)。

更新2

我编写了以下程序,证明Date不仅包含正确的"即时":

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DataNature2 {

   public static void main(String[] args) throws ParseException {

      SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

      String dateTimeString = "1970-01-01 00:00:01";

      Date date = simpleDateFormat.parse(dateTimeString);

      System.out.println("1 second = " + date.getTime());

   }
}

输出如下:

1 second = -10799000

虽然应该

1 second = 1000

如果Date是"即时"。

数字107990003*60*60*1000-1000 - 我当地时间的时区偏移量。

这意味着,Date类是双重的。它的毫秒部分可能会被时区偏移相对移动hh mm ss部分。

这意味着,如果任何实用程序根据它的部分(Date)返回hh mm ss个对象,则它会隐式转换为本地时间。 getTime()同时意味着不同的时间。我的意思是在不同的机器上,如果这个程序同时运行,getTime()将是相同的,而时间部分将是不同的。

所以,开头的代码示例是正确的:它需要" instant" Date的一部分,并提供系统时区部分,该部分隐含在Date中使用。即它将双Date个对象转换为具有相同部分的显式LocalDateTime对象。因此,之后的格式是正确的。

更新3

活动更有趣:

Date date = new Date(70, 0, 1, 0, 0, 1);
assertEquals(1000, date.getTime());

此测试失败。

UDPATE 4

新代码。献给所有信徒。

public class DataNature3 {

   public static class TZ extends java.util.TimeZone {


      private int offsetMillis;

      public TZ(int offsetHours) {
         this.offsetMillis = offsetHours * 60 * 60 * 1000;
      }

      @Override
      public int getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds) {
         throw new UnsupportedOperationException();
      }

      @Override
      public void setRawOffset(int offsetMillis) {
         this.offsetMillis = offsetMillis;
      }

      @Override
      public int getRawOffset() {
         return offsetMillis;
      }

      @Override
      public boolean useDaylightTime() {
         return false;
      }

      @Override
      public boolean inDaylightTime(Date date) {
         return false;
      }
   }

   public static void main(String[] args) {

      Date date = new Date(0);

      for(int i=0; i<10; ++i) {

         TimeZone.setDefault(new TZ(i));

         if( i<5 ) {
            System.out.println("I am date, I am an instant, I am immutable, my hours property is " + date.getHours() + ", Amen!");
         }
         else {
            System.out.println("WTF!? My hours property is now " + date.getHours() + " and changing! But I AM AN INSTANT! I AM IMMUTABLE!");
         }

      }

      System.out.println("Oh, please, don't do that, this is deprecated!");

   }
}

输出:

I am date, I am an instant, I am immutable, my hours property is 0, Amen!
I am date, I am an instant, I am immutable, my hours property is 1, Amen!
I am date, I am an instant, I am immutable, my hours property is 2, Amen!
I am date, I am an instant, I am immutable, my hours property is 3, Amen!
I am date, I am an instant, I am immutable, my hours property is 4, Amen!
WTF!? My hours property is now 5 and changing! But I AM AN INSTANT! I AM IMMUTABLE!
WTF!? My hours property is now 6 and changing! But I AM AN INSTANT! I AM IMMUTABLE!
WTF!? My hours property is now 7 and changing! But I AM AN INSTANT! I AM IMMUTABLE!
WTF!? My hours property is now 8 and changing! But I AM AN INSTANT! I AM IMMUTABLE!
WTF!? My hours property is now 9 and changing! But I AM AN INSTANT! I AM IMMUTABLE!
Oh, please, don't do that, this is deprecated!

2 个答案:

答案 0 :(得分:33)

TL; DR:您关注系统本地时区的使用是正确的,但是当您使用系统本地时区构建{{{ 1}}首先。

如果您只希望格式化的字符串具有DateDate.getDate()Date.getMonth()等返回的相同组件,那么原始代码是合适的:< / p>

Date.getYear()

你说你害怕使用LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault()); 会引入一些变化&#34; - 但这恰恰是ZoneId.systemDefault()等使用的。

Date.getDate()没有任何&#34;双重合同&#34;这使您可以将其视为无时区表示。 只是一个瞬间。几乎所有允许您构建或解构为组件的方法都可以清楚地记录下来,使用系统默认时区,就像使用Date一样。 (一个值得注意的例外是ZoneId.systemDefault()方法。)

隐式使用系统默认时区UTC是一个有效的无时区表示相同,并且很容易证明原因:它可以很容易丢失数据。考虑&#34; 2017年3月26日,凌晨1:30和#34;的无时区日期和时间。您可能希望能够对其进行文本表示,解析它,然后再重新格式化。如果你在欧洲/伦敦时区这样做,你将遇到问题,如下所示:

Date

输出结果为2017-03-26T02:30。并不是代码中出现了一个错误的错误 - 如果你将其更改为显示在上午9:30,那就可以正常工作了。

问题是2017-03-26T01:30 由于DST而在欧洲/伦敦时区不存在 - 凌晨1点,时钟跳到凌晨2点。

因此,如果您对这种破碎感到满意,那么请确保使用import java.util.*; import java.time.*; import java.time.format.*; public class Test { public static void main(String[] args) { TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Date date = new Date(2017 - 1900, 3 - 1, 26, 1, 30); Instant instant = date.toInstant(); ZoneId zone = ZoneId.systemDefault(); LocalDateTime ldt = LocalDateTime.ofInstant(instant, zone); System.out.println(ldt); // Use ISO-8601 by default } } 和系统本地时区。否则,请不要为此目的使用Date

如果你绝对 以这种破碎的方式使用Date,那么使用已弃用了大约20年的方法,因为它们会产生误导,但是你是能够改变系统时区,然后将其更改为没有 - 而且从未有过 - DST。 UTC是这里显而易见的选择。此时,您可以在本地日期/时间和Date之间进行转换,而不会丢失数据。它仍然是Date的错误用法, 就像Date一样,但至少你不会丢失数据。< / p>

或者您可以确保无论何时从本地日期/时间构建Instant,您都可以使用UTC进行转换,而不是系统本地时区...是否&#39 ; s通过Date方法,或使用UTC中的Date.UTC或其他任何内容解析文本。不幸的是,您还没有告诉我们您的SimpleDateFormat价值来自何处...

答案 1 :(得分:2)

您可以根据您的要求使用。

   java.util.Date
   DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
   Date date = new Date();
   System.out.println(dateFormat.format(date));

   java.util.Calendar
   DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
   Calendar cal = Calendar.getInstance();
   System.out.println(dateFormat.format(cal.getTime()));

   java.time.LocalDateTime
   DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
   LocalDateTime localDateTime = LocalDateTime.now();
   System.out.println(dateTimeFormat.format(localDateTime));