将 java.util.Date 截断为 LocalDate *没有* toInstant() 因为 java.sql.Date 给出了 UnsupportedOperationException
Truncating java.util.Date to LocalDate *without* toInstant() because java.sql.Date gives UnsupportedOperationException
如果我在恰好是 java.sql.Date
的变量上使用 java.util.Date
的 toInstant()
,我会得到一个 UnsupportedOperationException
。
try {
java.util.Date input = new java.sql.Date(System.currentTimeMillis());
LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
} catch (UnsupportedOperationException e) {
// grrr!
}
我关心的 java.util.Date
来自 mysql DB 中的 DATE 字段,通过遗留 API,实际上是 java.sql.Date
。
下面的相关问题都很有意思:
Convert java.util.Date to java.time.LocalDate
但他们没有提供任何优雅的方法来截断 java.util.Date
以摆脱时间部分并获得 Java 8 LocalDate
.
我承认存在一个问题,即一个时区的同一时刻可能与另一个时区的同一时刻不同。
我怀疑该解决方案将涉及 java.util.Calendar,但与其制定我自己的解决方案,我宁愿先确定其他人已经完成的工作。
我希望找到比这更短的内容:
from Resetting the time part of a timestamp in Java :
Date date = new Date(); // timestamp now
Calendar cal = Calendar.getInstance(); // get calendar instance
cal.setTime(date); // set cal to date
cal.set(Calendar.HOUR_OF_DAY, 0); // set hour to midnight
cal.set(Calendar.MINUTE, 0); // set minute in hour
cal.set(Calendar.SECOND, 0); // set second in minute
cal.set(Calendar.MILLISECOND, 0); // set millis in second
Date zeroedDate = cal.getTime(); // actually computes the new Date
如果已知 input
变量是 java.sql.Date
,那么您可以简单地转换它并调用 toLocalDate()
方法:
LocalDate date = ((java.sql.Date) input).toLocalDate();
不幸的是,您不能在 java.sql.Date
上调用 toInstant()
,因为 according to javadoc,它总是抛出 UnsupportedOperationException
。
如果您不知道类型(可以是 java.util.Date
或 java.sql.Date
),您可以使用 getTime()
方法返回的值来构建 Instant
,然后将其转换为时区(下面我使用的是 JVM 的默认值),最后从中获取本地日期:
LocalDate date = Instant
// get the millis value to build the Instant
.ofEpochMilli(input.getTime())
// convert to JVM default timezone
.atZone(ZoneId.systemDefault())
// convert to LocalDate
.toLocalDate();
toLocalDate()
方法获取日期部分(day/month/year),忽略其余部分,因此无需截断它:时间是否为午夜、上午 10 点无关紧要,或一天中的任何其他时间,toLocalDate()
将忽略它并只获取日期部分。
如果你真的想将时间设置为午夜,你可以使用 with
method and pass a LocalTime
:
LocalDate date = Instant
// get the millis value to build the Instant
.ofEpochMilli(input.getTime())
// convert to JVM default timezone
.atZone(ZoneId.systemDefault())
// set time to midnight
.with(LocalTime.MIDNIGHT)
// convert to LocalDate
.toLocalDate();
但正如我所说,toLocalDate()
方法将忽略时间部分,因此在这种情况下不需要设置时间(LocalDate
将相同)。
您还可以检查日期类型并相应地选择适当的操作,如下所示:
if (input instanceof java.sql.Date) {
date = ((java.sql.Date) input).toLocalDate();
} else {
date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
除了使用 JVM 默认时区 (ZoneId.systemDefault()
),您可以根据需要使用任何其他时区,方法是调用 ZoneId.of("zoneName")
,其中时区名称是任何有效的 IANA timezones names(始终采用 Region/City
格式,如 America/New_York
或 Europe/London
)。
避免使用 3 个字母的缩写(如 CET
或 PST
),因为它们是 ambiguous and not standard.
您可以通过调用 ZoneId.getAvailableZoneIds()
获得可用时区列表(并选择最适合您的系统的时区)。如果需要,您也可以继续使用 JVM 默认时区,但请注意它 ,因此最好始终明确说明您使用的是哪个时区。
您是否尝试过使用 SimpleDateFormat 来帮助您?
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
public class Utils {
private static SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd" );
public static void main( String[] args ) {
LocalDate ld = sqlDateToLocalDate( new java.sql.Date( System.currentTimeMillis() ) );
System.out.println( ld );
}
public static LocalDate sqlDateToLocalDate( java.sql.Date sqlDate ) {
try {
Date d = sdf.parse( sdf.format( sqlDate ) );
return d.toInstant().atZone( ZoneId.systemDefault() ).toLocalDate();
} catch ( ParseException exc ) {
}
return null;
}
}
tl;博士
myResultSet.getObject( … , LocalDate.class )
java.time
您的麻烦在使用旧日期时间 类 时开始,例如 java.sql.Date
、java.util.Date
和 Calendar
。完全避免这些 类。它们现在是遗留的,被 java.time 类 取代。
您说该值开始是 MySQL 类型 DATE
列中的存储值。该类型仅限日期,没有时间。因此,您使用错误的 类 引入了不必要的时间值 。
使用符合 JDBC 4.2 或更高版本的 JDBC 驱动程序,使用 java.time 类 与数据库交换值。
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
并传递给 PreparedStatement
.
myPstmt.setObject( … , myLocalDate ) ;
通常最简单的解决方案最难找到:
public LocalDate convertDateObject(java.util.Date suspectDate) {
try {
// Don't do this if there is the smallest chance
// it could be a java.sql.Date!
return suspectDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
} catch (UnsupportedOperationException e) {
// BOOM!!
}
// do this first:
java.util.Date safeDate = new Date(suspectDate.getTime());
return safeDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
tl;博士
你在 java.sql.Date
和 java.util.Date
之间交叉的前提是不正确的。
If I use java.util.Date … on a variable which happens to be a java.sql.Date
您违反了 class 文档中规定的合同。你被告知忽略 java.sql.Date
是 java.util.Date
.
的子 class 这一事实
但解决方案可能非常简单。
ZonedDateTime zdt = myJavaSqlDate.toLocalDate().atStartOfDay( ZoneId.of( "America/Montreal" ) ) ;
详情
显然你手头有一张 java.sql.Date
。 class 表示没有时间和时区的仅日期值。至少那是 class 的意图。从 java.util.Date
到 subclass 做出了一个悲惨的糟糕设计选择,尽管名称如此,它代表日期 和 UTC 中的一天中的时间。这种继承关系是一个 hack,一个糟糕的 hack,他们将内部时间设置为“00:00:00”。文档明确指示我们忽略这种继承关系,忽略具有时间组件的事实,并假装这两个 Date
class 是不相关的。但是像作者这样的许多人并没有仔细阅读该文档,而是根据 class 名称做出了粗略的假设。
两个 Date
class 都是 Java 最早版本附带的日期时间框架的可怕混乱的一部分。这些已被 java.time classes 取代 Java 8 及更高版本。
遇到遗留日期时间对象时的第一步:使用添加到旧 classes 的新方法转换为 java.time。
将我们伪造的仅日期对象 java.sql.Date
转换为 java.util.LocalDate
类型的真正仅日期对象。 “本地”一词表示没有时区或与 UTC 的偏移量。
LocalDate ld = myJavaSqlDate.toLocalDate() ;
显然您希望将该仅限日期的值转换为带有时间的日期。也许你想要一天的第一刻。确定第一时刻需要一个时区。例如,Europe/Paris
的新一天比 America/Montreal
早,Asia/Kolkata
更早。
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = ld.atStartOfDay( z ) ;
试试这个。
java.util.Date input = new java.sql.Date(System.currentTimeMillis());
Instant instant = Instant.ofEpochMilli(input.getTime());
LocalDate date = instant .atZone(ZoneId.systemDefault()).toLocalDate();
我不知道为什么,但这段代码对我有用:)
LocalDate ld =new java.util.Date(personEntity.getBirthday().getTime()).toInstant()
.atZone(ZoneId.systemDefault()).toLocalDate();
这不起作用:
LocalDate ld =personEntity.getBirthday().getTime().toInstant()
.atZone(ZoneId.systemDefault()).toLocalDate();
如果我在恰好是 java.sql.Date
的变量上使用 java.util.Date
的 toInstant()
,我会得到一个 UnsupportedOperationException
。
try {
java.util.Date input = new java.sql.Date(System.currentTimeMillis());
LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
} catch (UnsupportedOperationException e) {
// grrr!
}
我关心的 java.util.Date
来自 mysql DB 中的 DATE 字段,通过遗留 API,实际上是 java.sql.Date
。
下面的相关问题都很有意思:
Convert java.util.Date to java.time.LocalDate
但他们没有提供任何优雅的方法来截断 java.util.Date
以摆脱时间部分并获得 Java 8 LocalDate
.
我承认存在一个问题,即一个时区的同一时刻可能与另一个时区的同一时刻不同。
我怀疑该解决方案将涉及 java.util.Calendar,但与其制定我自己的解决方案,我宁愿先确定其他人已经完成的工作。
我希望找到比这更短的内容:
from Resetting the time part of a timestamp in Java :
Date date = new Date(); // timestamp now Calendar cal = Calendar.getInstance(); // get calendar instance cal.setTime(date); // set cal to date cal.set(Calendar.HOUR_OF_DAY, 0); // set hour to midnight cal.set(Calendar.MINUTE, 0); // set minute in hour cal.set(Calendar.SECOND, 0); // set second in minute cal.set(Calendar.MILLISECOND, 0); // set millis in second Date zeroedDate = cal.getTime(); // actually computes the new Date
如果已知 input
变量是 java.sql.Date
,那么您可以简单地转换它并调用 toLocalDate()
方法:
LocalDate date = ((java.sql.Date) input).toLocalDate();
不幸的是,您不能在 java.sql.Date
上调用 toInstant()
,因为 according to javadoc,它总是抛出 UnsupportedOperationException
。
如果您不知道类型(可以是 java.util.Date
或 java.sql.Date
),您可以使用 getTime()
方法返回的值来构建 Instant
,然后将其转换为时区(下面我使用的是 JVM 的默认值),最后从中获取本地日期:
LocalDate date = Instant
// get the millis value to build the Instant
.ofEpochMilli(input.getTime())
// convert to JVM default timezone
.atZone(ZoneId.systemDefault())
// convert to LocalDate
.toLocalDate();
toLocalDate()
方法获取日期部分(day/month/year),忽略其余部分,因此无需截断它:时间是否为午夜、上午 10 点无关紧要,或一天中的任何其他时间,toLocalDate()
将忽略它并只获取日期部分。
如果你真的想将时间设置为午夜,你可以使用 with
method and pass a LocalTime
:
LocalDate date = Instant
// get the millis value to build the Instant
.ofEpochMilli(input.getTime())
// convert to JVM default timezone
.atZone(ZoneId.systemDefault())
// set time to midnight
.with(LocalTime.MIDNIGHT)
// convert to LocalDate
.toLocalDate();
但正如我所说,toLocalDate()
方法将忽略时间部分,因此在这种情况下不需要设置时间(LocalDate
将相同)。
您还可以检查日期类型并相应地选择适当的操作,如下所示:
if (input instanceof java.sql.Date) {
date = ((java.sql.Date) input).toLocalDate();
} else {
date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
除了使用 JVM 默认时区 (ZoneId.systemDefault()
),您可以根据需要使用任何其他时区,方法是调用 ZoneId.of("zoneName")
,其中时区名称是任何有效的 IANA timezones names(始终采用 Region/City
格式,如 America/New_York
或 Europe/London
)。
避免使用 3 个字母的缩写(如 CET
或 PST
),因为它们是 ambiguous and not standard.
您可以通过调用 ZoneId.getAvailableZoneIds()
获得可用时区列表(并选择最适合您的系统的时区)。如果需要,您也可以继续使用 JVM 默认时区,但请注意它
您是否尝试过使用 SimpleDateFormat 来帮助您?
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
public class Utils {
private static SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd" );
public static void main( String[] args ) {
LocalDate ld = sqlDateToLocalDate( new java.sql.Date( System.currentTimeMillis() ) );
System.out.println( ld );
}
public static LocalDate sqlDateToLocalDate( java.sql.Date sqlDate ) {
try {
Date d = sdf.parse( sdf.format( sqlDate ) );
return d.toInstant().atZone( ZoneId.systemDefault() ).toLocalDate();
} catch ( ParseException exc ) {
}
return null;
}
}
tl;博士
myResultSet.getObject( … , LocalDate.class )
java.time
您的麻烦在使用旧日期时间 类 时开始,例如 java.sql.Date
、java.util.Date
和 Calendar
。完全避免这些 类。它们现在是遗留的,被 java.time 类 取代。
您说该值开始是 MySQL 类型 DATE
列中的存储值。该类型仅限日期,没有时间。因此,您使用错误的 类 引入了不必要的时间值 。
使用符合 JDBC 4.2 或更高版本的 JDBC 驱动程序,使用 java.time 类 与数据库交换值。
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
并传递给 PreparedStatement
.
myPstmt.setObject( … , myLocalDate ) ;
通常最简单的解决方案最难找到:
public LocalDate convertDateObject(java.util.Date suspectDate) {
try {
// Don't do this if there is the smallest chance
// it could be a java.sql.Date!
return suspectDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
} catch (UnsupportedOperationException e) {
// BOOM!!
}
// do this first:
java.util.Date safeDate = new Date(suspectDate.getTime());
return safeDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
tl;博士
你在 java.sql.Date
和 java.util.Date
之间交叉的前提是不正确的。
If I use java.util.Date … on a variable which happens to be a java.sql.Date
您违反了 class 文档中规定的合同。你被告知忽略 java.sql.Date
是 java.util.Date
.
但解决方案可能非常简单。
ZonedDateTime zdt = myJavaSqlDate.toLocalDate().atStartOfDay( ZoneId.of( "America/Montreal" ) ) ;
详情
显然你手头有一张 java.sql.Date
。 class 表示没有时间和时区的仅日期值。至少那是 class 的意图。从 java.util.Date
到 subclass 做出了一个悲惨的糟糕设计选择,尽管名称如此,它代表日期 和 UTC 中的一天中的时间。这种继承关系是一个 hack,一个糟糕的 hack,他们将内部时间设置为“00:00:00”。文档明确指示我们忽略这种继承关系,忽略具有时间组件的事实,并假装这两个 Date
class 是不相关的。但是像作者这样的许多人并没有仔细阅读该文档,而是根据 class 名称做出了粗略的假设。
两个 Date
class 都是 Java 最早版本附带的日期时间框架的可怕混乱的一部分。这些已被 java.time classes 取代 Java 8 及更高版本。
遇到遗留日期时间对象时的第一步:使用添加到旧 classes 的新方法转换为 java.time。
将我们伪造的仅日期对象 java.sql.Date
转换为 java.util.LocalDate
类型的真正仅日期对象。 “本地”一词表示没有时区或与 UTC 的偏移量。
LocalDate ld = myJavaSqlDate.toLocalDate() ;
显然您希望将该仅限日期的值转换为带有时间的日期。也许你想要一天的第一刻。确定第一时刻需要一个时区。例如,Europe/Paris
的新一天比 America/Montreal
早,Asia/Kolkata
更早。
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = ld.atStartOfDay( z ) ;
试试这个。
java.util.Date input = new java.sql.Date(System.currentTimeMillis());
Instant instant = Instant.ofEpochMilli(input.getTime());
LocalDate date = instant .atZone(ZoneId.systemDefault()).toLocalDate();
我不知道为什么,但这段代码对我有用:)
LocalDate ld =new java.util.Date(personEntity.getBirthday().getTime()).toInstant() .atZone(ZoneId.systemDefault()).toLocalDate();
这不起作用:
LocalDate ld =personEntity.getBirthday().getTime().toInstant() .atZone(ZoneId.systemDefault()).toLocalDate();