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

时间:2019-04-08 04:57:20

标签: java date date-comparison java-date

我写了两个简单的函数来获取MySQL表中日期的值。开始日期和结束日期列均为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在这两个日期之间,则该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
}

我存储在表中的日期的格式为YYYY-MM-DD,而我遇到的问题在上面的if语句中进行比较。我无法使用这些运算符进行检查。有什么方法可以实现我想要的?

3 个答案:

答案 0 :(得分:3)

不要使用String串联来构造SQL查询

这很容易受到SQL注入的攻击,并且确实很危险:

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

假设em引用了EntityManager对象,那么您可以构建一个基于Criteria的查询,或者如果您确实需要坚持使用本机SQL,则应该使用{{3} }。

对于您的比较问题,您遇到三个问题:

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

这是一种使用Date类进行比较的方法(请注意,由于Java 8 LocalDate是更好的选择,因此可以使用)。

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) {
  ...

回应您的评论

  

好的,但是确实使用prepareStatement可以帮助解决这种SQL错误。实体经理会阻止注入吗?有人告诉我不要使用preparedstatement,还有其他选择吗?

如果有人告诉您使用字符串串联(代码的+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抽象来构造查询。该怎么做可能是一个单独的问题。

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:1)

tl; dr

  

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

Answer by Player One是正确的,但是可以使用现代的 java.time 类而不是可怕的旧类(Date等)来改进。

LocalDate

对于SQL标准DATE类型的列,请使用LocalDate类。 LocalDate类表示没有日期,没有time zoneoffset-from-UTC的仅日期值。

时区

时区对于确定日期至关重要。在任何给定时刻,日期都会在全球范围内变化。例如,Paris France午夜之后的几分钟是新的一天,而Montréal Québec仍然是“昨天”。

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

ZoneID

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

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

这意味着在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代码中相同类型的半开逻辑看起来像下面的代码。注意,说“等于或晚于”的简短方式是“不在……之前”。

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). 
;

请勿将日期时间值与文本混淆

  

我存储在表格中的日期的格式为YYYY-MM-DD

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

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

LocalDateRange

如果要完成很多这项工作,请将ThreeTen-Extra库添加到您的项目中。这使您可以访问LocalDateRange类。

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

默认情况下,LocalDateRange类使用Half-Open方法工作。但是,如果您坚持采用全封闭方法,请使用LocalDateRange.ofClosed方法覆盖其默认行为。


关于 java.time

java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

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

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

相关问题