Какой хороший и простой способ вычислить номер недели ISO 8601? - PullRequest
16 голосов
/ 29 сентября 2008

Предположим, у меня есть дата, то есть год, месяц и день, в виде целых чисел. Какой хороший (правильный), краткий и достаточно читаемый алгоритм для вычисления ISO 8601 номера недели недели, в которую попадает данная дата? Я натолкнулся на какой-то действительно ужасный код, который заставляет меня думать, что должен быть лучший путь.

Я хочу сделать это на Java, но psuedocode для любого вида объектно-ориентированного языка подходит.

Ответы [ 8 ]

14 голосов
/ 29 сентября 2008

Полагаю, вы можете использовать объект Calendar (просто установите FirstDayOfWeek на Monday и MinimalDaysInFirstWeek на 4, чтобы он соответствовал ISO 8601) и вызовите get (Calendar.WEEK_OF_YEAR).

8 голосов
/ 25 января 2016

ТЛ; др

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

java.time

Поддержка недели 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.

enter image description here

enter image description here

ThreeTen-Extra - YearWeek

Класс YearWeek представляет номер года в формате ISO 8601 и номер недели вместе как один объект. Этот класс находится в проекте ThreeTen-Extra . Проект добавляет функциональность классам java.time, встроенным в Java.

ZoneId zoneId = ZoneId.of ( "America/Montreal" );
YearWeek yw = YearWeek.now( zoneId ) ;

Создать 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.time

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

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

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

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

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

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

8 голосов
/ 25 ноября 2013
/* 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);
8 голосов
/ 29 сентября 2008

Библиотека joda-time имеет календарь ISO8601 и предоставляет следующие функции:

http://joda -time.sourceforge.net / cal_iso.html

гггг-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

Недели всегда завершены, и первая неделя года это та, которая включает в себя первый четверг год. Это определение может означать, что первая неделя года начинается в предыдущий год и последняя неделя заканчивается в следующем году. поле weekyear определено как ссылка на год, которому принадлежит неделя, которая может отличаются от фактического года.

Результатом всего этого является то, что вы создаете объект DateTime и вызываете довольно запутанное (но логически) имя getWeekOfWeekyear (), где weekyear - это определенное недельное определение года, используемое ISO8601.

В общем, joda-time - это фантастически полезный API, я перестал полностью использовать java.util.Calendar и java.util.Date, за исключением случаев, когда мне нужно взаимодействовать с API, который их использует.

3 голосов
/ 03 февраля 2016

Просто 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 ().

2 голосов
/ 24 мая 2014

Класс Calendar почти работает, но год, основанный на неделях ISO, не совпадает с тем, что сообщает система, совместимая с пакетом Olson's 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

Но класс Calendar в Java все еще сообщает о 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 является правильным из Календаря, а недельный год - нет.

Страница руководства для 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)
1 голос
/ 29 сентября 2008

Если вы хотите быть на переднем крае, вы можете взять последнюю каплю кодовой базы JSR-310 (Date Time API), которую возглавляет Стивен Колебурн (из Joda Fame). Это свободный интерфейс и эффективный дизайн Joda.

0 голосов
/ 10 апреля 2009

наоборот: выдает дату понедельника недели (в 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)-@$t1[6]+1);
    } else {
        $seconds_for_week=$seconds_1_jan+3600*24*(7*($week-1)-@$t1[6]+8);
    }
    my $wt=localtime($seconds_for_week);
    $$p_date=sprintf("%02d/%02d/%04d",@$wt[3],@$wt[4]+1,@$wt[5]+1900);
}

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