Как проверить правильность типа даты Java? - PullRequest
2 голосов
/ 08 апреля 2019

Я написал две простые функции, чтобы получить значение моей даты в таблице 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 находится между этими двумя датами, он должен быть действительным и что-то делать. Ниже я выделю то, что имею в виду:

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 ]

3 голосов
/ 08 апреля 2019

Не использовать конкатенацию строк для построения запросов SQL

Это уязвимо для внедрения SQL и действительно опасно:

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

Предполагается emотносится к объекту EntityManager , тогда вы можете построить запрос на основе Criteria или, если вам действительно нужно придерживаться собственного SQL, тогда вместо него следует использовать PreparedStatement .

Для вашего вопроса сравнения у вас есть три проблемы:

  • Вы пытаетесь сравнить два разных типа (String и Date).
  • Вы пытаетесьиспользовать оператор, который можно использовать только для сравнения примитивов, но не объектов (<=).
  • Вы пишете свое условие как математическое утверждение, а не как условное выражение программирования (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) {
  ...

В ответ на ваш комментарий

Хорошо, но использование подготовленного состояния помогает в этом SQL-переходе.Менеджер объекта предотвращает инъекцию?Мне сказали не использовать готовое заявление, есть ли другие альтернативы?

Если кто-то сказал вам сделать собственный запрос, используя конкатенацию строк (части вашего кода +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 голос
/ 09 апреля 2019

ТЛ; др

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

Ответ от Player One правильный, но его можно улучшить, используя современные классы java.time , а не ужасные классы наследства (Date и т. Д.).

LocalDate

Для столбца типа SQL-стандарта DATE используйте класс LocalDate. Класс LocalDate представляет значение только для даты без времени суток и без часового пояса или смещения от UTC .

Часовой пояс

Часовой пояс имеет решающее значение при определении даты. В любой момент времени дата меняется по всему земному шару в зависимости от зоны. Например, через несколько минут после полуночи в Париж Франция - это новый день, а еще "вчера" в Монреаль Квебек .

Если часовой пояс не указан, JVM неявно применяет свой текущий часовой пояс по умолчанию. Это значение по умолчанию может измениться в любой момент во время выполнения (!), Поэтому ваши результаты могут отличаться. Лучше явно указать желаемый / ожидаемый часовой пояс в качестве аргумента. Если критично, подтвердите зону с вашим пользователем.

ZoneID

Укажите собственное имя часового пояса в формате Continent/Region, например America/Montreal, Africa/Casablanca или Pacific/Auckland. Никогда не используйте 2-4 буквенные сокращения, такие как EST или IST, так как они не истинные часовые пояса, не стандартизированы и даже не уникальны (!).

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

Если вы хотите использовать текущий часовой пояс JVM по умолчанию, запросите его и передайте в качестве аргумента. Если опущен, код становится неоднозначным для чтения, поскольку мы точно не знаем, намеревались ли вы использовать значение по умолчанию или если вы, как и многие программисты, не знали о проблеме.

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

Half-Open

if (file_getstartdate <= currentDate <= file_getendDate) {</p>

Вы найдете свою работу проще, если будете последовательно использовать подход Half-Open для определения промежутка времени. В этом подходе начало включительно , а окончание исключительно . Таким образом, месяц начинается с первого и продолжается, но не включает в себя, первое из следующего месяца.

Таким образом, ваша логика запроса будет, как показано ниже, с <= & <.

if (file_getstartdate <= currentDate <file_getendDate) </p>

Это означает, что в 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). 
;

Не путать значения даты и времени с текстом

Моя дата хранится в моей таблице в формате ГГГГ-ММ-ДД

Нет, это не так. Тип DATE в MySQL хранит дату с помощью собственного внутреннего механизма, а не обычного текста.

Объект даты-времени, такой как столбец DATE в базе данных или LocalDate в Java, может анализировать ввод строки в значение даты и может генерировать строку из этого значения даты. Но значение даты само по себе не является строкой. Не путайте их. Другими словами, значение даты не имеет «формата», формат имеют только их текстовые представления.

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 и более поздние версии. Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar и & SimpleDateFormat.

на выходПодробнее см. Учебное пособие по Oracle .И поиск переполнения стека для многих примеров и объяснений.Спецификация: JSR 310 .

Проект Joda-Time , теперь в режиме обслуживания , рекомендует перейти на java.time классы.

Вы можете обмениваться java.time объектами напрямую с вашей базой данных.Используйте драйвер JDBC , совместимый с JDBC 4.2 или более поздней версии.Нет необходимости в строках, нет необходимости в java.sql.* классах.

Где получить классы java.time?

  • Java SE 8 , Java SE 9 , Java SE 10, Java SE 11 и более поздних версий - часть стандартного Java API с связанной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и JavaSE 7
    • Большинство функций java.time перенесены в Java 6 & 7 в ThreeTen-Backport .
  • Android
    • Более поздние версии пакетов Android для реализации java.time классы.
    • Для более ранних версий Android (<26) проект <a href="https://github.com/JakeWharton/ThreeTenABP" rel="nofollow noreferrer"> ThreeTenABP адаптируется ThreeTen-Backport (упомянуто выше).См. Как использовать ThreeTenABP… .

ThreeTen-Extra Проект расширяет java.time дополнительными классами.Этот проект является полигоном для возможных будущих дополнений к java.time.Здесь вы можете найти несколько полезных классов, таких как Interval, YearWeek, YearQuarter и more .

1 голос
/ 08 апреля 2019

Если вы используете строки вместо объектов даты, то оператор if будет работать для этого формата, если вы напишите его как (a <= b && b <= c) </p>

В противном случае я не уверенкак getSingleResult() может быть преобразовано в объект Date, так как это может быть что угодно, и вам действительно нужно проанализировать значение, а затем использовать надлежащие методы Date для проверки isBefore и isAfter

Java:как проверить, находится ли Дата в определенном диапазоне?

...