在 Java.util.Date 和我自己的日期 class 之间转换时数字会发生变化

Numbers change when converting between Java.util.Date and my own date class

对于预订监控系统,我使用了一个简单的日期 class(未显示完整 class):

package DataBase;

public class Date {
    private int day;
    private int month;
    private int year;

    public Date(int day, int month, int year) {
        super();
        this.day = day;
        this.month = month;
        this.year = year;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    @Override
    public String toString() {
        return "Date [day=" + day + ", month=" + month + ", year=" + year + "]";
    }
}

我使用这个 class 是因为当我需要处理预订并将它们放入 JTables 时,它简化了我的很多代码。

我从 JDatePicker 获取日期,然后将其与有关预订的更多信息一起放入 SQL 数据库中。然后,当应用程序重新启动时,来自 table 的行用于可视化 JTables。

然而,我的问题是,在将日期从我从数据库中获取的 Java.util.Date 转换为上面我自己的日期 class 时,我遇到了奇怪的行为。我使用这段代码来转换它们:

//Java Util Date, dateString is a string formatted like this: dd-MM-yyyy
Date date = new Date();
DateFormat format = new SimpleDateFormat("dd-MM-yyyy");
date = format.parse(dateString);
//my own date
DataBase.Date dateTemp = new DataBase.Date(date.getDay(),date.getMonth() ,date.getYear());

当我打印 dateString & dateTemp.toString() 时,我得到以下输出:

19-03-2017
Date [day=0, month=2, year=117]

上面的日期是正确的,但第二行的日月年值不匹配。

另一个例子:

21-03-2017
Date [day=2, month=2, year=117]

它似乎有一个模式,但我似乎无法找到我在这里做错了什么!

您使用的 Date 方法根据计算机的时区设置给出日期,而 class 在内部存储 UTC 时区日期的午夜(0:00 小时)。如果您的时区在 UTC 以东(就像我的一样),那么您很幸运,日期是一致的;如果您 运行 在 UTC 以西的计算机上使用相同的代码,您将获得之前的日期。您绝对不希望您的代码如此脆弱。

除了:SimpleDateFormat 也使用了您计算机的时区设置,因此使用您发布的代码不会发现任何问题;但是你会从你的数据库中得到一个日期。

JB Nizet 在评论中提到这些方法已弃用。它们之所以被弃用,正是因为它们给了你如此脆弱的代码。

有两种解决方法:

  • the Date documentation 提到你的可怜人,Calendar class。
  • 好的:java.time.LocalDate class 在 Java 8 中引入,也被移植回 Java 6 和 7。

让我们先拿好的:

    LocalDate local = date.toInstant().atOffset(ZoneOffset.UTC).toLocalDate();

要对此进行测试,您可以使用这种方式生成与来自数据库的日期相等的日期:

    date = Date.from(LocalDate.of(2017, 3, 19).atStartOfDay().atOffset(ZoneOffset.UTC).toInstant());

LocalDate class 提供方法 getDayOfMonth()getMonth()getYear() 和许多其他方法,它们会给您预期的结果。我强烈希望您会发现这个 class 非常有吸引力,以至于您不需要自己的约会对象 class。如果要更改每个字段,可以使用 withDayOfMonthwithMonthwithYear 方法。请注意,这些 return 新 LocalDate 对象具有新日期,而不是修改现有对象。 LocalDate 实例是不可变的。

如果您不能使用 Java 8 并且不想采用 Eugen Pechanec 在评论中提到的 ThreeTen Backport,您可以求助于 Calendar class 中介绍的Java 1.1(那是很久以前的事了)。

    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    cal.setTime(date);
    DataBase.Date dateTemp = new DataBase.Date(cal.get(Calendar.DAY_OF_MONTH),
            cal.get(Calendar.MONTH) + 1, cal.get(Calendar.YEAR));

要使用等于来自您数据库的日期进行测试:

    DateFormat format = new SimpleDateFormat("dd-MM-yyyy");
    format.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date date = format.parse(dateString);

我在 cal.get(Calendar.MONTH) 上加 1,因为 Calendar 的月份是从 0 开始的,而你想要一个从 1 开始的日期。

同样,您可以考虑使用 Calendar 而不是您自己的 class。它的 set 方法非常强大,以备不时之需。

为了完整起见,您得到的输出与 Date class 的文档所说的完全一致。如果您想了解发生了什么(我希望如此),请参阅其 getDay, getMonth and getYear methods. And getDate().

的文档