日付の範囲クラスの作成(精度指定付き)-1
先日作ったRangeクラスをもとにとりあえず作ってみる。DateRangeのほうが良かったけどCalendarRangeにしてしまいまんた。
package sample; import java.util.Calendar; import java.util.Date; public class CalendarRange extends Range<Calendar> { public static enum Scale { YEAR ,MONTH ,DAY ,HOUR ,MINUTE ,SECOND ,MILLISECOND; } private final Scale scale; public CalendarRange(Calendar minVal, boolean containsMin, Calendar maxVal, boolean containsMax, Scale scale) { super(adjustScale(minVal, scale), containsMin, adjustScale(maxVal, scale), containsMax); this.scale = scale; } @Override public boolean contains(Calendar argValue) { Calendar val = adjustScale(argValue, scale); return super.contains(val); } public boolean contains(Date date) { if(date == null) throw new NullPointerException("Argument must not be null."); Calendar cal = Calendar.getInstance(); cal.setTime(date); return contains(cal); } /** * @param original * @param scale スケール。null不可 * @return 引数originalのクローン。scaleに合わせて時刻調整済み。 * 引数originalがnullの場合はnull */ private static Calendar adjustScale(Calendar original, Scale scale) { if(scale == null) throw new NullPointerException("scale must not be null."); if(original == null) return null; Calendar cal = (Calendar) original.clone(); switch(scale) { // breakなしcaseで下に落とす(fall through) case YEAR: cal.set(Calendar.MONTH, 0); case MONTH: cal.set(Calendar.DAY_OF_MONTH, 1); case DAY: cal.set(Calendar.HOUR_OF_DAY, 0); case HOUR: cal.set(Calendar.MINUTE, 0); case MINUTE: cal.set(Calendar.SECOND, 0); case SECOND: cal.set(Calendar.MILLISECOND, 0); case MILLISECOND: // no-scale-adjust } return cal; } }
使い方はこんな感じで↓
public void test_MonthScale() throws ParseException { Calendar min = getCal("2000-01-03 22:34:56.789"); Calendar max = getCal("2000-02-29 23:59:59.999"); // min <= cal <= max 精度:月 CalendarRange range = new CalendarRange(min, true, max, true, CalendarRange.Scale.MONTH); assertFalse(range.contains(getCal("1999-12-31 23:59:59.999"))); assertTrue (range.contains(getCal("2000-01-01 00:00:00.000"))); assertTrue (range.contains(getCal("2000-02-29 23:59:59.999"))); assertFalse(range.contains(getCal("2000-03-01 00:00:00.000"))); } public void test_HourScale() throws ParseException { Calendar min = getCal("2000-01-01 22:34:56.789"); Calendar max = getCal("2000-01-02 01:34:56.789"); // min <= cal <= max 精度:時 CalendarRange range = new CalendarRange(min, true, max, true, CalendarRange.Scale.HOUR); assertFalse(range.contains(getCal("2000-01-01 21:59:59.999"))); assertTrue (range.contains(getCal("2000-01-01 22:00:00.000"))); assertTrue (range.contains(getCal("2000-01-02 01:59:59.999"))); assertFalse(range.contains(getCal("2000-01-02 02:00:00.000"))); } public void test_MilliSecondScale() throws ParseException { Calendar min = getCal("2000-01-01 22:34:56.789"); Calendar max = getCal("2000-01-01 22:34:56.802"); // min <= cal <= max 精度:ミリ秒 CalendarRange range = new CalendarRange(min, true, max, true, CalendarRange.Scale.MILLISECOND); assertFalse(range.contains(getCal("2000-01-01 22:34:56.788"))); assertTrue (range.contains(getCal("2000-01-01 22:34:56.789"))); assertTrue (range.contains(getCal("2000-01-01 22:34:56.802"))); assertFalse(range.contains(getCal("2000-01-01 22:34:56.803"))); // min <= cal < max 精度:ミリ秒 range = new CalendarRange(min, true, max, false, CalendarRange.Scale.MILLISECOND); assertFalse(range.contains(getCal("2000-01-01 22:34:56.788"))); assertTrue (range.contains(getCal("2000-01-01 22:34:56.789"))); assertTrue (range.contains(getCal("2000-01-01 22:34:56.801"))); assertFalse(range.contains(getCal("2000-01-01 22:34:56.802"))); // というかミリ秒の場合は特に精度調整いらないけど } private static Calendar getCal(String str) throws ParseException { Calendar cal = Calendar.getInstance(); cal.setTime(getDate(str)); return cal; } private static final DateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.S"); private static Date getDate(String str) throws ParseException { return df.parse(str); }
スケール指定したクローンを返す adjustScale() 以外、前のソースと特に変わりなし。そしてスケール修正部分はバグを誘発しそうなbreakなしswitch-case構文*1。この部分はEnumを使ってforで回せばswitch-caseを使わないで書けそう。明日あたり書いてみる。
・Calendar#clear(int scale) というのがあるのだけど、時(hour)部分はクリアできないらしいので使用を断念。
・アップ直後に cal.set(Calendar.DAY_OF_MONTH, 0); という誤りを発見。日にちだけ1起算なのがまぎらわしい。
*1:breakなしで処理を下に落とすのをfall throughというらしい