什么是计算ISO 8601周数的好的,简单的方法?

时间:2008-09-29 01:23:48

标签: java date

假设我有一个日期,即年,月和日,作为整数。用于计算给定日期所属周的ISO 8601 week number的好(正确),简洁且可读的算法是什么?我遇到了一些真正可怕的代码,这些代码让我觉得必须有更好的方法。

我希望用Java做这个,但是任何类型的面向对象语言都可以使用伪代码。

8 个答案:

答案 0 :(得分:14)

我相信你可以使用Calendar对象(只需将FirstDayOfWeek设置为Monday,将MinimalDaysInFirstWeek设置为4以使其符合ISO 8601)并调用get(Calendar.WEEK_OF_YEAR)。

答案 1 :(得分:8)

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
     

周总是完整的,而且   一年的第一周是那个   包括第一个星期四   年。这个定义可能意味着   一年的第一周开始于   去年和上周   明年结束。该   weekyear字段定义为参考   拥有本周的那一年,可能   与实际年份不同。

所有这一切的结果是,你创建一个DateTime对象,并调用相当容易混淆(但逻辑上)命名的getWeekOfWeekyear(),其中一周是ISO8601使用年份的特定的基于周的定义。

一般来说,joda-time是一个非常有用的API,我完全停止使用java.util.Calendar和java.util.Date,除非我需要与使用它们的API进行交互。

答案 2 :(得分:8)

/* 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);

答案 3 :(得分:8)

TL;博士

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 week支持java.time现已构建到{8}框架的Java 8及更高版本中。避免使用旧的和臭名昭着的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。例如,请注意2015年底格里高利和ISO 8601日历之间的差异:第52周和第5周。 1成为52& 53。

enter image description here

enter image description here

ThreeTen-Extra - YearWeek

YearWeek类将ISO 8601基于周的年份数周数同时表示为单个对象。这个类可以在ThreeTen-Extra项目中找到。该项目为Java内置的java.time类添加了功能。

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)。要指定星期几,请使用Java 8及更高版本中内置的DayOfWeek枚举。

LocalDate ld = yw.atDay( DayOfWeek.WEDNESDAY ) ;

关于 java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和& SimpleDateFormat

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore

答案 4 :(得分:3)

只是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()是MONDAY,getMinimalDaysInFirstWeek()是4,   哪些值用于首选标准的语言环境中。   可以通过调用setFirstDayOfWeek()和set来显式设置这些值   setMinimalDaysInFirstWeek()。

答案 5 :(得分:2)

Calendar类几乎可以使用,但基于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年,尽管今年的一周是正确的。 skaffman 提到的Joda-Time类确实正确处理了这个问题:

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)

答案 6 :(得分:1)

如果你想处于最前沿,你可以采取由Stephen Colebourne(Joda Fame)领导的the latest drop of the JSR-310 codebase(日期时间API)。它是一个流畅的界面,实际上是Joda的自下而上的重新设计。

答案 7 :(得分:-2)

这是相反的:给你一周中星期一的日期(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);
}