Вопрос по date, java – Java Дата разница с месяцем

38

У меня есть дата начала и окончания.

Мне нужно количество месяцев между этими двумя датами в Java.

Например

  • From date: 2009-01-29
  • To date: 2009-02-02

У этого есть одна дата января и одна дата февраля.

Должен вернуться 2.

Ваш Ответ

21   ответ
2

tl;dr

ChronoUnit.MONTHS.between( 
    YearMonth.from( LocalDate.of( 2009 , 1 , 29 ) ) , 
    YearMonth.from( LocalDate.of( 2009 , 2 , 2 ) )
)

Time Zone

Ответ Роланда Теппа близко, но игнорирует ключевую проблему часового пояса. Для определения месяца и даты требуется часовой пояс, как и в любой данный моментthe date varies around the globe по зонам.

ZonedDateTime

Так что его пример преобразованияjava.util.Date возражает противjava.time.Instant объекты неявно используетуниверсальное глобальное время, Значения в любом из этих классов всегда в 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 ENUM.

long monthsBetween = ChronoUnit.MONTHS.between( start , stop );

1

Half-Open

Вы хотели получить результат 2, но мы получили 1 здесь. Причина в том, что в работе с датой и временем лучшая практика - определять промежутки времени с помощью подхода Half-Open. В Half-Open началоinclusive в то время как окончаниеexclusive, Я предлагаю вам придерживаться этого определения на протяжении всей работы с датой и временем, так как это в конечном итоге имеет смысл, устраняет запутанные неоднозначности и облегчает умственный анализ вашей работы и снижает вероятность ошибок. Но если вы настаиваете на своем определении, просто добавьте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


About java.time

java.time фреймворк встроен в Java 8 и выше. Эти классы вытесняют хлопотное староенаследие классы даты и времени, такие какjava.util.Date, Calendar& amp;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, а такжеБольше.

0
long monthsBetween = ChronoUnit.MONTHS.between(LocalDate.parse("2016-01-29").minusMonths(1),
            LocalDate.parse("2016-02-02").plusMonths(1));
  1. 2016-01-29 to 2016-01-02 = months 1
  2. 2016-02-29 to 2016-02-02 = months 1
  3. 2016-03-29 to 2016-05-02 = months 5
Error: User Rate Limit ExceededHow do I write a good answer ?
30

я мог быstrongly рекомендуюJoda времени за это.

  1. It makes this sort of work very easy (check out Periods)
  2. It doesn't suffer from the threading issues plaguing the current date/time objects (I'm thinking of formatters, particularly)
  3. It's the basis of the new Java date/time APIs to come with Java 7 (so you're learning something that will become standard)

Обратите также внимание на комментарии Ника Холта ниже. изменения летнего времени.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededLocalDateError: User Rate Limit Exceeded
Error: User Rate Limit ExceededJSR 310: Date and Time APIError: User Rate Limit Exceeded
0

Полный фрагмент кода для определения разницы месяцев между двумя датами выглядит следующим образом:

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;
}
0

Вы можете за 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;
    }
18

Теперь, когда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;
}

Это дает преимущество в четком изложении точности вычислений, и очень легко понять, какова цель расчета.

Error: User Rate Limit ExceededZoneIdError: User Rate Limit ExceededInstantError: User Rate Limit ExceededZonedDateTimeError: User Rate Limit ExceededYearMonth.from.
Error: User Rate Limit Exceeded
Правда, часовой пояс игнорируется. Однако влияние часового пояса незначительно, учитывая, что точность желаемого результата равна числу месяцев между двумя значениями даты. В патологическом случае разница может оказать влияние, но она крайне маловероятна, и в случае, если разница достаточно важна, разработчик должен убедиться, что часовые пояса выстроены правильно.
Да, спасибо за указание на мою ошибку @mkurz, Должно быть, как-то проскользнул. В любом случае, я исправил эти ошибки типа сейчас, и фрагмент должен быть в порядке.
0

Почему бы не рассчитать с полной датой

 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;
            }
        }
    }
1

Это зависит от вашего определения месяца, но это то, что мы используем:

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;
Error: User Rate Limit Exceededjava.timeError: User Rate Limit Exceededthis questions: How to use ThreeTenABP in Android Project.
Error: User Rate Limit ExceededhugeError: User Rate Limit Exceeded
Error: User Rate Limit ExceededCalendarError: User Rate Limit Exceededjava.timeError: User Rate Limit ExceededCalendarError: User Rate Limit Exceeded
0

Приведенная ниже логика принесет вам разницу в месяцах

(endCal.get(Calendar.YEAR)*12+endCal.get(Calendar.MONTH))-(startCal.get(Calendar.YEAR)*12+startCal.get(Calendar.MONTH))
1

Я должен был написать эту реализацию, потому что у меня были определенные пользователем периоды, которые я должен был искать в течение двух дат. Здесь вы можете определить свой пользовательский период и поставить логику для расчета.

Здесь TimePeriod - POJO, который имеет начало, конец, начало периода, конец периода

Public Class Monthly продлевает период {

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;
}

}

2

Время йода довольно классная библиотека для Java Date and Time и может помочь вам достичь того, что вы хотите, используяPeriods.

Error: User Rate Limit ExceededJoda-TimeError: User Rate Limit Exceededmaintenance modeError: User Rate Limit Exceededjava.time классы. УвидетьTutorial by Oracle.
0

Здесь полная реализация monthDiff в Java без итераций. Возвращает количествоfull месяц между двумя датами. Если вы хотите включить в результат число неполных месяцев (как в первоначальном вопросе), вам нужно обнулить день, часы, минуты, секунды и миллисекунды двух дат перед вызовом метода, или вы можете изменить способ не сравнивать дни, часы, минуты и т. д.

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's a full month yeah
                }
            }
        }
    }
    return monthDiff;
}
Большинство изjava.timeError: User Rate Limit ExceededThreeTen-BackportError: User Rate Limit Exceeded
Error: User Rate Limit Exceededjava.util.Date, java.util.CalendarError: User Rate Limit Exceededjava.text.SimpleDateFormatError: User Rate Limit ExceededlegacyError: User Rate Limit Exceededjava.timeError: User Rate Limit ExceededTutorial by Oracle.
Error: User Rate Limit Exceeded
37

Как говорят остальные, если есть библиотека, которая даст вам разницу во времени в месяцах, и вы сможете ее использовать, то вы тоже можете.

В противном случае, еслиy1 а такжеm1 год и месяц первого свидания, иy2 а такжеm2 год и месяц второго, то значение, которое вы хотите:

(y2 - y1) * 12 + (m2 - m1) + 1;

Обратите внимание, что средний член (m2 - m1) может быть отрицательным, даже если вторая дата следует за первой, но это нормально.

Не имеет значения, взяты ли месяцы с январем = 0 или январем = 1, и не имеет значения, являются ли годы н.э., годами с 1900 года или чем-то еще, если обе даты используют одну и ту же основу. Так, например, не смешивать даты AD и BC, поскольку не было года 0 и, следовательно, BC смещено на 1 от AD.

Вы получитеy1 и т. д. либо непосредственно с дат, если они предоставлены вам в подходящей форме, либо с использованием календаря.

Error: User Rate Limit Exceeded
8

с помощьювремя йода было бы так (я сравнил, сколько месяцев между сегодняшним днем и 20 декабря 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, а затем сделать разницу в миллисекундах между обеими датами и затем рассчитать обратно в месяцы. Но я все же предпочитаю использовать время Йода для простоты :)

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
К вашему сведению,Joda-TimeError: User Rate Limit Exceededmaintenance modeError: User Rate Limit Exceededjava.timeError: User Rate Limit ExceededTutorial by Oracle.
35

Помимо использования времени 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, существует более стандартный способ вычисления той же разницы. Смотрите мой альтернативный ответиспользуя API-интерфейс JSR-310 вместо.

Да, это намеренно. Это вернет количество месяцев, включенных в данный диапазон дат. В вашем примере диапазон содержит ровно один месяц.
Error: User Rate Limit ExceededJoda-Time? YodaError: User Rate Limit Exceeded
@Roland - Ах, спасибо за разъяснения, я сделал поспешные выводы относительно того, чего хотел ОП.
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
1

Вы можете использовать календарь илиВремя йода библиотека для этого.

Во время Joda вы можете использовать метод Days.daysBetween (). Затем вы можете рассчитать разницу месяцев. Вы также можете использовать DateTime.getMonthOfYear () и сделать вычитание (для дат в том же году).

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
8

Основываясь на предложенных выше ответах, я свернул свой собственный, который добавил в существующий класс 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());
    }
15

Решение 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);

}
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
1

Вот решение с использованием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;
}
1

Это потому, что классыJava Дата и Календарь используют индексы месяца из0-11

January = 0
December = 1

Рекомендуется использоватьВремя йода!

1

это не лучший ответ, но вы можете использовать unixtimestamp Сначала вы находите даты в unixtime затем вытолкнуть друг друга

Наконец, вы должны преобразовать Unixtime (сумма) в строку

Error: User Rate Limit Exceeded

Похожие вопросы