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
するとか。