整数として日付、つまり年、月、日があるとします。指定された日付が属する週のISO 8601 週番号を計算するための、適切な (正しい) 簡潔でかなり読みやすいアルゴリズムは何ですか? もっと良い方法があるに違いないと思わせる、本当に恐ろしいコードに出くわしました。
私はこれをJavaでやろうとしていますが、あらゆる種類のオブジェクト指向言語の疑似コードで問題ありません。
Calendar オブジェクトを使用して (FirstDayOfWeek を Monday に設定し、MinimalDaysInFirstWeek を 4 に設定して ISO 8601 に準拠させます)、get(Calendar.WEEK_OF_YEAR) を呼び出すことができると思います。
/* 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);
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 です。私は、それらを使用する API とのインターフェースが必要な場合を除いて、java.util.Calendar と java.util.Date を完全に使用するのをやめました。
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);
getFirstDayOfWeek() が MONDAY で getMinimalDaysInFirstWeek() が 4 の場合、週の決定はISO 8601標準と互換性があります。これらの値は、標準が優先されるロケールで使用されます。これらの値は、setFirstDayOfWeek() および setMinimalDaysInFirstWeek() を呼び出して明示的に設定できます。
Calendar クラスはほとんど機能しますが、ISO の週ベースの年は、"Olson's Timezone package" 準拠のシステムが報告するものと一致しません。次の 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 の週番号は Calendar から正しいですが、週ベースの年は正しくありません。
strftime(3) レポートの man ページ:
%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)
最先端に行きたい場合は、(Joda Fame の) Stephen Colebourne が率いるJSR-310 コードベース (Date Time API) の最新のドロップを利用できます。流暢なインターフェイスであり、効果的に Joda をボトムアップで再設計したものです。
これは逆です: 月曜日の日付を返します (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);
}