Как получить первую дату и последнюю дату текущего квартала в java.util.Date
Мне нужно получить первую дату текущего квартала как объект java.util.Date и последнюю дату текущего квартала как объект java.util.Date.
Я использую следующие методы, чтобы получить первый месяц этого месяца и последний месяц этого месяца.
private Date getThisMonthFirstDate(){
Calendar calendar = new GregorianCalendar();
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
calendar.set(Calendar.DAY_OF_MONTH, 1);
return calendar.getTime();
}
private Date getThisMonthLastDate(){
Calendar calandar = new GregorianCalendar();
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
calendar.set(Calendar.DAY_OF_MONTH,1);
calendar.add(Calendar.MONTH, 1);
calendar.add(Calendar.DATE, -1);
return calendar.getTime();
}
Есть ли способ изменить эту функцию для достижения этой цели или кто-нибудь может указать лучший способ?
Предположим, что Q1 = январь февраль март, Q2 = апрель, май, июнь и т. Д.
5 ответов
private static Date getFirstDayOfQuarter(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.MONTH, cal.get(Calendar.MONTH)/3 * 3);
cal.set(Calendar.DAY_OF_MONTH, 1);
return cal.getTime();
}
private static Date getLastDayOfQuarter(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.MONTH, cal.get(Calendar.MONTH)/3 * 3 + 2);
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
return cal.getTime();
}
Это должно сработать, но я предлагаю вам проверить это более тщательно, чем я. Примечание. Деление значений int в Java возвращает значение пола.
Чистое решение Java-8+:
LocalDate localDate = LocalDate.now();
LocalDate firstDayOfQuarter = localDate.with(localDate.getMonth().firstMonthOfQuarter())
.with(TemporalAdjusters.firstDayOfMonth());
LocalDate lastDayOfQuarter = firstDayOfQuarter.plusMonths(2)
.with(TemporalAdjusters.lastDayOfMonth());
Как уже упоминалось, вы используете устаревшие классы. Sun/Oracle решила заменить старые классы даты и времени инфраструктурой java.time, что является значительным улучшением.
Избегайте старых классов даты и времени. Они действительно плохо спроектированы, сбивают с толку, и хлопотно.
java.time
Инфраструктура java.time встроена в Java 8. Для Java 6 & 7 используйте back-port ThreeTen-Backport. Для Android адаптация этого бэк-порта, ThreeTenABP.
Инфраструктура java.time основана на очень успешной библиотеке Joda-Time, определенной в JSR 310 и расширенной проектом ThreeTen-Extra. Смотрите Oracle Tutorial.
LocalDate
Для значения только для даты, без времени суток и без часового пояса, используйте LocalDate
встроенный как часть java.time. Чтобы получить текущую дату, требуется часовой пояс, так как дата меняется по всему миру (новый день наступает раньше на востоке).
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
LocalDate today = LocalDate.now ( zoneId );
Quarter
& YearQuarter
Упомянутый выше проект ThreeTen-Extra служит полигоном для возможных будущих возможностей, которые будут добавлены в java.time. Чтобы использовать эту библиотеку, добавьте ее JAR-файл в свой проект (или используйте Maven и т. Д.). Если вы действительно не хотите добавлять библиотеку, посмотрите альтернативное решение Answer by dheeran.
Эта библиотека в настоящее время включает в себя Quarter
а также YearQuarter
классы, которые вы можете найти особенно полезными. Безопаснее передавать объекты этих типов в вашем коде, а не использовать строки и числа для представления ваших кварталов.
YearQuarter currentQuarter = YearQuarter.now ( zoneId );
YearQuarter
имеет много полезных методов, в том числе запрашивая его даты.
LocalDate start = currentQuarter.atDay ( 1 );
LocalDate stop = currentQuarter.atEndOfQuarter ();
Дамп на консоль.
System.out.println ( "today: " + today + " currentQuarter: " + currentQuarter + " start: " + start + " stop: " + stop );
сегодня: 2016-04-08 текущий квартал: 2016-Q2 начало: 2016-04-01 остановка: 2016-06-30
Если вы действительно не хотите добавлять библиотеку ThreeTen-Extra, как я рекомендовал, посмотрите Ответ Павла для другого решения, использующего только встроенные классы java.time. Но если вы много работаете с четвертями, я уверен, что вы обнаружите, что ThreeTen-Extra стоит того, чтобы добавить библиотеку в ваш проект.
преобразование
Если вы должны использовать java.util.Date для работы со старым кодом, который еще не обновлен до java.time, вы можете конвертировать туда и обратно. Найти новые методы, добавленные к старым классам для преобразования.
К сожалению, в старых классах отсутствует какой-либо понятный способ представления значения только для даты. Так что нет абсолютно чистого способа добраться до / от LocalDate
,
java.sql.Date
java.sql.Date
класс претендует быть только датой, но на самом деле содержит время суток, настроенное на 00:00:00.0
, Этот класс неловко наследует от java.util.Date
но док явно предупреждает против использования этого факта; вы должны относиться к ним как к отдельным не связанным классам.
java.sql.Date sqlDate_quarterStart = java.sql.Date.valueOf( start );
java.util.Date
java.util.Date
представляет как дату, так и время суток, фактически в UTC. Мы можем сначала настроить наш LocalDate
в первый момент дня, чтобы получить дату с временем суток, ZonedDateTime
, Оттуда мы можем попросить Instant
который является моментом на временной шкале в UTC. Старый класс.Date имеет статический from( Instant )
метод конвертации.
ZonedDateTime zdt = start.atStartOfDay( zoneId ); // First moment of the day on a certain date.
Instant instant = zdt.toInstant(); // Moment on the timeline in UTC.
java.util.Date utilDate = java.util.Date.from( instant );
О java.time
Инфраструктура java.time встроена в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые классы даты и времени, такие как java.util.Date
, Calendar
& SimpleDateFormat
,
Проект Joda-Time, находящийся сейчас в режиме обслуживания, рекомендует перейти на классы java.time.
Чтобы узнать больше, смотрите Oracle Tutorial. И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310.
Вы можете обмениваться объектами java.time напрямую с вашей базой данных. Используйте драйвер JDBC, соответствующий JDBC 4.2 или более поздней версии. Нет необходимости в строках, нет необходимости в java.sql.*
классы.
Где взять классы java.time?
- Java SE 8, Java SE 9, Java SE 10 и более поздние версии
- Встроенный.
- Часть стандартного Java API со встроенной реализацией.
- Java 9 добавляет некоторые незначительные функции и исправления.
- Java SE 6 и Java SE 7
- Большая часть функциональности java.time перенесена на Java 6 и 7 в ThreeTen-Backport.
- Android
- Более поздние версии Android связывают реализации классов java.time.
- Для более ранних версий Android (<26) проект ThreeTenABP адаптирует ThreeTen-Backport (упомянутый выше). Смотрите Как использовать ThreeTenABP….
Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Вы можете найти некоторые полезные классы здесь, такие как Interval
, YearWeek
, YearQuarter
и многое другое.
Будь проще
Я думаю, что следующее наиболее элегантно, потому что в нем нет никаких магических чисел (например, 92), TemporalAdjusters или неочевидных вычислений, и его можно легко адаптировать для получения начала и конца любого квартала (а не только текущего квартала).
import static java.time.temporal.IsoFields.QUARTER_OF_YEAR;
LocalDate date = LocalDate.now(); // can be any other date
int year = date.getYear();
int quarter = date.get(QUARTER_OF_YEAR);
LocalDate start = YearMonth.of(year, 1) // January of given year
.with(QUARTER_OF_YEAR, quarter) // becomes first month of given quarter
.atDay(1); // becomes first day of given quarter
LocalDate end = YearMonth.of(year, 3) // March of given year
.with(QUARTER_OF_YEAR, quarter) // becomes 3rd (last) month of given quarter
.atEndOfMonth(); // becomes last day of given quarter
Если у вас нет даты, а только год и квартал (например, второй квартал 2020 года), это будет:
LocalDate start = YearMonth.of(2020, 1).with(QUARTER_OF_YEAR, 2).atDay(1);
LocalDate end = YearMonth.of(2020, 3).with(QUARTER_OF_YEAR, 2).atEndOfMonth();
Это довольно просто с LocalDate
LocalDate inputDate = LocalDate.parse("2018-09-04");
LocalDate firstDayOfQuarter = inputDate.withMonth(inputDate.get(IsoFields.QUARTER_OF_YEAR) * 3 - 2).with(TemporalAdjusters.firstDayOfMonth());
LocalDate lastDayOfQuarter = inputDate.withMonth(inputDate.get(IsoFields.QUARTER_OF_YEAR) * 3).with(TemporalAdjusters.lastDayOfMonth());
Ура!
Вы можете использовать java 8 LocaDate, чтобы получить первый и последний день текущего квартала
Код ниже дает вам.
public static LocalDate getCurrentQuarterStartDay(LocalDate date) {
return date.with(IsoFields.DAY_OF_QUARTER, 1L);
}
public static LocalDate getCurrentQuarterEndDate(LocalDate date) {
return date.with(IsoFields.DAY_OF_QUARTER, 92L);
}
Здесь во втором методе 92 частично снисходительно
День квартала имеет значения от 1 до 90 в первом квартале стандартного года, от 1 до 91 в первом квартале високосного года, от 1 до 91 во втором квартале и от 1 до 92 в третьем и четвертом квартале.
подробности см. в документации ISOFields здесь
Вы можете использовать Йоду:
Date start = date.withMonthOfYear(((date.getMonthOfYear() / 3) + 1) * 3 - 2)
.withDayOfMonth(1)
.withTimeAtStartOfDay()
.toDate();
Date end = date.withMonthOfYear(((date.getMonthOfYear() / 3) + 1) * 3)
.withDayOfMonth(Month.of(((date.getMonthOfYear() / 3) + 1) * 3).maxLength())
.withTimeAtStartOfDay()
.toDate();
Когда DateTime date = новый DateTime(2016, 8, 12, 1, 1, 1);
Я получил:
Fri Jul 01 00:00:00 CEST 2016
Fri Sep 30 00:00:00 CEST 2016
Используя ответ Владимира Маслого с помощью java.util.Date
, в моих тестах обнаружилась ошибка. Если вы введете "31/05/2000", он вернет 1 июля того же года, что, очевидно, не то, что мы ищем.
я использовал java.time.LocalDate
как было предложено Бэзилом Бурком и dheeran, и тестовый пример работает нормально.
Мой метод следующий:
public static Date lastDayOfQuarter(Date date) {
LocalDate inputDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate lastDayOfQuarter = inputDate.withMonth(inputDate.get(IsoFields.QUARTER_OF_YEAR) * 3).with(TemporalAdjusters.lastDayOfMonth());
return Date.from(LastDayOfQuarter.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
}
Мои модульные тесты с использованием ZohhakRunner следующие:
@TestWith({
"01/03/2000, 31/03/2000",
"05/04/1982, 30/06/1982",
"31/05/1982, 30/06/1982",
"09/07/1990, 30/09/1990",
"11/11/2019, 31/12/2019",
"31/03/2016, 31/03/2016"})
public void lastDayOfQuarter_useInputDate_returnExpectedEndOfQuarterDate(String inputDateAsString, String endOfQuarterAsString) throws ParseException{
Date inputDate = new SimpleDateFormat("dd/MM/yyyy").parse(inputDateAsString);
Date expectedDate = new SimpleDateFormat("dd/MM/yyyy").parse(endOfQuarterAsString);
Date result = DateUtils.lastDayOfQuarter(inputDate);
assertEquals(result, expectedDate);
}