关于datetime:将java.util.Date转换为java.time.LocalDate

Convert java.util.Date to java.time.LocalDate

java.util.Date对象转换为新的JDK 8 / JSR-310 java.time.LocalDate的最佳方法是什么?

1
2
Date input = new Date();
LocalDate date = ???

简短的回答

1
2
Date input = new Date();
LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

说明

尽管它的名字,java.util.Date代表时间线上的瞬间,而不是"日期"。存储在对象中的实际数据是自1970-01-01T00:00Z(格林威治标准时间1970 / UTC开始时的午夜)以来的long毫秒计数。

JSR-310中java.util.Date的等价类是Instant,因此有一个方便的方法toInstant()来提供转换:

1
2
Date input = new Date();
Instant instant = input.toInstant();

java.util.Date实例没有时区概念。如果在java.util.Date上调用toString(),这可能看起来很奇怪,因为toString是相对于时区的。但是,该方法实际上使用Java的默认时区来提供字符串。时区不是java.util.Date的实际状态的一部分。

Instant也不包含有关时区的任何信息。因此,要从Instant转换为本地日期,必须指定时区。这可能是默认区域 - ZoneId.systemDefault() - 或者它可能是应用程序控制的时区,例如来自用户首选项的时区。使用atZone()方法应用时区:

1
2
3
Date input = new Date();
Instant instant = input.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());

ZonedDateTime包含由本地日期和时间,时区以及与GMT / UTC的偏移量组成的状态。因此,可以使用toLocalDate()轻松提取日期 - LocalDate - :

1
2
3
4
Date input = new Date();
Instant instant = input.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
LocalDate date = zdt.toLocalDate();

Java 9回答

在Java SE 9中,添加了一种新方法,可以略微简化此任务:

1
2
Date input = new Date();
LocalDate date = LocalDate.ofInstant(input.toInstant(), ZoneId.systemDefault());

这种新的替代方案更直接,产生更少的垃圾,因此应该表现更好。


更好的方法是:

1
2
Date date = ...;
Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate()

这个版本的优点:

  • 无论输入是java.util.Date的实例还是它的子类java.sql.Date(不像@ JodaStephen的方式)都可以工作。这在JDBC源数据中很常见。 java.sql.Date.toInstant()总是抛出异常。

  • 对于带有JSR-310反向端口的JDK8和JDK7,它也是一样的

我个人使用实用程序类(但这不是与backport兼容):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/**
 * Utilities for conversion between the old and new JDK date types
 * (between {@code java.util.Date} and {@code java.time.*}).
 *
 * <p>

 * All methods are null-safe.
 */

public class DateConvertUtils {

    /**
     * Calls {@link #asLocalDate(Date, ZoneId)} with the system default time zone.
     */

    public static LocalDate asLocalDate(java.util.Date date) {
        return asLocalDate(date, ZoneId.systemDefault());
    }

    /**
     * Creates {@link LocalDate} from {@code java.util.Date} or it's subclasses. Null-safe.
     */

    public static LocalDate asLocalDate(java.util.Date date, ZoneId zone) {
        if (date == null)
            return null;

        if (date instanceof java.sql.Date)
            return ((java.sql.Date) date).toLocalDate();
        else
            return Instant.ofEpochMilli(date.getTime()).atZone(zone).toLocalDate();
    }

    /**
     * Calls {@link #asLocalDateTime(Date, ZoneId)} with the system default time zone.
     */

    public static LocalDateTime asLocalDateTime(java.util.Date date) {
        return asLocalDateTime(date, ZoneId.systemDefault());
    }

    /**
     * Creates {@link LocalDateTime} from {@code java.util.Date} or it's subclasses. Null-safe.
     */

    public static LocalDateTime asLocalDateTime(java.util.Date date, ZoneId zone) {
        if (date == null)
            return null;

        if (date instanceof java.sql.Timestamp)
            return ((java.sql.Timestamp) date).toLocalDateTime();
        else
            return Instant.ofEpochMilli(date.getTime()).atZone(zone).toLocalDateTime();
    }

    /**
     * Calls {@link #asUtilDate(Object, ZoneId)} with the system default time zone.
     */

    public static java.util.Date asUtilDate(Object date) {
        return asUtilDate(date, ZoneId.systemDefault());
    }

    /**
     * Creates a {@link java.util.Date} from various date objects. Is null-safe. Currently supports:
<ul>

     *
<li>
{@link java.util.Date}
     *
<li>
{@link java.sql.Date}
     *
<li>
{@link java.sql.Timestamp}
     *
<li>
{@link java.time.LocalDate}
     *
<li>
{@link java.time.LocalDateTime}
     *
<li>
{@link java.time.ZonedDateTime}
     *
<li>
{@link java.time.Instant}
     *
</ul>

     *
     * @param zone Time zone, used only if the input object is LocalDate or LocalDateTime.
     *
     * @return {@link java.util.Date} (exactly this class, not a subclass, such as java.sql.Date)
     */

    public static java.util.Date asUtilDate(Object date, ZoneId zone) {
        if (date == null)
            return null;

        if (date instanceof java.sql.Date || date instanceof java.sql.Timestamp)
            return new java.util.Date(((java.util.Date) date).getTime());
        if (date instanceof java.util.Date)
            return (java.util.Date) date;
        if (date instanceof LocalDate)
            return java.util.Date.from(((LocalDate) date).atStartOfDay(zone).toInstant());
        if (date instanceof LocalDateTime)
            return java.util.Date.from(((LocalDateTime) date).atZone(zone).toInstant());
        if (date instanceof ZonedDateTime)
            return java.util.Date.from(((ZonedDateTime) date).toInstant());
        if (date instanceof Instant)
            return java.util.Date.from((Instant) date);

        throw new UnsupportedOperationException("Don't know hot to convert" + date.getClass().getName() +" to java.util.Date");
    }

    /**
     * Creates an {@link Instant} from {@code java.util.Date} or it's subclasses. Null-safe.
     */

    public static Instant asInstant(Date date) {
        if (date == null)
            return null;
        else
            return Instant.ofEpochMilli(date.getTime());
    }

    /**
     * Calls {@link #asZonedDateTime(Date, ZoneId)} with the system default time zone.
     */

    public static ZonedDateTime asZonedDateTime(Date date) {
        return asZonedDateTime(date, ZoneId.systemDefault());
    }

    /**
     * Creates {@link ZonedDateTime} from {@code java.util.Date} or it's subclasses. Null-safe.
     */

    public static ZonedDateTime asZonedDateTime(Date date, ZoneId zone) {
        if (date == null)
            return null;
        else
            return asInstant(date).atZone(zone);
    }

}

这里的asLocalDate()方法是空安全的,使用toLocalDate(),如果输入是java.sql.Date(它可以被JDBC驱动程序覆盖以避免时区问题或不必要的计算),否则使用上述方法。


1
LocalDate localDate = LocalDate.parse( new SimpleDateFormat("yyyy-MM-dd").format(date) );


如果您使用的是Java 8,@ JodaStephen的答案显然是最好的。但是,如果您正在使用JSR-310 backport,那么很遗憾,您必须执行以下操作:

1
2
3
4
5
6
Date input = new Date();
Calendar cal = Calendar.getInstance();
cal.setTime(input);
LocalDate date = LocalDate.of(cal.get(Calendar.YEAR),
        cal.get(Calendar.MONTH) + 1,
        cal.get(Calendar.DAY_OF_MONTH));


1
LocalDate ld = new java.sql.Date( new java.util.Date().getTime() ).toLocalDate();


您可以在一行中转换:

1
2
3
public static LocalDate getLocalDateFromDate(Date date){
   return LocalDate.from(Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()));
}


首先,很容易将日期转换为即时

1
Instant timestamp = new Date().toInstant();

然后,您可以使用ofInstant()方法将Instant转换为jdk 8中的任何日期api:

1
LocalDateTime date = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault());


1
2
3
Date input = new Date();
LocalDateTime  conv=LocalDateTime.ofInstant(input.toInstant(), ZoneId.systemDefault());
LocalDate convDate=conv.toLocalDate();

Date实例确实包含时间和日期,而LocalDate则没有。因此,您可以首先使用其方法ofInstant()将其转换为LocalDateTime,然后如果您想要它没有时间,则将实例转换为LocalDate


1
2
public static LocalDate Date2LocalDate(Date date) {
        return LocalDate.parse(date.toString(), DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy"))

此格式来自Date#tostring

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    public String toString() {
        //"EEE MMM dd HH:mm:ss zzz yyyy";
        BaseCalendar.Date date = normalize();
        StringBuilder sb = new StringBuilder(28);
        int index = date.getDayOfWeek();
        if (index == BaseCalendar.SUNDAY) {
            index = 8;
        }
        convertToAbbr(sb, wtb[index]).append(' ');                        // EEE
        convertToAbbr(sb, wtb[date.getMonth() - 1 + 2 + 7]).append(' ');  // MMM
        CalendarUtils.sprintf0d(sb, date.getDayOfMonth(), 2).append(' '); // dd

        CalendarUtils.sprintf0d(sb, date.getHours(), 2).append(':');   // HH
        CalendarUtils.sprintf0d(sb, date.getMinutes(), 2).append(':'); // mm
        CalendarUtils.sprintf0d(sb, date.getSeconds(), 2).append(' '); // ss
        TimeZone zi = date.getZone();
        if (zi != null) {
            sb.append(zi.getDisplayName(date.isDaylightTime(), TimeZone.SHORT, Locale.US)); // zzz
        } else {
            sb.append("GMT");
        }
        sb.append(' ').append(date.getYear());  // yyyy
        return sb.toString();
    }

这1条简单的线路有什么问题?

1
new LocalDateTime(new Date().getTime()).toLocalDate();


@JodaStephen在JBoss EAP 6上的实现遇到了问题。因此,我在http://docs.oracle.com/javase/tutorial/datetime/iso/legacy.html上重写了Oracle Java教程之后的转换。

1
2
3
4
5
    Date input = new Date();
    GregorianCalendar gregorianCalendar = (GregorianCalendar) Calendar.getInstance();
    gregorianCalendar.setTime(input);
    ZonedDateTime zonedDateTime = gregorianCalendar.toZonedDateTime();
    zonedDateTime.toLocalDate();


我用以下解决方案解决了这个问题

1
2
3
4
5
  import org.joda.time.LocalDate;
  Date myDate = new Date();
  LocalDate localDate = LocalDate.fromDateFields(myDate);
  System.out.println("My date using Date" Nov 18 11:23:33 BRST 2016);
  System.out.println("My date using joda.time LocalTime" 2016-11-18);

在这种情况下,localDate以"yyyy-MM-dd"格式打印您的日期