William Mora

SpinnerSimpleDateModel

Here is the code for a JSpinnerModel that handles Date objects in simple format (e.g. ‘dd/MM/yyyy’). It extends a SpinnerDateModel so most of the code is based on that class. This class assumes that values are separated by a slash (/). The reason why I created this was based on a client’s requirement where I could only display the date in simple format and select the calendar using a JSpinner:

/**
 * A SpinnerSimpleDateModel for sequences of formatted Dates.
 * in simple format(e.g. "dd/MM/yyyy")
 *
 * @author William Mora
 * @version 1.0
 */
public class SpinnerSimpleDateModel extends SpinnerDateModel {

    private Comparable start, end;
    private Date value;
    private int calendarField;
    private SimpleDateFormat dateFormat;

    private boolean calendarFieldOK(int calendarField) {
        switch (calendarField) {
            case Calendar.DATE:
            case Calendar.YEAR:
            case Calendar.MONTH:
                return true;
            default:
                return false;
        }
    }

    /**
     * Creates an instance of SpinnerSimpleDateModel
     *
     * @param value         Initial date for the model
     * @param start         Minimum date for the model
     * @param end           Maximum date for the model
     * @param calendarField The interval desired for next and previous values.
     *                      The options are:
     *                      Calendar.DATE - Day of the month
     *                      Calendar.MONTH - Month
     *                      Calendar.YEAR - Year
     * @param dateFormat    A format specified by an instance of SimpleDateFormat
     */
    public SpinnerSimpleDateModel(Date value, Comparable start, Comparable end,
int calendarField, SimpleDateFormat dateFormat) {
        this.dateFormat = dateFormat;
        if (value == null) {
            throw new IllegalArgumentException("value is null");
        }
        if (!calendarFieldOK(calendarField)) {
            throw new IllegalArgumentException("invalid calendarField");
        }
        if ( !(((start == null) || (start.compareTo(value) <= 0)) &&
        ((end == null) || (end.compareTo(value) >= 0)))){
            throw new IllegalArgumentException("(start <= value <= end) is false");
        }
        this.value = value;
        this.start = start;
        this.end = end;
        this.calendarField = calendarField;
    }

    @Override
    public void setStart(Comparable start) {
        if ((start == null) ? (this.start != null) : !start.equals(this.start)) {
            this.start = start;
            fireStateChanged();
        }
    }

    @Override
    public void setEnd(Comparable end) {
        if ((end == null) ? (this.end != null) : !end.equals(this.end)) {
            this.end = end;
            fireStateChanged();
        }
    }

    @Override
    public void setCalendarField(int calendarField) {
        if (!calendarFieldOK(calendarField)) {
            throw new IllegalArgumentException("invalid calendarField");
        }
        if (calendarField != this.calendarField) {
            this.calendarField = calendarField;
            fireStateChanged();
        }
    }

    @Override
    public Comparable getEnd() {
        return end;
    }

    @Override
    public Object getNextValue() {
        Calendar cal = Calendar.getInstance();
        cal.setTime(value);
        cal.add(calendarField, 1);
        Date next = cal.getTime();
        return (end == null) || (end.compareTo(next) >= 0) ? next: null;
    }

    @Override
    public Object getPreviousValue() {
        Calendar cal = Calendar.getInstance();
        cal.setTime(value);
        cal.add(calendarField, -1);
        Date prev = cal.getTime();
        return ((start == null) || (start.compareTo((prev)) <= 0)) ? prev : null;
    }

    @Override
    public Object getValue() {
        return value;
    }

    @Override
    public void setValue(Object value) {
        if (value == null) {
            throw new NullPointerException("Date is null!");
        } else {
            if ((value instanceof String) && ((String) value).matches("[0-9]+[/][0-9]+[/][0-9]+")) {
                String date = ((String) value);
                int day = Integer.parseInt(date.substring(0, 2));
                int month = Integer.parseInt(date.substring(3, 5));
                int year = Integer.parseInt(date.substring(6));
                Calendar cal = Calendar.getInstance();
                //Month-1 because is zero-based
                cal.set(year, month - 1, day);
                this.value = cal.getTime();
            }
            if (value instanceof Date) {
                if (!dateFormat.format((Date) value).equals(dateFormat.format(this.value))) {
                    String temp = dateFormat.format((Date) value);
                    this.setValue(temp);
                    fireStateChanged();
                }
            }
        }

    }
}

Now the model can be used for any JSpinner. To create an instance for this class, the following will work: SpinnerSimpleDateModel spinnerModel = new SpinnerSimpleDateModel(new Date(), null, null, Calendar.DATE, new SimpleDateFormat("dd/MM/yyyy")); Then, set the editor of the JSpinner object to show the same format specified for the SpinnerSimpleDateModel object like the following:

JSpinner spinner1 = new javax.swing.JSpinner();
spinner1.setModel(spinnerModel);
spinner1.setEditor(new javax.swing.JSpinner.DateEditor(spinner1, "dd/MM/yyyy"));

Hope someone finds this useful. An advantage of using this technique is to manipulate date formats as you wish regardless of the locale.