У меня есть дата начала и окончания.
Мне нужно количество месяцев между этими двумя датами в Java.
Например
- От даты: 2009-01-29
- На сегодняшний день: 2009-02-02
У этого есть одна дата января и одна дата февраля.
Должен вернуться 2.
У меня есть дата начала и окончания.
Мне нужно количество месяцев между этими двумя датами в Java.
Например
У этого есть одна дата января и одна дата февраля.
Должен вернуться 2.
Как говорят остальные, если есть библиотека, которая даст вам временные разницы в месяцы, и вы можете ее использовать, тогда вы также можете.
В противном случае, если y1
и m1
- это год и месяц первой даты, а y2
и m2
- год и месяц второго, то значение, которое вы хотите:
(y2 - y1) * 12 + (m2 - m1) + 1;
Обратите внимание, что средний член (m2 - m1) может быть отрицательным, даже если вторая дата после первого, но это штраф.
Не имеет значения, взяты ли месяцы с января = 0 или январем = 1, и не имеет значения, являются ли годы AD, годами с 1900 года и т.д., если обе даты используют ту же основу. Так, например, не смешивайте даты AD и BC, так как не было года 0, и поэтому BC смещается на 1 от AD.
Вы получили бы y1
и т.д. либо с дат непосредственно, если они будут предоставлены вам в подходящей форме, либо с помощью Календаря.
Помимо использования времени Joda, которое, кажется, является излюбленным предложением, я предлагаю следующий фрагмент:
public static final int getMonthsDifference(Date date1, Date date2) {
int m1 = date1.getYear() * 12 + date1.getMonth();
int m2 = date2.getYear() * 12 + date2.getMonth();
return m2 - m1 + 1;
}
EDIT: Начиная с Java 8, существует более стандартный способ вычисления такой же разницы. Посмотрите мой альтернативный ответ с использованием JSR-310 api.
Я настоятельно рекомендую для этого Joda-Time (и начиная с Java 8, Java Time apis).
Обратите внимание также на комментарии Ника Холта ниже. изменения летнего времени.
Теперь, когда JSR-310 был включен в SDK Java 8 и выше, здесь более стандартный способ получить месячную разницу двух значения даты:
public static final long getMonthsDifference(Date date1, Date date2) {
YearMonth m1 = YearMonth.from(date1.toInstant());
YearMonth m2 = YearMonth.from(date2.toInstant());
return m1.until(m2, ChronoUnit.MONTHS) + 1;
}
Это позволяет четко определить точность вычисления, и очень легко понять, в чем заключается цель расчета.
Решение Java 8:
@Test
public void monthBetween() {
LocalDate d1 = LocalDate.of(2013, Month.APRIL, 1);
LocalDate d2 = LocalDate.of(2014, Month.APRIL, 1);
long monthBetween = ChronoUnit.MONTHS.between(d1, d2);
assertEquals(12, monthBetween);
}
используя время joda, было бы так (я сравнивал, сколько месяцев между сегодняшним днем и 20/dec/2012)
import org.joda.time.DateTime ;
import org.joda.time.Months;
DateTime x = new DateTime().withDate(2009,12,20); // doomsday lol
Months d = Months.monthsBetween( new DateTime(), x);
int monthsDiff = d.getMonths();
Результат: 41 месяц (с 6 июля 2009 года)
должно быть легко?:)
ps: вы также можете конвертировать свою дату с помощью SimpleDateFormat как:
Date x = new SimpleDateFormat("dd/mm/yyyy").parse("20/12/2009");
DateTime z = new DateTime(x);
Если вы не хотите использовать Joda (по какой бы то ни было причине), вы можете конвертировать свою дату в TimeStamp, а затем выполнять разницу в миллисекундах между датой, а затем рассчитывать до месяца. Но я все же предпочитаю использовать время Джоды для простоты:)
Основываясь на приведенных выше ответах, я применил свой собственный, который я добавил к существующему классу DateUtils:
public static Integer differenceInMonths(Date beginningDate, Date endingDate) {
if (beginningDate == null || endingDate == null) {
return 0;
}
Calendar cal1 = new GregorianCalendar();
cal1.setTime(beginningDate);
Calendar cal2 = new GregorianCalendar();
cal2.setTime(endingDate);
return differenceInMonths(cal1, cal2);
}
private static Integer differenceInMonths(Calendar beginningDate, Calendar endingDate) {
if (beginningDate == null || endingDate == null) {
return 0;
}
int m1 = beginningDate.get(Calendar.YEAR) * 12 + beginningDate.get(Calendar.MONTH);
int m2 = endingDate.get(Calendar.YEAR) * 12 + endingDate.get(Calendar.MONTH);
return m2 - m1;
}
И ассоциативные модульные тесты:
public void testDifferenceInMonths() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
assertEquals(12, DateUtils.differenceInMonths(sdf.parse("2014/03/22"), sdf.parse("2015/03/22")).intValue());
assertEquals(11, DateUtils.differenceInMonths(sdf.parse("2014/01/01"), sdf.parse("2014/12/25")).intValue());
assertEquals(88, DateUtils.differenceInMonths(sdf.parse("2014/03/22"), sdf.parse("2021/07/05")).intValue());
assertEquals(6, DateUtils.differenceInMonths(sdf.parse("2014/01/22"), sdf.parse("2014/07/22")).intValue());
}
Joda Time - довольно классная библиотека для Java Date and Time и может помочь вам достичь того, что вы хотите использовать Period
s.
ChronoUnit.MONTHS.between(
YearMonth.from( LocalDate.of( 2009 , 1 , 29 ) ) ,
YearMonth.from( LocalDate.of( 2009 , 2 , 2 ) )
)
Ответ Роланда Теппа близок, но игнорирует критическую проблему часового пояса. Для определения месяца и даты требуется часовой пояс, так как в любой данный момент дата меняется по всему земному шару в зависимости от зоны.
ZonedDateTime
Так что его пример преобразования объектов java.util.Date
объекты java.time.Instant
неявно использует UTC. Значения в любом из этих классов всегда в UTC по определению. Таким образом, вам нужно настроить эти объекты в желаемом/предполагаемом часовом поясе, чтобы иметь возможность извлечь значимую дату.
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdtStart = myJavaUtilDate1.toInstant().atZone( z );
ZonedDateTime zdtStop = myJavaUtilDate2.toInstant().atZone( z );
YearMonth
Поскольку вы хотите знать, сколько календарных месяцев было затронуто вашим диапазоном дат, а не количеством прошедших 30-дневных чанков, преобразуйте их в объекты YearMonth
.
YearMonth start = YearMonth.from( zdtStart );
YearMonth stop = YearMonth.from( zdtStop );
ChronoUnit
ChronoUnit
месяцы между ними, позвонив в перечисление ChronoUnit
.
long monthsBetween = ChronoUnit.MONTHS.between( start , stop );
1
Вы хотели получить результат 2, но мы получаем 1 здесь. Причина в том, что в работе с датой и временем лучшая практика - определять промежутки времени с помощью подхода Half-Open. В Half-Open начало включительно, а окончание - эксклюзивно. Я предлагаю вам придерживаться этого определения на протяжении всей работы с датой и временем, так как это в конечном счете имеет смысл, устраняет запутанные неоднозначности и облегчает умственный анализ вашей работы и делает ее менее подверженной ошибкам. Но если вы настаиваете на своем определении, просто добавьте 1
к результату, предполагая, что у вас есть положительные пронумерованные результаты (то есть ваши промежутки времени идут вперед во времени, а не назад).
LocalDate
Исходный Вопрос неясен, но может потребовать значения только для даты, а не значения даты и времени. Если это так, используйте класс LocalDate
. Класс LocalDate
представляет значение только для даты без времени суток и без часового пояса.
LocalDate start = LocalDate.of( 2009 , 1 , 29 ) ;
LocalDate stop = LocalDate.of( 2009 , 2 , 2 ) ;
long monthsBetween = ChronoUnit.MONTHS.between( start , stop );
1
Инфраструктура 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?
Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Здесь вы можете найти некоторые полезные классы, такие как Interval
, YearWeek
, YearQuarter
и другие.
Это зависит от вашего определения месяца, но это то, что мы используем:
int iMonths = 0;
Calendar cal1 = GregorianCalendar.getInstance();
cal1.setTime(date1);
Calendar cal2 = GregorianCalendar.getInstance();
cal2.setTime(date2);
while (cal1.after(cal2)){
cal2.add(Calendar.MONTH, 1);
iMonths++;
}
if (cal2.get(Calendar.DAY_OF_MONTH) > cal1.get(Calendar.DAY_OF_MONTH)){
iMonths--;
}
return iMonths;
Для этого вы можете использовать календарь или Joda time.
Во время Йоды вы можете использовать метод Days.daysBetween(). Затем вы можете рассчитать разницу в месяцах. Вы также можете использовать DateTime.getMonthOfYear() и делать вычитание (для дат в том же году).
Мне пришлось написать эту реализацию, потому что у меня были определенные определенные периоды, которые я должен был искать в течение двух дат. Здесь вы можете определить свой пользовательский период и поместить логику для расчета.
Здесь TimePeriod - это POJO, у которого есть начало, конец, начало периода, период End
public class Monthly extends Period {
public int getPeriodCount(String startDate, String endDate, int scalar) {
int cnt = getPeriods(startDate, endDate, scalar).size();
return cnt;
}
public List getPeriods(String startDate, String endDate, int scalar) {
ArrayList list = new ArrayList();
Calendar startCal = CalendarUtil.getCalendar(startDate);
Calendar endCal = CalendarUtil.getCalendar(endDate);
while (startCal.compareTo(endCal) <= 0) {
TimePeriod period = new TimePeriod();
period.setStartDate(startCal.getTime());
period.setPeriodStartDate(getPeriodStartDate((Calendar) startCal.clone()).getTime());
Calendar periodEndCal = getPeriodEndDate((Calendar) startCal.clone(), scalar);
period.setEndDate(endCal.before(periodEndCal) ? endCal.getTime() : periodEndCal.getTime());
period.setPeriodEndDate(periodEndCal.getTime());
periodEndCal.add(Calendar.DATE, 1);
startCal = periodEndCal;
list.add(period);
}
return list;
}
private Calendar getPeriodStartDate(Calendar cal) {
cal.set(Calendar.DATE, cal.getActualMinimum(Calendar.DATE));
return cal;
}
private Calendar getPeriodEndDate(Calendar cal, int scalar) {
while (scalar-- > 0) {
cal.set(Calendar.DATE, cal.getActualMaximum(Calendar.DATE));
if (scalar > 0)
cal.add(Calendar.DATE, 1);
}
return cal;
}
}
это не лучший anwer, но вы можете использовать unixtimestamp Сначала вы найдете unixtime дат затем выталкивать друг друга
Наконец, вы должны преобразовать unixtime (sum) в String
Что, поскольку классы Java
Дата и Календарь используют индексы месяца из 0-11
January = 0
December = 1
Рекомендуется использовать Joda Time!
Вот решение с использованием объекта java.util.Calendar
:
private static Integer getMonthsBetweenDates(Date d1, Date d2) {
Calendar todayDate = getCalendar(d1);
Calendar pastDate = getCalendar(d2);
int yearDiff = todayDate.get(Calendar.YEAR) - pastDate.get(Calendar.YEAR);
if (pastDate.get(Calendar.MONTH) < 11 && pastDate.get(Calendar.DAY_OF_MONTH) < 31){ //if pastDate is smaller than 31/12
yearDiff++;
}
int monthCount = 0;
for (int year = 0 ; year < yearDiff ; year++){
if (year == 0) {
monthCount += 12 - pastDate.get(Calendar.MONTH);
} else if (year == yearDiff - 1){ //last year
if (todayDate.get(Calendar.MONTH) < pastDate.get(Calendar.MONTH)){
monthCount += todayDate.get(Calendar.MONTH) + 1;
} else if (todayDate.get(Calendar.MONTH) >= pastDate.get(Calendar.MONTH) && todayDate.get(Calendar.DAY_OF_MONTH) < pastDate.get(Calendar.DAY_OF_MONTH)){
monthCount += todayDate.get(Calendar.MONTH);
} else if (todayDate.get(Calendar.MONTH) >= pastDate.get(Calendar.MONTH) && todayDate.get(Calendar.DAY_OF_MONTH) >= pastDate.get(Calendar.DAY_OF_MONTH)){
monthCount += todayDate.get(Calendar.MONTH) + 1;
}
}
for (int months = 0 ; months < 12 ; months++){
if (year > 0 && year < yearDiff -1){
monthCount++;
}
}
}
return monthCount;
}
Почему бы не рассчитать с полным временем
public static Integer calculateMonthDiff(Date begining, Date end) throws Exception {
if (begining.compareTo(end) > 0) {
throw new Exception("Beginning date is greater than the ending date");
}
if (begining.compareTo(end) == 0) {
return 0;
}
Calendar cEndCheckDate = Calendar.getInstance();
cEndCheckDate.setTime(begining);
int add = 0;
while (true) {
cEndCheckDate.add(Calendar.MONTH, 1);
add++;
if (cEndCheckDate.getTime().compareTo(end) > 0) {
return add - 1;
}
}
}
Полный фрагмент кода для определения разницы между месяцами между двумя датами выглядит следующим образом:
public String getContractMonth(String contractStart, String contractEnd) {
SimpleDateFormat dfDate = new SimpleDateFormat("yyyy-MM-dd");
String months = "0";
try {
Date startDate = dfDate.parse(contractStart);
Date endDate = dfDate.parse(contractEnd);
Calendar startCalendar = Calendar.getInstance();
startCalendar.setTime(startDate);
Calendar endCalendar = Calendar.getInstance();
endCalendar.setTime(endDate);
int diffYear = endCalendar.get(Calendar.YEAR) - startCalendar.get(Calendar.YEAR);
int diffMonth = diffYear * 12 + endCalendar.get(Calendar.MONTH) - startCalendar.get(Calendar.MONTH);
months = diffMonth + "";
} catch (ParseException e) {
e.printStackTrace();
} catch (java.text.ParseException e) {
e.printStackTrace();
}
return months;
}
ниже логика выберет вам разницу в месяцах
(endCal.get(Calendar.YEAR)*12+endCal.get(Calendar.MONTH))-(startCal.get(Calendar.YEAR)*12+startCal.get(Calendar.MONTH))
Вы можете за 30 дней или по месяцам:
public static void main(String[] args) throws IOException {
int n = getNumbertOfMonth(LocalDate.parse("2016-08-31"),LocalDate.parse("2016-11-30"));
System.out.println("number of month = "+n);
n = getNumbertOfDays(LocalDate.parse("2016-08-31"),LocalDate.parse("2016-11-30"));
System.out.println("number of days = "+n);
System.out.println("number of 30 days = "+n/30);
}
static int getNumbertOfMonth(LocalDate dateDebut, LocalDate dateFin) {
LocalDate start = dateDebut;
LocalDate end = dateFin;
int count = 0 ;
List<String> lTotalDates = new ArrayList<>();
while (!start.isAfter(end)) {
count++;
start = start.plusMonths(1);
}
return count;
}
static int getNumbertOfDays(LocalDate dateDebut, LocalDate dateFin) {
LocalDate start = dateDebut;
LocalDate end = dateFin;
int count = 0 ;
List<String> lTotalDates = new ArrayList<>();
while (!start.isAfter(end)) {
count++;
start = start.plusDays(1);
}
return count;
}
long monthsBetween = ChronoUnit.MONTHS.between(LocalDate.parse("2016-01-29").minusMonths(1),
LocalDate.parse("2016-02-02").plusMonths(1));
Здесь полная реализация monthDiff в Java без итераций. Возвращает номер полного месяца между двумя датами. Если вы хотите включить в результат число неполных месяцев (как в первоначальном вопросе), вам нужно обнулить день, часы, минуты, секунды и миллисекунды двух дат перед вызовом метода, или вы можете изменить способ не сравнивать дни, часы, минуты и т.д.
import java.util.Date;
import java.util.Calendar;
...
public static int monthDiff(Date d1, Date d2) {
int monthDiff;
Calendar c1, c2;
int M1, M2, y1, y2, t1, t2, h1, h2, m1, m2, s1, s2, ms1, ms2;
c1 = Calendar.getInstance();
c1.setTime(d1);
c2 = Calendar.getInstance();
c2.setTime(d2);
M1 = c1.get(Calendar.MONTH);
M2 = c2.get(Calendar.MONTH);
y1 = c1.get(Calendar.YEAR);
y2 = c2.get(Calendar.YEAR);
t1 = c1.get(Calendar.DAY_OF_MONTH);
t2 = c2.get(Calendar.DAY_OF_MONTH);
if(M2 < M1) {
M2 += 12;
y2--;
}
monthDiff = 12*(y2 - y1) + M2 - M1;
if(t2 < t1)
monthDiff --; // not a full month
else if(t2 == t1) { // perhaps a full month, we have to look into the details
h1 = c1.get(Calendar.HOUR_OF_DAY);
h2 = c2.get(Calendar.HOUR_OF_DAY);
if(h2 < h1)
monthDiff--; // not a full month
else if(h2 == h1) { // go deeper
m1 = c1.get(Calendar.MINUTE);
m2 = c2.get(Calendar.MINUTE);
if(m2 < m1) // not a full month
monthDiff--;
else if(m2 == m1) { // look deeper
s1 = c1.get(Calendar.SECOND);
s2 = c2.get(Calendar.SECOND);
if(s2 < s1)
monthDiff--; // on enleve l'age de mon hamster
else if(s2 == s1) {
ms1 = c1.get(Calendar.MILLISECOND);
ms2 = c2.get(Calendar.MILLISECOND);
if(ms2 < ms1)
monthDiff--;
// else // it a full month yeah
}
}
}
}
return monthDiff;
}
Вы можете попробовать this-
Date startDate = // Set start date
Date endDate = // Set end date
long duration = endDate.getTime() - startDate.getTime();
long diffInSeconds = TimeUnit.MILLISECONDS.toSeconds(duration);
long diffInMinutes = TimeUnit.MILLISECONDS.toMinutes(duration);
long diffInHours = TimeUnit.MILLISECONDS.toHours(duration);
long diffInDays = TimeUnit.MILLISECONDS.toDays(duration);
Так много ответов с длинным кодом, когда вы можете просто сделать это с 1 строкой и немного математики:
LocalDate from = yourdate;
LocalDate to = yourotherdate;
int difference = to.getMonthValue() - from.getMonthValue()) + ((to.getYear() - from.getYear()) * 12) + 1;