如何检查 Java 日期类型的有效性?

How to check validity of Java date type?

我写了两个简单的函数来获取我在 MySQL table 中的日期值。开始日期和结束日期列都是 Date 数据类型。所以,在我的这两个函数中是这样的:

public Date get_startdate(long nodeid,String ts) {
    try {
        String sql="Select STARTDT FROM urllink WHERE URL='f0="+nodeid+"&ts="+ts + "'";
        if (em == null) {
             throw new Exception("could not found URL object.");
        }
        return (Date) em.createNativeQuery(sql).getSingleResult();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

public Date get_enddate(long nodeid,String ts) {
    try {
        String sql="Select ENDDT FROM urllink WHERE URL='f0="+nodeid+"&ts="+ts + "'";
        if (em == null) {
            throw new Exception("could not found URL object.");
        }
        return (Date) em.createNativeQuery(sql).getSingleResult();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

既然我在我的主页上调用了这些函数,我想检查日期条件,如果 URL 在这两个日期之间,那么它应该有效并做一些事情。我在下面强调我的意思:

Date file_getstartdate=fileFacade1.get_startdate(fileID,hash);
Date file_getenddate=fileFacade1.get_enddate(fileID,hash);

String currentDate = CoreUtil.parseDate(new Date());

if( file_getstartdate<= currentDate<= file_getendDate){
    //URL is valid, do something
}else {
    //do nothing
}

我的 table 中存储的日期采用 YYYY-MM-DD 格式,我面临的问题是上面的 if 语句使 comparison.I 不能'不要使用那些运算符来进行检查。有没有办法实现我的愿望?

如果您使用字符串而不是日期对象,则 if 语句将适用于该格式,假设您将其编写为 (a <= b && b <= c)

否则,我不确定如何将 getSingleResult() 转换为 Date 对象,因为它可以是任何东西,您实际上必须解析该值,然后使用适当的 Date 方法来检查isBefore 和 isAfter

Java: how do I check if a Date is within a certain range?

不要使用字符串连接来构造 SQL 查询

这很容易受到 SQL 注入,并且非常危险:

String sql="Select STARTDT FROM urllink WHERE URL='f0="+nodeid+"&ts="+ts + "'";
...
return (Date) em.createNativeQuery(sql).getSingleResult();

假设 em 指的是 EntityManager object then you can build a Criteria based query, or if you really need to stick with native SQL then you should use a PreparedStatement

对于你的比较题,你有三个问题:

  • 您正在尝试比较两种不同的类型(StringDate)。
  • 您尝试使用的运算符只能用于比较基元而不是对象 (<=)。
  • 您将条件写成数学语句而不是编程条件语句(a <= b <= c 不是有效的 Java 语句。它需要是 a <= b && b <= c)。

这是使用 Date class 进行比较的方法(请注意,因为 Java 8 LocalDate 比 class 要好得多,如果你可以选择)。

Date fileStartDate = fileFacade1.get_startdate(fileID, hash);
Date fileEndDate = fileFacade1.get_enddate(fileID, hash);

Date currentDate = new Date();

if (fileStartDate.before(currentDate) && fileEndDate.after(currentDate) {
  ...

回应您的评论

Okay but does using preparedStatement helps with this SQL inejction. Does entity manager prevent the injection? I was told not to use preparedstatement,are there any other alternatives?

如果有人告诉您使用字符串连接(代码的 +nodeid++ts + 部分)而不是使用 PreparedStatement 进行本机查询,那么他们就错了。 EntityManager 不会保护您免受上面代码中的注入,但是 PreparedStatement 以及更改查询的构造方式会。

PreparedStatement 看起来像

String url = "f0=" + nodeid + "&ts=" + ts;
PreparedStatement preparedStatement = connection.prepareStatement("Select STARTDT FROM urllink WHERE URL= ?");
preparedStatement.setString(1, url);
ResultSet resultSet = preparedStatement.executeQuery();

如果有人告诉您使用 EntityManager 而不是编写本机 SQL 那么这实际上是个好建议。您需要使用 Criteria 抽象来构建查询。如何做到这一点可能是一个单独的问题。

tl;博士

if( file_getstartdate<= currentDate<= file_getendDate) { … }

LocalDateRange                                // Represent a span-of-time as a pair of `LocalDate` (date-only) objects.
.ofClosed( startLocalDate , stopLocalDate )   // Making the date range in fully-closed approach, against my advice of using half-open approach.
.contains(                                    // Compares a specified `LocalDate` against the start and stop dates of the range.
    LocalDate.now(                            // Use `java.time.LocalDate` to represent a date-only value without a time-of-day and without a time zone.
        ZoneId.of( "Africa/Tunis" )           // Specify the time zone by which we want to perceive the calendar date for the current moment. "Tomorrow" arrives in Paris France while still "yesterday" in Montréal Québec.
    )                                         // Returns a `LocalDate` object.
)                                             // Returns a `boolean` primitive.

java.time

是正确的,但可以通过使用现代 java.time classes 而不是可怕的遗留 classes(Date,等等)。

LocalDate

对于 SQL-标准 DATE 类型的列,使用 LocalDate class。 LocalDate class represents a date-only value without time-of-day and without time zone or offset-from-UTC.

时区

时区对于确定日期至关重要。对于任何给定时刻,日期在全球范围内因地区而异。例如,Paris France is a new day while still “yesterday” in Montréal Québec.

午夜后几分钟

如果未指定时区,JVM 将隐式应用其当前默认时区。该默认值在运行时可能 change at any moment (!),因此您的结果可能会有所不同。最好明确指定您的 desired/expected 时区作为参数。如果关键,请与您的用户确认区域。

ZoneID

Continent/Region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 ESTIST 等 2-4 字母缩写,因为它们 不是 真正的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Montreal" ) ;  
LocalDate today = LocalDate.now( z ) ;

如果你想使用 JVM 当前的默认时区,请求它并作为参数传递。如果省略,代码将变得难以阅读,因为我们不确定您是否打算使用默认值,或者您是否像许多程序员一样没有意识到这个问题。

ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.

半开

if( file_getstartdate<= currentDate<= file_getendDate){

如果您始终使用半开放方法来定义时间跨度,您会发现您的工作更轻松。在这种方法中,开头是 包含 而结尾是 不包含 。因此,一个月从第一个月开始,一直到但不包括 下一个 月份的第一个月。

因此您的查询逻辑将如下所示,其中包含 <= & <

if( file_getstartdate <= currentDate < file_getendDate)

这意味着在 SQL 中,不要 使用 BETWEEN 进行日期时间工作。

String sql = "SELECT * from tbl WHERE when >= ? AND when < ? ;" ;
…
myPreparedStatement.setObject( 1 , today ) ;
myPreparedStatement.setObject( 2 , today ) ;

要检索 DATE,值:

LocalDate localDate = myResultSet.getObject( … , LocalDate.class ) ;

Java 代码中的同类半开逻辑类似于以下代码。请注意 "is equal to or is later than" 的简写形式是 "is not before"。

boolean isTodayWithinDateRange = 
    ( ! today.isBefore( startDate ) )  // Is today same or later than start…
    &&                                 // AND
    today.isBefore( stopDate )         // Is today before the stop (for Half-Open approach). 
;

不要将日期时间值与文本混淆

My date stored in my table is in the format YYYY-MM-DD

不,不是。 DATE 中的 DATE 类型通过其内部定义的机制存储日期,而不是纯文本。

日期时间对象,例如数据库中的 DATE 列或 Java 中的 LocalDate 可以将字符串输入解析为日期值,并可以从该日期生成字符串价值。但日期值本身不是字符串。不要混淆两者。换句话说,日期值没有“格式”,只有它们的文本表示有格式。

LocalDateRange

如果要完成大部分工作,请添加 ThreeTen-Extra library to your project. This gives you access to the LocalDateRange class。

LocalDate start = … ;
LocalDate stop = … ;
LocalDateRange range = LocalDateRange.of( start , stop ) ;
boolean rangeContainsToday = range.contains( today ) ;

默认情况下,LocalDateRange class 使用半开方式工作。但是如果你坚持你的全封闭方法,用 LocalDateRange.ofClosed 方法覆盖它的默认行为。


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

您可以直接与数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes.

从哪里获得java.time classes?

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.