CalendarクラスのWEEK_OF_YEAR
CalendarクラスのWEEK_OF_YEARは、一年の中で何週目かを返すfieldなのですが、week1:第一週目をいつから数えるかは、getFirstDayOfWeek() (週が何曜日からはじまるか)とgetMinimalDaysInFirstWeek()(最初の週を何日以上とするか)によって計算されます。
WEEK_OF_YEAR
public static final int WEEK_OF_YEAR
get および set のためのフィールド値で、現在の年の何週目かを示します。getFirstDayOfWeek() および getMinimalDaysInFirstWeek() で定義される年の最初の週には、値 1 が使用されます。サブクラスでは、年の最初の週より前の日に対して WEEK_OF_YEAR の値が定義されます。
今年2016年の 1/1 は金曜日なので、例えば次の月曜日1/4の週をweek1と数えるようにするためには、
setFirstDayOfWeek(Calendar.MONDAY);
setMinimalDaysInFirstWeek(4);
とします。
Android だと、、、
しかし、Android apk でコレを実装してみると、うまく動かない。。。
例えばこの記事を執筆している今日は2/10で、以下のようなコードだとweek6となってほしいのですが、week7と出力されてしまう。
private void getWeekNumber() {
Calendar calendar = Calendar.getInstance();
Log.d("###", "Original week:" + calendar.get(Calendar.WEEK_OF_YEAR));
calendar.setFirstDayOfWeek(Calendar.MONDAY);
calendar.setMinimalDaysInFirstWeek(4);
Log.d("###", "Modified week:" + calendar.get(Calendar.WEEK_OF_YEAR));
}
実行結果
Original week:7
Modified week:7
あれれと少しの間ハマって試行錯誤した結果、何かしらのfieldにset()すれば、期待どおりにweek6と出力されることがわかりました。
private void getWeekNumber() {
Calendar calendar = Calendar.getInstance();
Log.d("###", "Time:" + calendar.getTime());
Log.d("###", "Original week:" + calendar.get(Calendar.WEEK_OF_YEAR));
calendar.setFirstDayOfWeek(Calendar.MONDAY);
calendar.setMinimalDaysInFirstWeek(4);
Log.d("###", "Time:" + calendar.getTime());
Log.d("###", "Modified week:" + calendar.get(Calendar.WEEK_OF_YEAR));
calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR)); // これを実行した以降は正しい結果に
Log.d("###", "Time:" + calendar.getTime());
Log.d("###", "Modified week:" + calendar.get(Calendar.WEEK_OF_YEAR));
}
実行結果
Time:Wed Feb 10 22:36:22 JST 2016
Original week:7
Time:Wed Feb 10 22:36:22 JST 2016
Modified week:7
Time:Wed Feb 10 22:36:22 JST 2016
Modified week:6 // 期待通り
という不思議な結果です。getTime()の値は全部同じなのに、、、
Androidのバグ??
と思ってCalendarクラスの実装を見てみる。
まずはget(). fieldの値を返す前にcomplete() を呼んでます。
898 public int get(int field) {
899 complete();
900 return fields[field];
901 }
complete() の中では、computeTime() というやつを呼んで、最新の設定をもとにCalendar情報を計算しているのでしょうかね、名前からして。それを実行するためには、areFieldsSet が falseである必要がありますと。
838 protected void complete() {
839 if (!isTimeSet) {
840 computeTime();
841 isTimeSet = true;
842 }
843 if (!areFieldsSet) {
844 computeFields();
845 areFieldsSet = true;
846 }
847 }
そんでsetFirstDayOfWeek() と setMinimalDaysInFirstWeek()をみてみると
1174 public void setFirstDayOfWeek(int value) {
1175 firstDayOfWeek = value;
1176 }
...
1189 public void setMinimalDaysInFirstWeek(int value) {
1190 minimalDaysInFirstWeek = value;
1191 }
値をセットしているだけで、特にフラグなどいじってませんので、このままget() しても再計算された値が取得できないように見えます。
一方 set() では、areFieldsSet に false をセットしてます。
つまり、あとでcomputeする必要がありまっせということでしょうかね。
1123 public void set(int field, int value) {
1124 fields[field] = value;
1125 isSet[field] = true;
1126 areFieldsSet = isTimeSet = false; // ここ
...
set()を呼ぶと問題を回避できるのはこのためでしょうかね。
ということで、
Androidのバグ??
その他の環境では
ちなみにmacのJava SE 1.8.0_45-b14では、こういった動作にはならなかった。
import java.util.Calendar;
public class MyCal {
public static void main(String[] args){
Calendar calendar = Calendar.getInstance();
System.out.println("Original:" + calendar.get(Calendar.WEEK_OF_YEAR));
calendar.setFirstDayOfWeek(Calendar.MONDAY);
calendar.setMinimalDaysInFirstWeek(4);
System.out.println("Modified:" + calendar.get(Calendar.WEEK_OF_YEAR));
}
}
実行結果
Original:7
Modified:6
多分、、、
setFirstDayOfWeek() をした後も、computeFields()しなきゃいけないのかなと。
例えばメソッドの中で
areFieldsSet = false
するとか。