Java последний день месяца проблема - PullRequest
7 голосов
/ 09 сентября 2010

Я пытаюсь создать диапазон дат, охватывающий весь месяц, например

[startDate;endDate]

Таким образом, у меня есть контрольная дата, и я пытаюсь создать из нее новую дату.У меня проблема с endDate, потому что я хочу, чтобы он был ближе к концу дня (т. Е. 23:59:59).

Я использую следующий код:

  public static Date previousMonthLastDate(Date referenceDate) {
    Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
    calendar.setTime(referenceDate);
    calendar.add(Calendar.MONTH, -1); // move to the previous month
    int lastDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    calendar.set(Calendar.DAY_OF_MONTH, lastDay);
    // set the time to be the end of the day
    calendar.set(Calendar.HOUR_OF_DAY, 23);
    calendar.set(Calendar.MINUTE, 59);
    calendar.set(Calendar.SECOND, 59);

    return calendar.getTime();
  }

Этот код работает должным образом на эмуляторе Android .Однако запуск его на реальном телефоне дает неправильную дату.Таким образом, я предполагаю, что это какая-то проблема с часовым поясом.

По телефону вместо 31 скажем / август / 2010 он дает 01 / сентябрь / 2010.Это значение будет установлено после строки кода, в которой для HOUR_OF_DAY установлено значение 23.

Есть идеи?

Спасибо.

Ответы [ 6 ]

6 голосов
/ 07 августа 2012

Я работаю в чем-то подобном:

С помощью этого кода я устанавливаю день в интервале:

Date day = new Date()

С этим кодом я получаю интервал:

Calendar startDate = Calendar.getInstance();
Calendar endDate = Calendar.getInstance();

// Set time
startDate.setTime(day);
endDate.setTime(day);

startDate.set(Calendar.DAY_OF_MONTH, 1);
startDate.set(Calendar.HOUR_OF_DAY, 0);
startDate.set(Calendar.MINUTE, 0);
startDate.set(Calendar.SECOND, 0);
startDate.set(Calendar.MILLISECOND, 0);

endDate.set(Calendar.DAY_OF_MONTH, endDate.getActualMaximum(Calendar.DAY_OF_MONTH));
endDate.set(Calendar.HOUR_OF_DAY, 23);
endDate.set(Calendar.MINUTE, 59);
endDate.set(Calendar.SECOND, 59);
6 голосов
/ 09 сентября 2010

Я не могу ответить, почему это происходит, но вы пытались установить его на первый день следующего месяца и вычитать одну секунду / миллисекунду?

calendar.set(Calendar.DAY_OF_MONTH, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
calendar.add(Calendar.MILLISECOND, -1);
3 голосов
/ 09 сентября 2010

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

Calendar calendar = Calendar.getInstance();
2 голосов
/ 09 сентября 2011

Календарь API предоставляет вам эту функциональность из коробки, вот хитрость, чтобы сделать это:

// Get the instance of the Calendar.
Calendar calendar = Calendar.getInstance();
// Set the date of the First day of Month
calendar.set(Calendar.DAY_OF_MONTH, 1);
// Roll it to previous day of Year to get the Last day of Month
calendar.roll(Calendar.DAY_OF_YEAR, -1);

Редактировать: Вы также должны установить часы, минуты, секунды и миллисекунды.

0 голосов
/ 25 марта 2017

ТЛ; др

ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );
ZonedDateTime start = today.with( TemporalAdjusters.firstDayOfMonth() )
                           .atStartOfDay( z ) ;
ZonedDateTime stop = today.with( TemporalAdjusters.firstDayOfNextMonth() )
                          .atStartOfDay( z ) ;
  • Для определения даты требуется часовой пояс.
  • Лучше указать явно, чем неявно полагаться на текущий часовой пояс JVM по умолчанию.

Избегайте старых классов

Вы используете проблемные старые классы даты и времени, теперь устаревшие, замененные классами java.time.

Использование java.time

Эта работа намного проще с классами java.time.

LocalDate

Класс LocalDate представляет значение только для даты без времени суток и без часового пояса.

Часовой пояс

Часовой пояс имеет решающее значение при определении даты. В любой момент времени дата меняется по всему земному шару в зависимости от зоны. Например, через несколько минут после полуночи в Париж Франция - это новый день, а еще "вчера" в Монреаль Квебек .

Укажите собственное имя часового пояса в формате continent/region, например America/Montreal, Africa/Casablanca или Pacific/Auckland. Никогда не используйте 3-4-буквенное сокращение, например EST или IST, так как они не истинные часовые пояса, не стандартизированы и даже не уникальны (!).

ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );

Всегда указывайте часовой пояс явно. Если опущен, текущий часовой пояс JVM по умолчанию применяется неявно. Это значение по умолчанию может быть изменено в любой момент с помощью любого кода любого приложения в JVM. Поэтому, если это важно, попросите пользователя указать желаемый / ожидаемый часовой пояс. Если это не имеет решающего значения, вы можете попросить явное значение по умолчанию, чтобы прояснить свои намерения в коде, а не двусмысленность, основанную на неявном значении по умолчанию.

ZoneId z = ZoneId.systemDefault();  // Get JVM’s current default time zone.
LocalDate today = LocalDate.now( z );

TemporalAdjuster

Интерфейс TemporalAdjuster предоставляет классы, которые могут настраивать значения даты и времени. Класс TemporalAdjusters предоставляет несколько удобных реализаций.

LocalDate firstOfThisMonth = today.with( TemporalAdjusters.firstDayOfMonth() );

ZonedDateTime

Чтобы превратить эту дату только в дату-время-день, примените часовой пояс ZoneId, чтобы получить объект ZonedDateTime.

Не думайте, что первый момент дня - 00:00:00. Из-за таких аномалий, как летнее время (DST), первый момент может быть чем-то вроде времени 01:00:00. Пусть java.time выяснит это, позвонив atStartOfDay.

ZonedDateTime zdtStartOfMonth = firstOfThisMonth.atStartOfDay( z );

Half-Open

Вы ошибаетесь, пытаясь определить последний момент месяца. Этот последний момент имеет бесконечно делимую дробную секунду. Попытка разрешить конкретную гранулярность нецелесообразна, поскольку разные системы используют разные гранулярности. Старые классы даты и времени Java используют миллисекунды, некоторые базы данных, такие как Postgres, используют микросекунды, классы java.time используют наносекунды, а другие системы используют другие варианты.

Более мудрым подходом, обычно используемым в работе с датой и временем для определения промежутков времени, является Half-Open, где начало включительно , а окончание эксклюзив . Это означает, что месяц начинается с первого момента первого числа месяца и продолжается, но не включает первый момент первого числа месяца , следующего за .

LocalDate firstOfNextMonth = today.with( TemporalAdjusters.firstOfNextMonth() );

Настройтесь на часовой пояс, чтобы получить определенный момент.

ZonedDateTime zdtStartOfNextMonth = firstOfNextMonth.atStartOfDay( z );

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

Кроме того, более короткий способ сказать часть «а» - «не до начала». Таким образом, мы можем спросить более просто: «Этот момент не перед началом И не перед окончанием?».

ZonedDateTime moment = ZonedDateTime.now( z );
Boolean spanContainsMoment = ( ! moment.isBefore( zdtStartOfMonth ) ) && ( moment.isBefore( zdtStartOfNextMonth ) ) ;

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

String output = zdtStartOfMonth.toString() + "/" + zdtStartOfNextMonth.toString() ;

Interval

Вы можете представитьt этот промежуток времени с использованием класса Interval в библиотеке ThreeTen-Extra .Этот класс отслеживает начало и конец в UTC как Instant объектов.Вы можете извлечь Instant из ZonedDateTime объекта.

Interval interval = Interval.of( zdtStartOfMonth.toInstant() , zdtStartOfNextMonth.toInstant() );

YearMonth

Кстати, вы можете найти класс YearMonthполезен в вашей работе.


О java.time

В java.time интегрирован фреймворкЯва 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) проект <a href="https://github.com/JakeWharton/ThreeTenABP" rel="nofollow noreferrer"> ThreeTenABP адаптируется ThreeTen-Backport (упомянуто выше).См. Как использовать ThreeTenABP… .

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

0 голосов
/ 02 ноября 2015
calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...