Ответ 1
Я считаю, что вы можете использовать объект Calendar (просто установите FirstDayOfWeek на понедельник и MinimalDaysInFirstWeek на 4, чтобы он соответствовал стандарту ISO 8601) и вызывайте get (Calendar.WEEK_OF_YEAR).
Предположим, что у меня есть дата, т.е. год, месяц и день, как целые числа. Какой хороший (правильный), сжатый и достаточно читаемый алгоритм для вычисления ISO 8601 номер недели недели, в которую входит данная дата? Я столкнулся с каким-то поистине ужасным кодом, который заставляет меня думать, что должен быть лучший способ.
Я хочу сделать это на Java, но psuedocode для любого объектно-ориентированного языка - это нормально.
Я считаю, что вы можете использовать объект Calendar (просто установите FirstDayOfWeek на понедельник и MinimalDaysInFirstWeek на 4, чтобы он соответствовал стандарту ISO 8601) и вызывайте get (Calendar.WEEK_OF_YEAR).
Библиотека joda-time имеет календарь ISO8601 и предоставляет эту функциональность:
http://joda-time.sourceforge.net/cal_iso.html
yyyy-Www-dTHH: MM: SS.SSS Этот формат ISO8601 имеет следующие поля:
* four digit weekyear, see rules below * two digit week of year, from 01 to 53 * one digit day of week, from 1 to 7 where 1 is Monday and 7 is Sunday * two digit hour, from 00 to 23 * two digit minute, from 00 to 59 * two digit second, from 00 to 59 * three decimal places for milliseconds if required
Неделя всегда полны, а первая неделя года - та, которая включает первый четверг год. Это определение может означать, что первая неделя года начинается в в прошлом году и на прошлой неделе заканчивается в следующем году. недельное поле определено для обозначения год, который владеет неделей, что может отличаются от фактического года.
Результат всего, что вы создаете объект DateTime, и вызываете довольно смутно (но логически) имя getWeekOfWeekyear(), где weekyear является конкретным недельным определением года, используемого ISO8601.
В общем, joda-time - это фантастически полезный API, я полностью прекратил использование java.util.Calendar и java.util.Date, за исключением случаев, когда мне нужно взаимодействовать с API, который их использует.
/* Build a calendar suitable to extract ISO8601 week numbers
* (see http://en.wikipedia.org/wiki/ISO_8601_week_number) */
Calendar calendar = Calendar.getInstance();
calendar.setMinimalDaysInFirstWeek(4);
calendar.setFirstDayOfWeek(Calendar.MONDAY);
/* Set date */
calendar.setTime(date);
/* Get ISO8601 week number */
calendar.get(Calendar.WEEK_OF_YEAR);
LocalDate.of( 2015 , 12 , 30 )
.get (
IsoFields.WEEK_OF_WEEK_BASED_YEAR
)
53
... или...
org.threeten.extra.YearWeek.from (
LocalDate.of( 2015 , 12 , 30 )
)
2015-W53
Поддержка ISO 8601 неделя теперь встроена в Java 8 и более поздние версии, в java.time. Избегайте старых и печально известных проблемных классов java.util.Date/.Calendar, поскольку они были вытеснены java.time.
Эти новые классы java.time включают LocalDate
для значения даты только без времени или часового пояса. Обратите внимание, что вы должны указать часовой пояс, чтобы определить "сегодня, поскольку дата не одинакова во всем мире".
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );
Или укажите год, месяц и день месяца, как указано в вопросе.
LocalDate localDate = LocalDate.of( year , month , dayOfMonth );
Класс IsoFields
предоставляет информацию в соответствии со стандартом ISO 8601, включая недельный год для недельного года.
int calendarYear = now.getYear();
int weekNumber = now.get ( IsoFields.WEEK_OF_WEEK_BASED_YEAR );
int weekYear = now.get ( IsoFields.WEEK_BASED_YEAR );
В начале/в конце года недельный год может составлять ± 1, отличный от календарного года. Например, обратите внимание на разницу между григорианскими и ISO 8601 календарями на конец 2015 года: недели 52 и 1 становятся 52 и 53.
YearWeek
Класс YearWeek
представляет как недельный номер по ISO 8601, так и номер недели вместе как один объект. Этот класс находится в проекте ThreeTen-Extra. Проект добавляет функциональность для классов java.time, встроенных в Java.
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
YearWeek yw = YearWeek.now( zoneId ) ;
Сгенерируйте a YearWeek
с даты.
YearWeek yw = YearWeek.from (
LocalDate.of( 2015 , 12 , 30 )
)
Этот класс может генерировать и анализировать строки в стандартном формате ISO 8601.
String output = yw.toString() ;
2015-W53
YearWeek yw = YearWeek.parse( "2015-W53" ) ;
Вы можете извлечь номер недели или номер недели за неделю.
int weekNumber = yw.getWeek() ;
int weekBasedYearNumber = yw.getYear() ;
Вы можете создать конкретную дату (LocalDate
), указав желаемый день недели, который будет найден на этой неделе. Чтобы указать день недели, используйте перечисление DayOfWeek
, встроенное в Java 8 и более поздние версии.
LocalDate ld = yw.atDay( DayOfWeek.WEDNESDAY ) ;
Просто Java.util.Calendar может сделать трюк: Вы можете создать экземпляр календаря и установить Первый день недели и Минимальные дни на первой неделе
Calendar calendar = Calendar.getInstance();
calendar.setMinimalDaysInFirstWeek(4);
calendar.setFirstDayOfWeek(Calendar.MONDAY);
calendar.setTime(date);
// Now you are ready to take the week of year.
calendar.get(Calendar.WEEK_OF_YEAR);
Это обеспечивается javaDoc
Определение недели совместимо со стандартом ISO 8601, когда getFirstDayOfWeek() - ПОНЕДЕЛЬНИК и getMinimalDaysInFirstWeek() - 4, какие значения используются в местах, где стандарт является предпочтительным. Эти значения могут быть явно заданы вызовом setFirstDayOfWeek() и setMinimalDaysInFirstWeek().
Если вы хотите быть на краю кровотечения, вы можете взять последнее падение кодовой базы JSR-310 (API Time Time) который возглавляет Стивен Коулборн (от славы Джоды). Его свободный интерфейс и фактически представляет собой восходящий дизайн Joda.
Календарный класс почти работает, но год, основанный на ISO, не совпадает с тем, что сообщает система совместимых с Olson Timezone пакетов. Этот пример из окна Linux показывает, как недельное значение года (2009) может отличаться от фактического года (2010):
$ TZ=UTC /usr/bin/date --date="2010-01-01 12:34:56" "+%a %b %d %T %Z %%Y=%Y,%%G=%G %%W=%W,%%V=%V %s"
Fri Jan 01 12:34:56 UTC %Y=2010,%G=2009 %W=00,%V=53 1262349296
Но класс Java Calendar по-прежнему сообщает 2010 год, хотя неделя года верна. Классы Joda-Time, упомянутые skaffman, справляются с этим правильно:
import java.util.Calendar;
import java.util.TimeZone;
import org.joda.time.DateTime;
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.setTimeInMillis(1262349296 * 1000L);
cal.setMinimalDaysInFirstWeek(4);
cal.setFirstDayOfWeek(Calendar.MONDAY);
System.out.println(cal.get(Calendar.WEEK_OF_YEAR)); // %V
System.out.println(cal.get(Calendar.YEAR)); // %G
DateTime dt = new DateTime(1262349296 * 1000L);
System.out.println(dt.getWeekOfWeekyear()); // %V
System.out.println(dt.getWeekyear()); // %G
Запуск этой программы показывает:
53 2010 53 2009
Таким образом, номер недели ISO 8601 верен из Календаря, но недельный год - нет.
Страница man для отчетов strftime (3):
%G The ISO 8601 week-based year (see NOTES) with century as a decimal number. The 4-digit year corresponding to the ISO week number (see %V). This has the same for‐ mat and value as %Y, except that if the ISO week number belongs to the previous or next year, that year is used instead. (TZ)
это наоборот: дается дата понедельника недели (в perl)
use POSIX qw(mktime);
use Time::localtime;
sub monday_of_week {
my $year=shift;
my $week=shift;
my $p_date=shift;
my $seconds_1_jan=mktime(0,0,0,1,0,$year-1900,0,0,0);
my $t1=localtime($seconds_1_jan);
my $seconds_for_week;
if (@$t1[6] < 5) {
#first of january is a thursday (or below)
$seconds_for_week=$seconds_1_jan+3600*24*(7*($week-1)[email protected]$t1[6]+1);
} else {
$seconds_for_week=$seconds_1_jan+3600*24*(7*($week-1)[email protected]$t1[6]+8);
}
my $wt=localtime($seconds_for_week);
$$p_date=sprintf("%02d/%02d/%04d",@$wt[3],@$wt[4]+1,@$wt[5]+1900);
}