21

假设我有一个日期,即年、月和日,作为整数。什么是计算给定日期所在周的ISO 8601 周数的好(正确)、简洁且相当易读的算法?我遇到了一些真正可怕的代码,这让我认为肯定有更好的方法。

我希望在 Java 中执行此操作,但任何类型的面向对象语言的伪代码都可以。

4

8 回答 8

22
于 2016-01-25T07:18:38.090 回答
13

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

于 2008-09-29T01:29:37.400 回答
12
/* 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);
于 2013-11-25T17:32:55.210 回答
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(),其中 weekyear 是 ISO8601 使用的基于特定周的年份定义。

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

于 2008-09-29T07:32:17.217 回答
4

只有 Java.util.Calendar 可以解决问题:您可以创建一个 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提供的

当 getFirstDayOfWeek() 为 MONDAY 且 getMinimalDaysInFirstWeek() 为 4 时,星期确定与ISO 8601标准兼容,这些值用于首选标准的区域设置。这些值可以通过调用 setFirstDayOfWeek() 和 setMinimalDaysInFirstWeek() 显式设置。

于 2016-02-03T15:28:37.917 回答
2

Calendar 类几乎可以工作,但基于 ISO 周的年份与符合“Olson 的时区包”的系统报告的不一致。这个来自 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)
于 2014-05-23T21:04:04.453 回答
1

如果您想走在最前沿,您可以采用由 Stephen Colebourne(Joda Fame 的)领导的 JSR-310 代码库(日期时间 API)的最新版本。它是一个流畅的界面,实际上是对 Joda 的自下而上的重新设计。

于 2008-09-29T13:54:52.087 回答
-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);
}

于 2009-04-09T22:25:29.047 回答