Swing/SpinnerLocalDateTimeModel のバックアップの現在との差分(No.4)
- category: swing folder: SpinnerLocalDateTimeModel title: JSpinnerでLocalDateTimeを使用する tags: [JSpinner, SpinnerModel, LocalDateTime] tags: [JSpinner, SpinnerModel, LocalDateTime, JFormattedTextField, Calendar] author: aterai pubdate: 2015-02-02T00:12:04+09:00 description: DateやCalendarなどを使用するSpinnerDateModelの代わりに、JDK 8で導入されたLocalDateTimeなどを使用するSpinnerModelを作成して、JSpinnerで日付を選択します。 image:
概要
概要
Date
やCalendar
などを使用するSpinnerDateModel
の代わりに、JDK 8
で導入されたLocalDateTime
などを使用するSpinnerModel
を作成して、JSpinner
で日付を選択します。
Screenshot
Advertisement
サンプルコード
サンプルコード
class SpinnerLocalDateTimeModel extends AbstractSpinnerModel implements Serializable {
private Comparable<ChronoLocalDateTime<?>> start, end;
private ChronoLocalDateTime<?> value;
private TemporalUnit temporalUnit;
public SpinnerLocalDateTimeModel(
ChronoLocalDateTime<?> value,
Comparable<ChronoLocalDateTime<?>> start,
Comparable<ChronoLocalDateTime<?>> end,
TemporalUnit temporalUnit) {
super();
if (Objects.isNull(value)) {
throw new IllegalArgumentException("value is null");
}
if (!(Objects.isNull(start) || start.compareTo(value) <= 0)
&& (Objects.isNull(end) || end.compareTo(value) >= 0)) {
if (Objects.nonNull(start) && start.compareTo(value) >= 0
|| Objects.nonNull(end) && end.compareTo(value) <= 0) {
throw new IllegalArgumentException("(start <= value <= end) is false");
}
this.value = value;
this.value = Optional.ofNullable(value)
.orElseThrow(() -> new IllegalArgumentException("value is null"));
this.start = start;
this.end = end;
this.temporalUnit = temporalUnit;
}
public void setStart(Comparable<ChronoLocalDateTime<?>> start) {
if (Objects.isNull(start) ? Objects.nonNull(this.start)
: !Objects.equals(start, this.start)) {
this.start = start;
fireStateChanged();
}
}
public Comparable<ChronoLocalDateTime<?>> getStart() {
return start;
}
public void setEnd(Comparable<ChronoLocalDateTime<?>> end) {
if (Objects.isNull(end) ? Objects.nonNull(this.end)
: !Objects.equals(end, this.end)) {
this.end = end;
fireStateChanged();
}
}
public Comparable<ChronoLocalDateTime<?>> getEnd() {
return end;
}
public void setTemporalUnit(TemporalUnit temporalUnit) {
if (temporalUnit != this.temporalUnit) {
this.temporalUnit = temporalUnit;
fireStateChanged();
}
}
public TemporalUnit getTemporalUnit() {
return temporalUnit;
}
@Override public Object getNextValue() {
//Calendar cal = Calendar.getInstance();
//cal.setTime(value.getTime());
//cal.add(calendarField, 1);
//Date next = cal.getTime();
// Calendar cal = Calendar.getInstance();
// cal.setTime(value.getTime());
// cal.add(calendarField, 1);
// Date next = cal.getTime();
ChronoLocalDateTime<?> next = value.plus(1, temporalUnit);
return Objects.isNull(end) || end.compareTo(next) >= 0 ? next : null;
}
@Override public Object getPreviousValue() {
//Calendar cal = Calendar.getInstance();
//cal.setTime(value.getTime());
//cal.add(calendarField, -1);
//Date prev = cal.getTime();
// Calendar cal = Calendar.getInstance();
// cal.setTime(value.getTime());
// cal.add(calendarField, -1);
// Date prev = cal.getTime();
ChronoLocalDateTime<?> prev = value.minus(1, temporalUnit);
return Objects.isNull(start) || start.compareTo(prev) <= 0 ? prev : null;
}
public ChronoLocalDateTime<?> getLocalDateTime() {
return value;
}
@Override public Object getValue() {
return value;
}
@Override public void setValue(Object value) {
if (!(value instanceof ChronoLocalDateTime<?>)) {
throw new IllegalArgumentException("illegal value");
}
if (!value.equals(this.value)) {
this.value = (LocalDateTime) value;
fireStateChanged();
}
}
}
View in GitHub: Java, Kotlin解説
解説
- 上:
SpinnerDateModel
-
Calendar
などを使用して開始日、終了日を生成し、SpinnerDateModel
を作成して、JSpinner
に設定 -
Calendar
などを使用して開始日、終了日を生成しSpinnerDateModel
を作成してJSpinner
に設定Calendar cal = Calendar.getInstance(); cal.clear(Calendar.MILLISECOND); cal.clear(Calendar.SECOND); cal.clear(Calendar.MINUTE); cal.set(Calendar.HOUR_OF_DAY, 0); Date date = cal.getTime(); cal.add(Calendar.DATE, -2); Date start = cal.getTime(); cal.add(Calendar.DATE, 9); Date end = cal.getTime(); JSpinner spinner1 = new JSpinner(new SpinnerDateModel( date, start, end, Calendar.DAY_OF_MONTH));
- 参考: JSpinnerで日付を設定
-
- 下:
SpinnerLocalDateTimeModel
- 内部で、
Date
ではなく、LocalDateTime
を使用するSpinnerLocalDateTimeModel
(AbstractSpinnerModel
を継承)を作成 - 内部で
Date
ではなくLocalDateTime
を使用するSpinnerLocalDateTimeModel
(AbstractSpinnerModel
を継承)を作成LocalDateTime d = LocalDateTime.now(); LocalDateTime s = d.minus(2, ChronoUnit.DAYS); LocalDateTime e = d.plus(7, ChronoUnit.DAYS); JSpinner spinner2 = new JSpinner(new SpinnerLocalDateTimeModel( d, s, e, ChronoUnit.DAYS));
- スピナーエディタも、以下のような
LocalDateTime
を扱うものに変更 - スピナエディタも以下のような
LocalDateTime
を扱うものに変更- 参考: Temporal Spinners « Java Tips Weblog
class LocalDateTimeEditor extends JSpinner.DefaultEditor { private final DateTimeFormatter dateTimeFormatter; private final SpinnerLocalDateTimeModel model; public LocalDateTimeEditor(final JSpinner spinner, String dateFormatPattern) { super(spinner); if (!(spinner.getModel() instanceof SpinnerLocalDateTimeModel)) { throw new IllegalArgumentException("model not a SpinnerLocalDateTimeModel"); } dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormatPattern); model = (SpinnerLocalDateTimeModel) spinner.getModel(); final DefaultFormatter formatter = new LocalDateTimeFormatter(); EventQueue.invokeLater(new Runnable() { @Override public void run() { formatter.setValueClass(LocalDateTime.class); DefaultFormatterFactory factory = new DefaultFormatterFactory(formatter); JFormattedTextField ftf = (JFormattedTextField) getTextField(); try { String maxString = formatter.valueToString(model.getStart()); String minString = formatter.valueToString(model.getEnd()); ftf.setColumns(Math.max(maxString.length(), minString.length())); } catch (ParseException e) { // PENDING: hmuller e.printStackTrace(); } ftf.setHorizontalAlignment(JTextField.LEFT); ftf.setEditable(true); ftf.setFormatterFactory(factory); } }); } public SpinnerLocalDateTimeModel getModel() { return (SpinnerLocalDateTimeModel) getSpinner().getModel(); } class LocalDateTimeFormatter extends InternationalFormatter { public LocalDateTimeFormatter() { super(dateTimeFormatter.toFormat()); } #spanadd #spanend @Override public String valueToString(Object value) throws ParseException { //System.out.println(value.getClass().getName()); // System.out.println(value.getClass().getName()); if (value instanceof TemporalAccessor) { //return ((LocalDateTime) value).format(dateTimeFormatter); // return ((LocalDateTime) value).format(dateTimeFormatter); return dateTimeFormatter.format((TemporalAccessor) value); } else { return ""; } } #spanadd #spanend @Override public Object stringToValue(String text) throws ParseException { //System.out.println("stringToValue:" + text); // System.out.println("stringToValue:" + text); try { //LocalDateTime value = LocalDate.parse(text, dateTimeFormatter).atStartOfDay(); // LocalDateTime value = LocalDate.parse(text, dateTimeFormatter).atStartOfDay(); TemporalAccessor ta = dateTimeFormatter.parse(text); ChronoLocalDateTime<?> value = model.getLocalDateTime(); //@see https://tips4java.wordpress.com/2015/04/09/temporal-spinners/ // @see https://tips4java.wordpress.com/2015/04/09/temporal-spinners/ for (ChronoField field : ChronoField.values()) { if (field.isSupportedBy(value) && ta.isSupported(field)) { value = field.adjustInto(value, ta.getLong(field)); } } Comparable<ChronoLocalDateTime<?>> min = model.getStart(); Comparable<ChronoLocalDateTime<?>> max = model.getEnd(); if (Objects.nonNull(min) && min.compareTo(value) > 0 if (Objects.nonNull(min) && min.compareTo(value) > 0 || Objects.nonNull(max) && max.compareTo(value) < 0) { throw new ParseException(text + " is out of range", 0); } return value; } catch (DateTimeParseException e) { ParseException pe = new ParseException(e.getMessage(), e.getErrorIndex()); pe.setStackTrace(e.getStackTrace()); throw pe; } } } }
- 参考: Temporal Spinners « Java Tips Weblog
- 内部で、
参考リンク
- -
- JDK-8169482 java.time.DateTimeFormatter javadoc: F is not week-of-month - Java Bug System
- time - Java 8 DateFormatter week of month and year key symbols difference - Stack Overflow
- DateTimeFormatterのパターン文字で、
W
とF
の説明が両方week-of-month
になっている - 実際は
F
がday_of_week_in_month
で、JDK 1.9.0
ではドキュメントが修正されている
- DateTimeFormatterのパターン文字で、
-
day_of_week_in_month
について- ChronoField#ALIGNED_DAY_OF_WEEK_IN_MONTH (Java Platform SE 8)
たとえば、7日からなる週を使用する暦体系では、最初の「位置合せされた月の週番号」は「月の日」1から始まり、2番目の「位置合せされた月の週番号」は「月の日」8から始まる、というようになります。これらの位置合せされた各週に含まれる日に1から7までの番号が付けられ、このフィールドの値として返されます。したがって、「月の日」1から7では、「位置合せされた曜日」の値は1から7になります。また、「月の日」8から14でもこれが繰り返され、「位置合せされた曜日」の値は1から7になります。
-
F
(day_of_week_in_month
)は1
から7
の数値文字列に変換される - 例えば月の第一日が日曜日の場合、その月の日曜日は
1
、月曜日は2
、土曜日は7
となる-
2017-01-10
(火)は2017-01-01
が日曜なので3
に変換される -
DateTimeFormatter.ofPattern("F").format(LocalDate.of(2017, Month.JANUARY, 10))
は文字列の3
を返す
-
- ChronoField#ALIGNED_DAY_OF_WEEK_IN_MONTH (Java Platform SE 8)