質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

2回答

1174閲覧

JavaのGregorianCalendarが暦週の基準年にそって期待通り繰り下がらない

key_FoolyCooly

総合スコア19

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

1クリップ

投稿2019/07/07 15:43

編集2019/07/09 03:15

実現したいこと

JavaのGregorianCalendarが暦週の基準年にそって繰り下がるのを確認したい。

該当のソースコード

Java

1 // YYYY表記 2 SimpleDateFormat YYYY = new SimpleDateFormat("YYYY年MM月dd日 E曜日"); 3 4 // 年が繰り上がる場合 5 Calendar calDownerYYYY11 = new GregorianCalendar(); 6 calDownerYYYY11.set(1997, 11, 31); 7 String dateDownerYYYY11 = YYYY.format(calDownerYYYY11.getTime()); 8 int weekOfYearDownerYYYY11 = calDownerYYYY11.get(Calendar.WEEK_OF_YEAR); 9 10 // 年が繰り下がる場合 11 Calendar calDownerYYYY01 = new GregorianCalendar(); 12 calDownerYYYY01.setFirstDayOfWeek(Calendar.SUNDAY); 13 calDownerYYYY01.setMinimalDaysInFirstWeek(4); 14 calDownerYYYY01.set(1998, 0, 1); 15 String dateDownerYYYY01 = YYYY.format(calDownerYYYY01.getTime()); 16 int weekOfYearDownerYYYY01 = calDownerYYYY01.get(Calendar.WEEK_OF_YEAR); 17 18 System.out.println("週の最初の曜日:" + calDownerYYYY11.getFirstDayOfWeek()); 19 System.out.println("何日以上あると最初の週と見做すか:" + calDownerYYYY11.getMinimalDaysInFirstWeek()); 20 System.out.println("日付:" + dateDownerYYYY11 + "/第何週:" + weekOfYearDownerYYYY11); 21 System.out.println("週の最初の曜日:" + calDownerYYYY01.getFirstDayOfWeek()); 22 System.out.println("何日以上あると最初の週と見做すか:" + calDownerYYYY01.getMinimalDaysInFirstWeek()); 23 System.out.println("日付:" + dateDownerYYYY01 + "/第何週:" + weekOfYearDownerYYYY01);

console

1週の最初の曜日:1 2何日以上あると最初の週と見做すか:1 3日付:1998年12月31日 水曜日/第何週:1 4週の最初の曜日:1 5何日以上あると最初の週と見做すか:4 6日付:1998年01月01日 木曜日/第何週:53

直観的にはconsoleの日付:1998年12月31日日付:1997年12月31日と表示したいところです。
しかし、GregorianCalendar的には日付:1997年12月28日から1998年なため、console通り表示されます。
これはJavaの仕様通りであり、私の期待通りです。

これと同様に、私の期待では、consoleの日付:1998年01月01日日付:1997年01月01日と表示されるはずです。
なぜなら、GregorianCalendar的には日付:1998年01月01日はまだ1997年なはずだからです。
しかし、これは私の期待通りにはなりません。

私の期待は間違っていますか?
また、暦週の基準年にそって期待通りに繰り下がらせたい場合、どのようにすれば良いですか?

補足情報(環境)

  • 言語

Java8

  • 開発環境

Spring Tool Suite 4 Version: 4.2.2.RELEASE

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/07/07 23:45

javascriptではこうなったがjavaではこうなぅた 年週周りは言語仕様、設定により異なるので思い通りにならないならラップしましょう
fuzzball

2019/07/08 02:40 編集

(deleted)
guest

回答2

0

おそらく java.text.SimpleDateFormat で 'YYYY' を使って表示したものと推測します.
ですが, YYYY.format(calDownerYYYY11.getTime()); で GregorianCalendar を Date に変換した後に format しているため, GregorianCalendar に設定した setFirstDayOfWeek(Calendar.SUNDAY); 等は SimpleDateFormat に渡らず, 内部で再度 Calendar(GregorianCalendar?) に変換され, デフォルトの値によって 1998年の第1週は1997年12月29日~となっているものと思います.

試しに GregorianCalendar の getWeekYear メソッドの値を表示してみれば, 両方とも 1997 となっているのが確認できます.

GregorianCalendar (Java Platform SE 8)

年の何週目かを表す値および暦週の基準年

(略)
たとえば、1998年1月1日は木曜日です。getFirstDayOfWeek()がMONDAYでgetMinimalDaysInFirstWeek()が4 (ISO 8601規格に準拠した設定)の場合、1998年の第1週は1997年12月29日に始まり1998年1月4日で終わります。暦年が1997年の最後の3日については、暦週の基準年が1998になります。ただし、getFirstDayOfWeek()がSUNDAYの場合、1998年の第1週は1998年1月4日に始まり1998年1月10日に終わります。1998年の最初の3日間は1997年の第53週に入り、それらの日の暦週の基準年は1997です。

投稿2019/07/07 17:24

編集2019/07/07 17:42
jimbe

総合スコア12648

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

key_FoolyCooly

2019/07/07 20:24 編集

すみません、サンプルコードが間違ってました。 > おそらく java.text.SimpleDateFormat で~ はい、`SimpleDateFormat YYYY = new SimpleDateFormat("YYYY年MM月dd日 E曜日");` の事です。 > 両方とも 1997 となって~ calDownerYYYY11の場合、以下のコードが不要でした。 ``` calDownerYYYY11.setFirstDayOfWeek(Calendar.SUNDAY); calDownerYYYY11.setMinimalDaysInFirstWeek(4); ``` これでGregorianCalendarなら1998年扱いのはずです。
key_FoolyCooly

2019/07/07 20:29

> getWeekYear メソッドの値を表示してみれば~ こちらでも`1997`で確認できました。 > GregorianCalendar に設定した setFirstDayOfWeek(Calendar.SUNDAY); 等は SimpleDateFormat に渡らず~ では、渡るようにするにはどのようにすれば良いのでしょうか?
jimbe

2019/07/08 01:58 編集

SimpleDateFormat には Calendar による format は無かったと思いますので, このままの形では「出来ない」という結論かと思います. SimpleDateFormat#setCalendar でセットするか, swordone さんが追記されましたように, SimpleDateFormat#getCalendar で取得した Calendar に setFirstDayOfWeek(Calendar.SUNDAY) 等を設定するか, SimpleDateFormat を使わず文字列化するか, Calendar を渡せる’他のライブラリのフォーマッタ’を探すか...かと思います.
fuzzball

2019/07/08 05:58

YYYY.getCalendar().setFirstDayOfWeek(Calendar.SUNDAY); YYYY.getCalendar().setMinimalDaysInFirstWeek(4); に置き換えればいけそうな?
key_FoolyCooly

2019/07/08 14:45

ミスの多い質問にお答えしていただき、ありがとうございます。 swordoneさんの方法で確認できました。
guest

0

ベストアンサー

SimpleDateFormatは、内部にCalendarを持っており、formatメソッドで渡されたDateをそのCalendarに詰めなおすことで年月日などを取得しています。Dateはエポックからのミリ秒しか持っていないため、週の始まりの曜日や年の開始週の日数などの情報は渡りません。SimpleDateFormat側ではそれらの設定が残っていない状態で解釈を進めるので、「1998年1月1日」の時も、「年の開始週の日数が1日以上」というデフォルト設定で解釈するため、開始週が1998年と処理されていたのです。
出力時にその設定を適用するのであれば、SimpleDateFormatの持つCalendarの設定を変更する必要があります。getCalendar()でCalendarを取得し、それに対して同様の設定をします。逆に、formatを使って出力するだけならば元のCalendarでの設定変更は不要です。

java

1 SimpleDateFormat YYYY = new SimpleDateFormat("YYYY年MM月dd日 E曜日"); 2 Calendar fCal = YYYY.getCalendar(); 3 fCal.setFirstDayOfWeek(Calendar.SUNDAY); 4 fCal.setMinimalDaysInFirstWeek(4); 5 6 Calendar calDownerYYYY01 = new GregorianCalendar(); 7 calDownerYYYY01.set(1998, 0, 1); 8 String dateDownerYYYY01 = YYYY.format(calDownerYYYY01.getTime()); 9 10 System.out.println("週の最初の曜日:" + fCal.getFirstDayOfWeek()); 11 System.out.println("何日以上あると最初の週と見做すか:" + fCal.getMinimalDaysInFirstWeek()); 12 System.out.println("日付:" + dateDownerYYYY01);

追記:DateFormatクラスに、Calendarの設定を引き渡すsetCalendarというメソッドがありました。

java

1 // YYYY表記 2 SimpleDateFormat YYYY = new SimpleDateFormat("YYYY年MM月dd日 E曜日"); 3 4 Calendar calDownerYYYY01 = new GregorianCalendar(); 5 calDownerYYYY01.setFirstDayOfWeek(Calendar.SUNDAY); 6 calDownerYYYY01.setMinimalDaysInFirstWeek(4); 7 calDownerYYYY01.set(1998, 0, 1); 8 // DateFormatにCalendarの設定を渡す 9 YYYY.setCalendar(calDownerYYYY01); 10 String dateDownerYYYY01 = YYYY.format(calDownerYYYY01.getTime()); 11 int weekOfYearDownerYYYY01 = calDownerYYYY01.get(Calendar.WEEK_OF_YEAR); 12 13 System.out.println("週の最初の曜日:" + calDownerYYYY01.getFirstDayOfWeek()); 14 System.out.println("何日以上あると最初の週と見做すか:" + calDownerYYYY01.getMinimalDaysInFirstWeek()); 15 System.out.println("日付:" + dateDownerYYYY01 + "/第何週:" + weekOfYearDownerYYYY01);
前の回答

YYYYの実装が不明ですが推測して、paiza.ioで前半のカレンダー関連のコードを実行して出力してみたところ、普通に「1997年12月31日」と出力されました。
何か勘違いされているように思います。1997年は1997年であることに間違いはなく、あくまで「年の週」を求める上で、前半の条件では1997年12月31日は1998年の第1週に含まれるというだけの話です。
同様に、後半の条件では1998年1月1日は1997年の第53週に含まれるだけの事であり、勝手に年が変わったりはしません。

ドキュメントにそのまま書いてありますがね。

投稿2019/07/07 16:37

編集2019/07/08 02:01
swordone

総合スコア20651

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

key_FoolyCooly

2019/07/07 20:24 編集

すみません、サンプルコードが間違ってました。 > YYYYの実装が不明 `SimpleDateFormat YYYY = new SimpleDateFormat("YYYY年MM月dd日 E曜日");` の事です。 > 普通に「1997年12月31日」と出力されました。 calDownerYYYY11の場合、以下のコードが不要でした。 ``` calDownerYYYY11.setFirstDayOfWeek(Calendar.SUNDAY); calDownerYYYY11.setMinimalDaysInFirstWeek(4); ``` これでGregorianCalendarなら1998年扱いのはずです。
swordone

2019/07/07 21:26

サンプルコードが違うのなら質問を編集してください。 1998年扱いというのはあくまで「年の週」を求める場合の話であり、「1997年12月31日」が1997年の日付であることには変わりありません。 1989年1月1日~1989年1月7日は昭和64年で、1989年1月8日~12月31日は平成元年ですが、どちらも西暦1989年であることには変わりません。それと同じです。
key_FoolyCooly

2019/07/07 22:17

> サンプルコードが違うのなら質問を編集してください。 編集しました。 > 1998年扱いというのはあくまで~ それは正しいと考えます。 日付フォーマットがyyyyなら紀元(デフォルトで西暦)基準で1997年12月31日は1997年12月31日になります。 しかし、GregorianCalendarをYYYYでフォーマットした場合、 1997年12月31日は1998年12月31日になります。 これは1998年の第1週だからだと考えます。 それと同様に、1998年1月1日は1997年の第53週に含まれるため、 1997年1月1日になると考えたのです。 しかし、その期待通りにならないため、どうしてかと質問しました。
key_FoolyCooly

2019/07/08 14:45

拙い質問にも関わらずお答えいただき、ありがとうございます。 おかげで挙動が確認できるとともに、仕様の理解も深まりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問