tl; dr
Используйте современные java.time классы.Никогда не используйте Date
/ Calendar
.
29 февраля 2020 г. вернет меня на следующий день: 1 марта 2019 г.
LocalDate // Represent a date-only value, without time-of-day and without time zone.
.of( 2020 , Month.FEBRUARY , 29 ) // Specify a date. Here, Leap Day of 2020. Returns a `LocalDate` object.
.minusYears( 1 ) // Intelligently move backwards in time one year. Returns another `LocalDate` object, per immutable objects pattern.
.toString() // Generate text representing the value of this date, in standard ISO 8601 format of YYYY-MM-DD.
Когда запустится на IdeOne.com :
2019-02-28
Избегайте устаревших классов даты и времени
Вы используете ужасные классы даты и времени, которые теперь унаследованы, полностью вытесненные java.time классы.
LocalDate
Класс LocalDate
представляет значение только для даты без времени суток и без часовой пояс или смещение от UTC .
Часовой пояс имеет решающее значение при определении даты.В любой момент времени дата меняется по всему земному шару в зависимости от зоны.Например, через несколько минут после полуночи в Париж Франция - это новый день, в то время как "вчера" в Монреаль Квебек .
Если часовой пояс не указан,JVM неявно применяет свой текущий часовой пояс по умолчанию.Это значение по умолчанию может измениться в любой момент во время выполнения (!), Поэтому ваши результаты могут отличаться.Лучше явно указать желаемый / ожидаемый часовой пояс в качестве аргумента.
Укажите собственное имя часового пояса в формате Continent/Region
, например America/Montreal
, Africa/Casablanca
,или Pacific/Auckland
.Никогда не используйте 2-4 буквенные сокращения, такие как EST
или IST
, так как они не истинные часовые пояса, не стандартизированы и даже не уникальны (!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
Если вы хотите использовать текущий часовой пояс JVM по умолчанию, запросите его и передайте в качестве аргумента.Если опущен, код становится неоднозначным для чтения, поскольку мы точно не знаем, намеревались ли вы использовать значение по умолчанию или если вы, как и многие другие программисты, не знали о проблеме.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Или укажитесвидание.Вы можете установить месяц по номеру, с нормальным номером 1-12 для января-декабря.
LocalDate ld = LocalDate.of( 2020 , 2 , 29 ) ; // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.
Или, лучше, использовать предварительно определенные объекты перечисления Month
, по одному на каждый месяц года.Совет: используйте эти Month
объекты по всей вашей кодовой базе, а не просто целое число, чтобы сделать ваш код более самодокументируемым, обеспечить допустимые значения и обеспечить type-safety .То же самое для Year
& YearMonth
.
LocalDate ld = LocalDate.of( 2020 , Month.FEBRUARY , 29 ) ;
Математика даты и времени
Математика даты и времени может быть выполнена внесколько способов с * java.time.Один из способов - позвонить по номеру plus
или minus
и передать Period
или Duration
.Другим способом является вызов вспомогательных методов, таких как plusYears
или minusYears
.
Класс LocalDate
, кажется, дает именно то поведение, которое вы хотите с помощью этих методов.
Для ясности:
- 2020 - високосный год.Таким образом, 2020-02-29 является действительной датой.
- 2018, 2019 и 2021 - нет.29-е число не действительная дата в эти годы.
См. Примеры ниже запустить в прямом эфире на IdeOne.com .
Свисокосный год
Давайте начнем с високосного дня, 29-го числа, затем добавим год и вычтем год.Мы ожидаем увидеть 28-е место в обоих случаях.
LocalDate leapDay2020 = LocalDate.of( 2020 , Month.FEBRUARY , 29 );
LocalDate yearBeforeLeap = leapDay2020.minusYears( 1 );
LocalDate yearAfterLeap = leapDay2020.plusYears( 1 );
System.out.println( "leapDay2020.toString(): " + leapDay2020 );
System.out.println( "yearBeforeLeap.toString(): " + yearBeforeLeap );
System.out.println( "yearAfterLeap.toString(): " + yearAfterLeap );
Действительно, это то, что мы получаем.
leapDay2020.toString (): 2020-02-29
yearBeforeLeap.toString (): 2019-02-28
yearAfterLeap.toString (): 2021-02-28
Из неисключительного года
Теперь давайтеначните не в високосный год 28 февраля, затем добавьте и вычтите год.Мы ожидаем увидеть 28-е место во всех трех.29-й в високосном году 2020 игнорируется.
LocalDate nonLeap2019 = LocalDate.of( 2019 , Month.FEBRUARY , 28 );
LocalDate yearBeforeNonLeapIntoNonLeap = nonLeap2019.minusYears( 1 );
LocalDate yearBeforeNonLeapIntoLeap = nonLeap2019.plusYears( 1 );
System.out.println( "nonLeap2019.toString(): " + nonLeap2019 );
System.out.println( "yearBeforeNonLeapIntoNonLeap.toString(): " + yearBeforeNonLeapIntoNonLeap );
System.out.println( "yearBeforeNonLeapIntoLeap.toString(): " + yearBeforeNonLeapIntoLeap );
nonLeap2019.toString (): 2019-02-28
yearBeforeNonLeapIntoNonLeap.toString (): 2018-02-28
yearBeforeNonLeapIntoLeap.toString (): 2020-02-28
О java.time
* java.time Framework встроен в Java 8 и более поздние версии.Эти классы вытесняют старые классные устаревшие классы даты и времени, такие как java.util.Date
, Calendar
и & SimpleDateFormat
.
йe Проект 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
и more .