ТЛ; др
Используйте классы java.time , перенесенные на раннюю версию Android.
Формулировка проблемы: От текущей даты перейдите к предыдущему или тому же понедельнику, затем перейдите к понедельнику стандартной недели ISO 8601 с номером недели по этой дате, добавьте одну неделю и сгенерируйте текст в стандартный формат ISO 8601 для итоговой даты.
org.threeten.bp.LocalDate.now( // Represent a date-only value, without time-of-day and without time zone.
ZoneId.of( "Europe/London" ) // Determining current date requires a time zone. For any given moment, the date and time vary around the globe by zone.
) // Returns a `LocalDate`. Per immutable objects pattern, any further actions generate another object rather than changing (“mutating”) this object.
.with(
TemporalAdjusters.previousOrSame( // Move to another date.
DayOfWeek.MONDAY // Specify desired day-of-week using `DayOfWeek` enum, with seven objects pre-defined for each day-of-week.
)
) // Renders another `LocalDate` object.
.with(
IsoFields.WEEK_OF_WEEK_BASED_YEAR ,
10
)
.plusWeeks( 1 )
.toString()
2018-03-12
Упростить задачу
При поиске загадочного или ошибочного поведения просто запрограммируйте до минимума, необходимого для воспроизведения проблемы. В этом случае удалите предположительно нерелевантный код GUI, чтобы сосредоточиться на классах даты и времени.
Как и в научном эксперименте, контроль различных переменных. В этом случае часовой пояс и Locale
влияют на поведение Calendar
. С одной стороны, определение недели в Calendar
меняется на Locale
. Так что определите эти аспекты явным образом с помощью жесткого кодирования.
Установите конкретную дату и время, так как разные времена в разные дни в разных зонах могут влиять на поведение.
Calendar
- это суперкласс с различными реализациями. Если вы ожидаете GregorianCalendar
, используйте это явно при отладке.
Итак, попробуйте выполнить что-то вроде следующего в сценариях инструмента, чтобы устранить проблему.
TimeZone tz = TimeZone.getTimeZone( "America/Los_Angeles" );
Locale locale = Locale.US;
GregorianCalendar gc = new GregorianCalendar( tz , locale );
gc.set( 2018 , 9- 1 , 3 , 0 , 0 , 0 ); // Subtract 1 from month number to account for nonsensical month numbering used by this terrible class.
gc.set( Calendar.MILLISECOND , 0 ); // Clear fractional second.
System.out.println( "gc (original): " + gc.toString() );
System.out.println( gc.toZonedDateTime() + "\n" ); // Generate a more readable string, using modern java.time classes. Delete this line if running on Android <26.
int week = 10;
gc.set( Calendar.WEEK_OF_YEAR , week );
System.out.println( "gc (week=10): " + gc.toString() );
System.out.println( gc.toZonedDateTime() + "\n" );
int weekAfter = ( week + 1 );
gc.set( Calendar.WEEK_OF_YEAR , weekAfter );
System.out.println( "gc (weekAfter): " + gc.toString() );
System.out.println( gc.toZonedDateTime() + "\n" );
при запуске.
gc (оригинал): java.util.GregorianCalendar [time = ?, areFieldsSet = false, areAllFieldsSet = true, lenient = true, zone = sun.util.calendar.ZoneInfo [id = "America / Los_Angeles", offset = -28800000, dstSavings = 3600000, useDaylight = верно, переходы = 185, lastRule = java.util.SimpleTimeZone [ID = Америка / Los_Angeles, смещение = -28800000, dstSavings = 3600000, useDaylight = верно, StartYear = 0, StartMode = 3, StartMonth = 2, startDay = 8, startDayOfWeek = 1, начальный промежуток = 7200000, startTimeMode = 0, endMode = 3, endMonth = 10, endDay = 1, endDayOfWeek = 1, EndTime = 7200000, endTimeMode = 0]], firstDayOfWeek = 1, minimalDaysInFirstWeek = 1, ЭРА = 1, год = 2018, МЕСЯЦ = 8, WEEK_OF_YEAR = 36, WEEK_OF_MONTH = 2, DAY_OF_MONTH = 3, day_of_year = 251, DAY_OF_WEEK = 7, DAY_OF_WEEK_IN_MONTH = 2, AM_PM = 1, ЧАС = 2, HOUR_OF_DAY = 0, МИНУТНЫЙ = 0, ВТОРОЙ = 0, миллисекунды = 0, ZONE_OFFSET = -28800000, DST_OFFSET = 3600000]
2018-09-03T00: 00-07: 00 [Америка / Los_Angeles]
gc (неделя = 10): java.util.GregorianCalendar [time = ?, areFieldsSet = false, areAllFieldsSet = true, lenient = true, zone = sun.util.calendar.ZoneInfo [id = "America / Los_Angeles", смещение = -28800000, dstSavings = 3600000, useDaylight = верно, переходы = 185, lastRule = java.util.SimpleTimeZone [ID = Америка / Los_Angeles, смещение = -28800000, dstSavings = 3600000, useDaylight = верно, StartYear = 0, StartMode = 3, StartMonth = 2, startDay = 8, startDayOfWeek = 1, начальный промежуток = 7200000, startTimeMode = 0, endMode = 3, endMonth = 10, endDay = 1, endDayOfWeek = 1, EndTime = 7200000, endTimeMode = 0]], firstDayOfWeek = 1, minimalDaysInFirstWeek = 1, ЭРА = 1, год = 2018, МЕСЯЦ = 8, WEEK_OF_YEAR = 10, WEEK_OF_MONTH = 2, DAY_OF_MONTH = 3, day_of_year = 246, DAY_OF_WEEK = 2, DAY_OF_WEEK_IN_MONTH = 1, AM_PM = 0, ЧАС = 0, HOUR_OF_DAY = 0, МИНУТНЫЙ = 0, ВТОРОЙ = 0, миллисекунды = 0, ZONE_OFFSET = -28800000, DST_OFFSET = 3600000]
2018-03-05T00: 00-08: 00 [Америка / Los_Angeles]
gc (weekAfter): java.util.GregorianCalendar [time = ?, areFieldsSet = false, areAllFieldsSet = true, lenient = true, zone = sun.util.calendar.ZoneInfo [id = "America / Los_Angeles", offset = -28800000, dstSavings = 3600000, useDaylight = верно, переходы = 185, lastRule = java.util.SimpleTimeZone [ID = Америка / Los_Angeles, смещение = -28800000, dstSavings = 3600000, useDaylight = верно, StartYear = 0, StartMode = 3, StartMonth = 2, startDay = 8, startDayOfWeek = 1, начальный промежуток = 7200000, startTimeMode = 0, endMode = 3, endMonth = 10, endDay = 1, endDayOfWeek = 1, EndTime = 7200000, endTimeMode = 0]], firstDayOfWeek = 1, minimalDaysInFirstWeek = 1, ЭРА = 1, год = 2018, МЕСЯЦ = 2, WEEK_OF_YEAR = 11, WEEK_OF_MONTH = 2, DAY_OF_MONTH = 5, day_of_year = 64, DAY_OF_WEEK = 2, DAY_OF_WEEK_IN_MONTH = 1, AM_PM = 0, ЧАС = 0, HOUR_OF_DAY = 0, МИНУТНЫЙ = 0, ВТОРОЙ = 0, миллисекунды = 0, ZONE_OFFSET = -28800000, DST_OFFSET = 0]
2018-03-12T00: 00-07: 00 [Америка / Los_Angeles]
java.time
Действительно, ваша проблема спорная, потому что вам вообще не следует использовать ужасный старый класс Calendar
. Это часть проблемных старых классов даты и времени, которые много лет назад были вытеснены современными java.time классами. Для ранних версий Android см. Последние маркеры внизу.
В Calendar
/ GregorianCalendar
определение недели изменяется на Locale
, Не так в java.time по умолчанию, который использует стандарт ISO 8601 определение недели .
- Неделя № 1 имеет первый четверг календарного года.
- Понедельник - первый день недели.
- Недельный год имеет 52 или 53 недели.
- Первые / последние несколько дней календаря могут появиться в предыдущей / следующей неделе на основе года.
LocalDate
Класс LocalDate
представляет значение только для даты без времени суток и без часового пояса.
Часовой пояс имеет решающее значение при определении даты. В любой момент времени дата меняется по всему земному шару в зависимости от зоны. Например, через несколько минут после полуночи в Париж Франция - это новый день, хотя все еще "вчера" в Монреаль Квебек .
Если часовой пояс не указан, JVM неявно применяет свой текущий часовой пояс по умолчанию. Это значение по умолчанию может измениться в любой момент во время выполнения (!), Поэтому ваши результаты могут отличаться. Лучше указать в качестве аргумента ваш желаемый / ожидаемый часовой пояс .
Укажите собственное имя часового пояса в формате 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 применяется неявно. Лучше быть явным, поскольку значение по умолчанию может быть изменено в любой момент во время выполнения любым кодом в любом потоке любого приложения в JVM.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Или укажите дату. Вы можете установить месяц по номеру, с нормальным номером 1-12 для января-декабря.
LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ; // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.
Или, лучше, использовать предварительно определенные объекты перечисления Month
, по одному на каждый месяц года. Совет: используйте эти Month
объекты по всей вашей кодовой базе, а не просто целое число, чтобы сделать ваш код более самодокументируемым, обеспечить допустимые значения и обеспечить безопасность типов .
LocalDate ld = LocalDate.of( 2018 , Month.SEPTEMBER , 3 ) ;
TemporalAdjuster
Чтобы перейти к предыдущему понедельнику или остаться на дату, если уже понедельник, используйте реализацию TemporalAdjuster
, предоставленную в классе TemporalAdjusters
. Укажите желаемый день недели с помощью DayOfWeek
enum.
LocalDate monday = ld.with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) ) ;
IsoFields
Классы java.time имеют ограниченную поддержку в течение нескольких недель. Используйте класс IsoFields
с его константами WEEK_OF_WEEK_BASED_YEAR
& WEEK_BASED_YEAR
.
LocalDate mondayOfWeekTen = monday.with( IsoFields.WEEK_OF_WEEK_BASED_YEAR , 10 ) ;
ISO 8601
Стандарт ISO 8601 определяет множество полезных практических форматов для представления значений даты и времени в виде текста. Это включает недели. Давайте сгенерируем такой текст как вывод.
String weekLaterOutput =
weekLater
.get( IsoFields.WEEK_BASED_YEAR )
+ "-W"
+ String.format( "%02d" , weekLater.get( IsoFields.WEEK_OF_WEEK_BASED_YEAR ) )
+ "-"
+ weekLater.getDayOfWeek().getValue()
; // Generate standard ISO 8601 output. Ex: 2018-W11-1
Дамп на консоль.
System.out.println("ld.toString(): " + ld);
System.out.println("monday.toString(): " +monday);
System.out.println("weekLater.toString(): " + weekLater);
System.out.println( "weekLaterOutput: " + weekLaterOutput ) ;
При запуске.
ld.toString (): 2018-09-03
monday.toString (): 2018-09-03
weekLater.toString (): 2018-03-12
weekLaterOutput: 2018-W11-1
Совет для Java (не для Android): Если вы выполняете много работы неделями, рассмотрите возможность добавления библиотеки ThreeTen-Extra для доступа к классу YearWeek
.
О java.time
Фреймворк 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?