ではjava.util.Calendar
、1月は月1ではなく月0として定義されています。これに特別な理由はありますか?
多くの人が混乱しているのを見てきました...
ではjava.util.Calendar
、1月は月1ではなく月0として定義されています。これに特別な理由はありますか?
多くの人が混乱しているのを見てきました...
回答:
これは、Javaの日付/時刻APIである恐ろしい混乱のほんの一部です。何が問題なのかをリストするのに非常に時間がかかります(そして、問題の半分を知らないと確信しています)。確かに日付と時刻を操作するのは難しいですが、とにかくそうです。
代わりにJoda Timeを使用するか、JSR-310を使用してください。
編集:理由について-他の回答で述べたように、それは古いC API、またはすべてが0から始まるという一般的な感覚による可能性があります...もちろん、1から始まる日を除きます。元の実装チームの外の誰かが本当に理由を述べることができるかどうか疑問です-繰り返しますが、私は読者に、悪い決定がなされた理由についてそれほど心配しないようにしてくださいjava.util.Calendar
。
0ベースのインデックスを使用することの利点の1つは、「名前の配列」などを簡単にすることです。
// I "know" there are 12 months
String[] monthNames = new String[12]; // and populate...
String name = monthNames[calendar.get(Calendar.MONTH)];
もちろん、これは、13か月のカレンダーを取得するとすぐに失敗します...しかし、少なくとも指定されたサイズは、期待する月数です。
これは正当な理由ではありませんが、それは理由です...
編集:コメントの一種として、日付/カレンダーで私が間違っていると思うことについていくつかのアイデアを要求します:
Date
とCalendar
異なるものとしてではなく、日付/時刻が時間対日対であるような値を「ゾーニング」対「ローカル」の分離は、不足していますDate.toString()
いつも(のは、今までに多くのスタックオーバーフローのユーザーを混同していること)システムのローカルタイムゾーンを使用して実装数か月で計算する方がはるかに簡単だからです。
12月の1か月後は1月ですが、通常これを理解するには、月数を計算して計算する必要があります。
12 + 1 = 13 // What month is 13?
知っている!モジュラス12を使用すると、これをすばやく修正できます。
(12 + 1) % 12 = 1
これは11月までの11か月間は問題なく動作します...
(11 + 1) % 12 = 0 // What month is 0?
月を追加する前に1を減算してから、この係数をすべて実行し、最後に1を再び追加することで、このすべての作業を再度行うことができます...根本的な問題を回避します。
((11 - 1 + 1) % 12) + 1 = 12 // Lots of magical numbers!
では、問題を0〜11か月で考えてみましょう。
(0 + 1) % 12 = 1 // February
(1 + 1) % 12 = 2 // March
(2 + 1) % 12 = 3 // April
(3 + 1) % 12 = 4 // May
(4 + 1) % 12 = 5 // June
(5 + 1) % 12 = 6 // July
(6 + 1) % 12 = 7 // August
(7 + 1) % 12 = 8 // September
(8 + 1) % 12 = 9 // October
(9 + 1) % 12 = 10 // November
(10 + 1) % 12 = 11 // December
(11 + 1) % 12 = 0 // January
すべての月が同じように機能し、回避策は必要ありません。
((11 - 1 + 1) % 12) + 1 = 12
だけで(11 % 12) + 1
あなただけの1を追加する必要がありヶ月1..12のために、すなわち後に剰余をやって。魔法は必要ありません。
これに対する答えはたくさんありますが、私はとにかくこの問題について私の見解を述べます。前述のように、この奇妙な動作の背後にある理由time.h
は、月が0〜11の範囲のintに格納されているPOSIX Cにあります。理由を説明するには、次のように見てください。年と日は話し言葉の数字と見なされますが、月には独自の名前があります。したがって、1月は最初の月なので、最初の配列要素であるオフセット0として格納されます。monthname[JANUARY]
だろう"January"
。年の最初の月は、最初の月の配列要素です。
一方、日番号には名前がないため、0〜30としてintに格納すると混乱を招き、day+1
出力するための多くの指示が追加されます。もちろん、多くのバグが発生しやすくなります。
そうは言っても、特にJavaScript(この "機能"も継承している)の場合、言語から遠く離れた場所で抽象化する必要があるスクリプト言語では、矛盾がわかりにくくなっています。
TL; DR:月には名前があり、日にはないので。
Java 8には、より健全な新しい日付/時刻API JSR 310があります。仕様リードはJodaTimeの主要な作成者と同じであり、多くの類似した概念とパターンを共有しています。
私は怠惰だと思います。配列は0から始まります(誰もが知っています)。今年の月は配列です。Sunのエンジニアは、Javaコードにこれを少しも気にしないだけだと思いました。
個人的には、JavaカレンダーAPIの奇妙さを、グレゴリオ中心の考え方から自分自身を離婚し、その点でより不可知論的にプログラミングしようとする必要があることを示していたと考えました。具体的には、月などの定数をハードコードしないようにもう一度学びました。
次のうちどれが正しい可能性が高いですか?
if (date.getMonth() == 3) out.print("March");
if (date.getMonth() == Calendar.MARCH) out.print("March");
これは、Joda Timeについて私を少しいらいらさせる1つのことを示しています-それはプログラマーがハードコードされた定数の観点から考えることを奨励するかもしれません。(ただし、ほんの少しです。Jodaがプログラマーに不適切なプログラミングを強いているわけではありません。)
私にとって、mindpro.comほど優れた説明はありません。
ガチャ
java.util.GregorianCalendar
old java.util.Date
クラスよりバグと落とし穴ははるかに少ない ですが、それでもピクニックではありません。サマータイムが最初に提案されたときにプログラマがいたとしたら、彼らはそれを狂気で扱いにくいものとして拒否したでしょう。夏時間には、根本的な曖昧さがあります。秋に時計を午前1時に1時間戻すと、現地時間の午前1時30分と呼ばれる2つの異なる瞬間があります。読書で夏時間か標準時間のどちらを意図したかを記録した場合にのみ、それらを区別することができます。
残念ながら、
GregorianCalendar
あなたがどちらを意図しているかを知る方法はありません。あいまいさを避けるために、ダミーのUTC TimeZoneを使用して現地時間を伝えることをお勧めします。プログラマは通常、この問題に目を閉じており、この時間は誰も何もしないことを望んでいます。ミレニアムバグ。バグはまだカレンダークラスから出ていません。JDK(Java Development Kit)1.3でさえ2001年のバグがあります。次のコードを検討してください。
GregorianCalendar gc = new GregorianCalendar(); gc.setLenient( false ); /* Bug only manifests if lenient set false */ gc.set( 2001, 1, 1, 1, 0, 0 ); int year = gc.get ( Calendar.YEAR ); /* throws exception */
MSTの場合、バグは2001/01/01の午前7時に消えます。
GregorianCalendar
型付けされていないintマジック定数の巨大な山によって制御されます。この手法は、コンパイル時エラーチェックの希望を完全に破壊します。たとえば、使用する月を取得するにはGregorianCalendar. get(Calendar.MONTH));
GregorianCalendar
には未加工GregorianCalendar.get(Calendar.ZONE_OFFSET)
と夏時間GregorianCalendar. get( Calendar. DST_OFFSET)
がありますが、使用されている実際のタイムゾーンオフセットを取得する方法はありません。これら2つを個別に取得して、一緒に追加する必要があります。
GregorianCalendar.set( year, month, day, hour, minute)
秒を0に設定しません。
DateFormat
GregorianCalendar
正しくメッシュしないでください。カレンダーを2回指定する必要があります。1回は間接的に日付として指定します。ユーザーが自分のタイムゾーンを正しく設定していない場合、デフォルトで静かにPSTまたはGMTになります。
GregorianCalendarでは、地球上のすべての人がそうであるように、月は1ではなく、1月0から始まる番号が付けられます。しかし、日は1から始まり、曜日は日曜日= 1、月曜日= 2、…土曜日= 7です。しかし、DateFormat。parseは、January = 1の従来の方法で動作します。
java.util.Month
Javaは、1ベースのインデックスを数か月間使用する別の方法を提供します。java.time.Month
列挙型を使用します。12か月ごとに1つのオブジェクトが事前定義されています。1〜12月の各1〜12に番号が割り当てられています。getValue
番号を呼び出します。
(Gives you 6)のMonth.JULY
代わりに(Gives you 7)を使用しCalendar.JULY
ます。
(import java.time.*;)
Month.FEBRUARY.getValue() // February → 2.
2
これで、これらの厄介な古いレガシー日時クラスの代わりに、java.timeクラスが使用できるようになりました。
java.time.Month
これらのクラスの中には列挙型があります。列挙型は、1つ以上の事前定義されたオブジェクト、つまり、クラスのロード時に自動的にインスタンス化されるオブジェクトを保持します。上:我々はダースそのようなオブジェクトを持って、それぞれが名前を与えられた、、、などを。これらはそれぞれクラス定数です。これらのオブジェクトは、コード内の任意の場所で使用して渡すことができます。例:Month
Month
JANUARY
FEBRUARY
MARCH
static final public
someMethod( Month.AUGUST )
幸いなことに、番号は1〜12で、1は1月、12は12月です。
Month
特定の月番号(1〜12)のオブジェクトを取得します。
Month month = Month.of( 2 ); // 2 → February.
別の方向に進んで、Month
オブジェクトに月数を尋ねます。
int monthNumber = Month.FEBRUARY.getValue(); // February → 2.
毎月の日数を知るなど、このクラスの他の多くの便利なメソッド。クラスは、月のローカライズされた名前を生成することもできます。
月のローカライズされた名前をさまざまな長さまたは省略形で取得できます。
String output =
Month.FEBRUARY.getDisplayName(
TextStyle.FULL ,
Locale.CANADA_FRENCH
);
フェブリエ
また、この列挙型のオブジェクトは、単なる整数ではなく、コードベースの周りに渡す必要があります。これにより、タイプセーフが提供され、有効な値の範囲が確保され、コードがより自己文書化されます。Javaの驚くほど強力な列挙型機能に慣れていない場合は、Oracleチュートリアルを参照してください。
java.timeのフレームワークは、Java 8に組み込まれており、後にされています。これらのクラスは、次のような厄介な古いレガシー日時クラスに取って代わります。java.util.Date
、.Calendar
、& java.text.SimpleDateFormat
。
ジョダタイムプロジェクトは、今でメンテナンスモード、java.timeへの移行をアドバイスします。
詳細については、 Oracleチュートリアルを。また、スタックオーバーフローで多くの例と説明を検索してください。仕様はJSR 310です。
java.timeクラスはどこで入手できますか?
ThreeTen-エクストラプロジェクトでは、追加のクラスでjava.timeを拡張します。このプロジェクトは、java.timeに将来追加される可能性があることを証明する場です。あなたはここにいくつかの有用なクラスのような見つけることがInterval
、YearWeek
、YearQuarter
、および多くを。
それ自体がゼロとして正確に定義されているわけではなく、Calendar.Januaryとして定義されています。列挙型の代わりに定数として整数を使用することの問題です。Calendar.January == 0。
言語を書くことは見た目よりも難しく、特に処理時間はほとんどの人が考えるよりもはるかに困難です。問題のごく一部(実際にはJavaではありません)については、https://www.youtube.com/watch?v = -5wpm-gesOYのYouTubeビデオ「The Problem with Time&Timezones-Computerphile」を参照してください。頭が混乱して笑い飛ばされても驚かないでください。
DannySmurfの怠惰の答えに加えて、のような定数を使用することをお勧めしますCalendar.JANUARY
。
すべてが0で始まるからです。これがJavaでのプログラミングの基本的な事実です。それから逸脱することが1つあるとすれば、それは混乱の種をまき散らすことになります。それらの形成について議論したり、コード化したりしないでください。