Получить первую и последнюю дату предыдущих месяцев

Мне нужно запросить данные за последний месяц из базы данных. Поэтому мне нужна первая и последняя дата предыдущего месяца, чтобы сделать SQL-запрос «между».

Конечно, первое свидание всегда начинается с 1, но жесткое кодирование выглядит плохо. Кроме того, жесткое кодирование формата даты «дд-мм-гггг» кажется плохой практикой. Есть ли более простой способ сделать это? Прямо сейчас у меня есть что-то любительское, но работающее, например:

        YearMonth thisMonth    = YearMonth.now();
        YearMonth lastMonth    = thisMonth.minusMonths(1);
        
        String dd = Integer.toString(lastMonth.lengthOfMonth());
        String mm = Integer.toString(lastMonth.getMonthValue());
        String yyyy = Integer.toString(lastMonth.getYear());
        
        String startDate = "01-" + mm + "-" + yyyy;
        String endDate = dd + "-" + mm + "-" + yyyy;

        System.out.println("startDate: " + startDate);
        System.out.println("endDate: " + endDate);

Вы можете сделать это вычисление непосредственно в своем запросе. Какую базу данных вы используете (mysql, oracle, sql-server...?), и как выглядит ваш запрос?

GMB 11.12.2020 11:20

@GMB Я использую оракул

willberthos 11.12.2020 11:39
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
2
588
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Поэтому мне нужна первая и последняя дата предыдущего месяца, чтобы сделать SQL-запрос «между».

Вы можете сделать это вычисление прямо в своей базе данных. В Oracle вы можете получить первый и последний день предыдущего месяца в виде date вот так:

where mydate between add_months(trunc(sysdate, 'month'), -1)
                 and last_day(add_months(trunc(sysdate, 'month'), -1))

В общем, between и последний день особо не нужен. Можно использовать полуоткрытые интервалы — что также безопаснее, так как учитывает даты, относящиеся к последнему дню месяца:

where mydate >= add_months(trunc(sysdate, 'month'), -1)
  and mydate <  trunc(sysdate, 'month')

первый запрос дал 29.11. как последний день предыдущего месяца, но у меня сработал второй (30.11. был правильным). В любом случае мне все еще нужно преобразовать это в запрос JPA. Я сообщу, если у меня все получится, и приму это как ответ!

willberthos 11.12.2020 13:03

@виллбертос . . . Я исправил первый запрос. В исходном ответе не учитывались некоторые крайние случаи.

Gordon Linoff 11.12.2020 13:50

Перенесите объекты LocalDate в ваш SQL-запрос

Не отправляйте первую и последнюю дату в виде строк в запрос к базе данных. Поскольку JDBC 4.2 передает LocalDate объекты. Есть несколько способов выполнить вычисления в Java. Я бы предпочел использовать YearMonth, как вы делаете в вопросе.

    YearMonth thisMonth    = YearMonth.now(ZoneId.of("Atlantic/Reykjavik"));
    YearMonth lastMonth    = thisMonth.minusMonths(1);
    
    LocalDate startDate = lastMonth.atDay(1);
    LocalDate endDate = lastMonth.atEndOfMonth();
    
    System.out.println("startDate: " + startDate);
    System.out.println("endDate: " + endDate);

Вывод при запуске сегодня:

startDate: 2020-11-01
endDate: 2020-11-30

Для перехода на JDBC:

    String query = "select * from your_table where your_date_col between ? and ?;";
    PreparedStatement statement = yourDatabaseConnection.prepareStatement(query);
    statement.setObject(1, startDate);
    statement.setObject(2, endDate);

В вашем распоряжении следующие регуляторы:

  1. TemporalAdjusters.firstDayOfMonth()

  2. TemporalAdjusters.lastDayOfMonth()

Демо:

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

class Main {
    public static void main(String[] args) {
        // Today's date
        LocalDate today = LocalDate.now();

        LocalDate oneMonthAgo = today.minusMonths(1);

        LocalDate firstDateOfLastMonth = oneMonthAgo.with(TemporalAdjusters.firstDayOfMonth());
        LocalDate lastDateOfLastMonth = oneMonthAgo.with(TemporalAdjusters.lastDayOfMonth());

        System.out.println(firstDateOfLastMonth);
        System.out.println(lastDateOfLastMonth);
    }
}

Выход:

2020-11-01
2020-11-30

Узнайте больше о современном API даты и времени на Trail: Date Time.

Как использовать API даты и времени Java 8 (JSR-310) с JDBC 4.2:

String query = "SELECT column_name(s)
                FROM table_name
                WHERE column_name BETWEEN ? AND ?";

PreparedStatement st = conn.prepareStatement(query);
st.setObject(1, firstDateOfLastMonth);
st.setObject(2, lastDateOfLastMonth);
st.executeQuery();

//...Process ResultSet 

st.close();

Это позволяет избежать жесткого кодирования 1 в соответствии с запросом. Хороший и рекомендуемый ответ.

Ole V.V. 14.12.2020 05:42

Вы можете использовать TemporalAdjusters.firstDayOfMonth() и TemporalAdjusters.lastDayOfMonth()

См. документ: https://docs.oracle.com/javase/8/docs/api/java/time/temporal/TemporalAdjusters.html#lastDayOfMonth--

LocalDate currentDate = LocalDate.now();
LocalDate currentDateLastMonth = LocalDate.of(
                currentDate.getYear(), 
                currentDate.getMonth().minus(1), 
                currentDate.getDayOfMonth());

LocalDate firstDayOfMonth = currentDateLastMonth.with(TemporalAdjusters.firstDayOfMonth());
LocalDate lastDayOfMonth = currentDateLastMonth.with(TemporalAdjusters.lastDayOfMonth());

System.out.println("first day of the month "+ firstDayOfMonth);
System.out.println("last day of the month "+ lastDayOfMonth);

Другие вопросы по теме