CalendarクラスのWEEK_OF_YEAR
CalendarクラスのWEEK_OF_YEAR
CalendarクラスのWEEK_OF_YEARは、一年の中で何週目かを返すfieldなのですが、week1:第一週目をいつから数えるかは、getFirstDayOfWeek()
(週が何曜日からはじまるか)とgetMinimalDaysInFirstWeek()
(最初の週を何日以上とするか)によって計算されます。
https://docs.oracle.com/javase/jp/6/api/java/util/Calendar.html#WEEK_OF_YEAR
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クラスの実装を見てみる。
http://tools.oesf.biz/android-6.0.0_r1.0/xref/libcore/luni/src/main/java/java/util/Calendar.java
まずは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
- 作者: 中山清喬,国本大悟
- 出版社/メーカー: インプレス
- 発売日: 2014/08/07
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (18件) を見る
スッキリわかる Java入門 実践編 第2版 (スッキリシリーズ)
- 作者: 中山清喬
- 出版社/メーカー: インプレス
- 発売日: 2014/09/22
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (7件) を見る
EFFECTIVE JAVA 第2版 (The Java Series)
- 作者: Joshua Bloch,柴田芳樹
- 出版社/メーカー: 丸善出版
- 発売日: 2014/03/11
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (9件) を見る