Java: Как получить дату x дня в месяце (например, третий понедельник февраля 2012 г.) - PullRequest
14 голосов
/ 30 октября 2011

Я с этим немного борюсь.

Я хочу настроить мой Календарь, скажем: Третий понедельник февраля 2012 года. И я не нашел никакого способа сделать это с помощью Java.

Например, если я хочу установить календарь на Рождество 2011 года, я могу сделать это легко, вот так:

Calendar when = Calendar.getInstance();
when.set (Calendar.MONTH, Calendar.DECEMBER);
when.set (Calendar.DAY_OF_MONTH, 25)
when.set (Calendar.YEAR, 2011);

Но я заблудился относительно того, как его настроить, скажем, на День Памяти 2012 года, который является последним понедельником мая. Это мой код, но он явно неверен, потому что я просто не могу предположить, что последний понедельник мая будет на 4-й неделе мая того года:

Calendar when = Calendar.getInstance ();
when.set (Calendar.DAY_OF_WEEK,Calendar.MONDAY);
when.set (Calendar.MONTH, Calendar.MAY);
when.set (Calendar.WEEK_OF_MONTH, 4)
when.set (Calendar.YEAR, 2012);

Любые предложения относительно того, как я могу программно узнать, в какой неделе мая 2012 года (в примере выше) последний понедельник? Предполагая, что я смогу получить эту информацию, я смогу заставить мой код выше работать.

Мне нужно что-то, что в принципе подойдет для любых других примеров. Что-то, что может дать точный день для тех же сценариев. Примеры:

Какая дата:

  • 3-й четверг мая 2015 года
  • 1-й понедельник июня 2050
  • 4-й вторник декабря 2012
  • 2-я среда июля 2000

Мне действительно нужно это для моего проекта, и я уверен, что это просто, но я ломаю голову над этим без каких-либо реальных результатов, чтобы показать :) И также не смог найти ничего в сети.


Добавлено:

Хорошо, вот где я в последний понедельник месяца:

when.set (GregorianCalendar.MONTH, GregorianCalendar.MAY);
when.set (GregorianCalendar.DAY_OF_WEEK, Calendar.MONDAY);
when.set (GregorianCalendar.DAY_OF_WEEK_IN_MONTH, -1);
when.set (Calendar.YEAR, 2012);

Но я не уверен, как бы я поступил, например, в течение нескольких секунд понедельника в том же месяце, как это?

when.set (GregorianCalendar.DAY_OF_WEEK_IN_MONTH, 2);

Есть предложения?

Ответы [ 8 ]

11 голосов
/ 30 октября 2011

Чтобы сделать арифметику дат в Java (и вообще, сделать что-нибудь с datetime, кроме самых тривиальных вещей) Joda-Time - ответ:

public static LocalDate getNDayOfMonth(int dayweek,int nthweek,int month,int year)  {
   LocalDate d = new LocalDate(year, month, 1).withDayOfWeek(dayweek);
   if(d.getMonthOfYear() != month) d = d.plusWeeks(1);
   return d.plusWeeks(nthweek-1);
}

public static LocalDate getLastWeekdayOfMonth(int dayweek,int month,int year) {
   LocalDate d = new LocalDate(year, month, 1).plusMonths(1).withDayOfWeek(dayweek);
   if(d.getMonthOfYear() != month) d = d.minusWeeks(1);
  return d;
}

public static void main(String[] args) {
   // second wednesday of oct-2011
   LocalDate d = getNDayOfMonth( DateTimeConstants.WEDNESDAY, 2, 10, 2011);
   System.out.println(d);
   // last wednesday of oct-2011
   LocalDate dlast = getLastWeekdayOfMonth( DateTimeConstants.WEDNESDAY,  10, 2011);
   System.out.println(dlast);
}

Редактировать : Начиная с Java 8 (2014), новый API даты (пакет java.time), который вдохновлен / похож на Jodatime, должен быть предпочтительным .

7 голосов
/ 24 ноября 2012

Следующий код был успешно протестирован для всех праздников в 2013 и 2014 годах. Я понимаю, что на самом деле это не отвечает на первоначальный вопрос, но я думаю, что он может быть полезен для людей, которые сталкиваются с этим постом в надежде выяснить, какработать с праздниками, используя Календарь.

public static boolean isMajorHoliday(java.util.Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);

    // check if New Year's Day
    if (cal.get(Calendar.MONTH) == Calendar.JANUARY
        && cal.get(Calendar.DAY_OF_MONTH) == 1) {
        return true;
    }

    // check if Christmas
    if (cal.get(Calendar.MONTH) == Calendar.DECEMBER
        && cal.get(Calendar.DAY_OF_MONTH) == 25) {
        return true;
    }

    // check if 4th of July
    if (cal.get(Calendar.MONTH) == Calendar.JULY
        && cal.get(Calendar.DAY_OF_MONTH) == 4) {
        return true;
    }

    // check Thanksgiving (4th Thursday of November)
    if (cal.get(Calendar.MONTH) == Calendar.NOVEMBER
        && cal.get(Calendar.DAY_OF_WEEK_IN_MONTH) == 4
        && cal.get(Calendar.DAY_OF_WEEK) == Calendar.THURSDAY) {
        return true;
    }

    // check Memorial Day (last Monday of May)
    if (cal.get(Calendar.MONTH) == Calendar.MAY
        && cal.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY
        && cal.get(Calendar.DAY_OF_MONTH) > (31 - 7) ) {
        return true;
    }

    // check Labor Day (1st Monday of September)
    if (cal.get(Calendar.MONTH) == Calendar.SEPTEMBER
        && cal.get(Calendar.DAY_OF_WEEK_IN_MONTH) == 1
        && cal.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY) {
        return true;
    }

    // check President's Day (3rd Monday of February)
    if (cal.get(Calendar.MONTH) == Calendar.FEBRUARY
        && cal.get(Calendar.DAY_OF_WEEK_IN_MONTH) == 3
        && cal.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY) {
        return true;
    }

    // check Veterans Day (November 11)
    if (cal.get(Calendar.MONTH) == Calendar.NOVEMBER
        && cal.get(Calendar.DAY_OF_MONTH) == 11) {
        return true;
    }

    // check MLK Day (3rd Monday of January)
    if (cal.get(Calendar.MONTH) == Calendar.JANUARY
        && cal.get(Calendar.DAY_OF_WEEK_IN_MONTH) == 3
        && cal.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY) {
        return true;
    }

    return false;

}
4 голосов
/ 30 октября 2011

Я не знаю «простого» пути, но могу предложить вам следующее.

  1. Установить календарь на первый день месяца.
  2. Получить день недели
  3. Рассчитать дату первого понедельника месяца.
  4. Добавьте 14 дней, используя метод calendar.add ().Вы получите третий понедельник.
2 голосов
/ 19 сентября 2016

ТЛ; др

LocalDate thirdMondayInFebruary2012 = 
    YearMonth.of( 2012 , Month.FEBRUARY )
             .atDay( 1 )
             .with( TemporalAdjusters.dayOfWeekInMonth( 3 , DayOfWeek.MONDAY ) );

... и ...

LocalDate lastMondayInMay2012 = 
    YearMonth.of( 2012 , Month.MAY )
             .atDay( 1 );
             .with( TemporalAdjusters.lastInMonth( DayOfWeek.MONDAY ) );

java.time

Очень просто сделать это с помощью классов java.time, которые теперь вытесняют Joda-Time и проблемные старые классы даты и времени, связанные с самыми ранними версиями Java.

Сначала нам нужно указать желаемый месяц. Класс YearMonth может сделать это. Оттуда мы получаем LocalDate, дату без времени суток и без часового пояса.

YearMonth ym = YearMonth.of( 2012 , Month.FEBRUARY ); // Or pass '2' for 'February'.
LocalDate ld = ym.atDay( 1 );

Интерфейс TemporalAdjuster обеспечивает настройку значения даты и времени в другое значение даты и времени. Реализации, предоставляемые классом TemporalAdjusters (обратите внимание на множественное число '). Укажите день недели с помощью удобного DayOfWeek enum.

int ordinal = 3 ; // Use '3' for 'third occurrence in month' such as '3rd Monday'.
LocalDate thirdMondayInFebruary2012 = ld.with( TemporalAdjusters.dayOfWeekInMonth( ordinal , DayOfWeek.MONDAY ) );

Подсказка: Вместо того, чтобы передавать простые целые числа года и месяца по всей базе кода, передайте такие объекты, как YearMonth, Year, Month и DayOfWeek. Это устраняет неоднозначность, делает ваш код более самодокументируемым, обеспечивает безопасность типов и обеспечивает допустимые значения.


О java.time

Фреймворк java.time встроен в Java 8 и более поздние версии. Эти классы вытесняют старые классные устаревшие классы даты и времени, такие как java.util.Date, Calendar, & SimpleDateFormat.

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

Чтобы узнать больше, см. Руководство по Oracle . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .

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

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

  • Java SE 8 , Java SE 9 и выше
    • Встроенный.
    • Часть стандартного Java API со встроенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функций java.time перенесена в Java 6 и 7 в ThreeTen-Backport .
  • Android

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

1 голос
/ 30 октября 2011

Все, что вам нужно, это цикл:

public class CalculateDate {

public static void main( String ... args ) {

    Calendar c = Calendar.getInstance();
    c.set( Calendar.YEAR, 2012 );
    c.set( Calendar.MONTH , Calendar.MAY);
    c.set( Calendar.DAY_OF_MONTH, 0 );
    c.add( Calendar.DAY_OF_MONTH, -1 );

    System.out.println( c.getTime() );

    int mondaysCount = 0;

    while ( mondaysCount != 4 ) {
        c.add( Calendar.DAY_OF_MONTH, 1 );
        if ( c.get( Calendar.DAY_OF_WEEK ) == Calendar.MONDAY ) {
            mondaysCount++; 
        }       
    }

    System.out.printf( "The fourth monday of may is %s", c.getTime() );     

}

}
0 голосов
/ 03 марта 2017
public String nDow(int year, int month, int nweek, int nday)
{
    Calendar cdt = Calendar.getInstance();
    cdt.set(year, month -1, 1);
    return year + "-" + month + "-" + (getPosOfWeekday(cdt.get(Calendar.DAY_OF_WEEK), nday) + ((nweek - 1) *7));
}

private int getPosOfWeekday(int startday, int nday)
{
    nday = weekDayValue(nday);
    return constructCircularArray(startday).indexOf(nday) + 1;
}

private ArrayList<Integer> constructCircularArray(int weekday)
{
    ArrayList<Integer> circularArray = new ArrayList<Integer>();
    for(int i = 0; i < 7; i++)
    {
        circularArray.add(i, weekDayValue(weekday++));
    }
    return circularArray;
}

private int weekDayValue(int x)
{
    return ((x-1) % 7) + 1;
}
0 голосов
/ 07 марта 2012

Вот альтернатива. Что он делает: получает нужную неделю (n) и другие параметры и возвращает дату дня этой недели. Так как Calendar дает дату предыдущего месяца (например, 29 февраля вместо 7 марта, поскольку 1-я неделя марта совпадает с последней неделей февраля), функция вычисляет 2-ю неделю, если дата превышает 7 или кратно это за каждую неделю этого. Надеюсь, это поможет.

public static int getNthWeekDay (int n, int day, int month, int year) {
    Calendar calendar = Calendar.getInstance();

    calendar.set(Calendar.DAY_OF_WEEK, day);
    calendar.set(Calendar.MONTH, month);
    calendar.set(Calendar.WEEK_OF_MONTH,n);
    calendar.set(Calendar.YEAR, year);
    if (calendar.get(Calendar.DATE) > n * 7) {
        calendar.set(Calendar.DAY_OF_WEEK,day);
        calendar.set(Calendar.MONTH, month);
        calendar.set(Calendar.WEEK_OF_MONTH,day+1);

    }
    return calendar.get(Calendar.DATE);
}
0 голосов
/ 30 октября 2011

Есть вероятность, что вы используете Кварцевый планировщик ?

public Date date(String cronExpression) {
    return new org.quartz.CronExpression(cronExpression).
        getNextValidTimeAfter(new Date(0));
}

System.out.println(date("0 0 0 ? May Thu#3 2015"));
System.out.println(date("0 0 0 ? Jun Mon#1 2050"));
System.out.println(date("0 0 0 ? Dec Tue#4 2012"));
System.out.println(date("0 0 0 ? Jul Wed#2 2000"));

Этот простой код печатает правильные (?) Результаты:

Thu May 21 00:00:00 CEST 2015
Mon Jun 06 00:00:00 CEST 2050
Tue Dec 25 00:00:00 CET 2012
Wed Jul 12 00:00:00 CEST 2000

Требуемое CronExpression не имеет никаких зависимостей от остальной части Quartz, поэтому вы можете подумать о копировании его в свой проект (следите за лицензией!)

Примечание: внутренняя реализация getNextValidTimeAfter() - это 400 строк кода ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...