JDK-8098254 : StringConverter support for LocalDate/LocalTime/LocalDateTime
  • Type: Enhancement
  • Component: javafx
  • Sub-Component: controls
  • Affected Version: 8
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2014-07-24
  • Updated: 2015-06-12
  • Resolved: 2014-08-13
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8
8u40Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Description
The three classes javafx.util.converter.[Date|Time|DateTime]StringConverter are based on the legacy types Date and Calendar in java.util, as well as DateFormat in java.text.

The new java.time API in Java 8 is intended as replacement for those older APIs. JavaFX controls such as DatePicker and the forthcoming Spinner are based on the new java.time API and would benefit from improved StringConverter classes.

DatePicker currently has a default internal StringConverter<LocalDate> implementation with special logic to ensure symmetry between formatting and parsing. This code should be moved to a class in javafx.util.converter, together with corresponding API for LocalTime and LocalDateTime.
Comments
Changeset: http://hg.openjdk.java.net/openjfx/8u-dev/rt/rev/2a469ac93070
13-08-2014

+1
08-08-2014

No steps are missing. I was just making you aware of the link FYI in case you were not. Believe it or not, the process was not well documented until recently although it was understood by most committers.
05-08-2014

Sorry, but I don't see what steps of the API review process are missing on my part.
05-08-2014

Thanks Mario, I have replaced the two printlns with logging: > Logging.getLogger().warning("Converting LocalDate " + value + " to " + chronology + " failed, falling back to IsoChronology.", ex); > Logging.getLogger().warning("Converting LocalDateTime " + value + " to " + chronology + " failed, falling back to IsoChronology.", ex);
05-08-2014

@Steve - this is not a CSS to java type converter
05-08-2014

There are System.err.println(ex) eg. in this file: http://cr.openjdk.java.net/~leifs/rt38011/webrev.02/raw_files/new/modules/base/src/main/java/javafx/util/converter/LocalDateTimeStringConverter.java At least java logging should be used there, no?
05-08-2014

Lief, FYI this is the API review process: https://wiki.openjdk.java.net/display/OpenJFX/API+Review
05-08-2014

David, do you need to update the CSS property API that you recently added to take this new type / conversion into account?
05-08-2014

Updated the copyright.
05-08-2014

And even if it weren't milestone rampdown week, it is new API, meaning that it similarly requires approval from Steve or me.
04-08-2014

Only one minor comment: update the copyright years in the new files to be just 2014, not '2010, 2013'. Other than that, it is a +1 from me, but given the week we find ourselves in (8u40 milestone 1 week), you'll need to get a +1 from Kevin or Steve as well.
04-08-2014

The solution provides three new public classes: package javafx.util.converter; /** * <p>{@link StringConverter} implementation for {@link LocalDate} values.</p> * * @see LocalTimeStringConverter * @see LocalDateTimeStringConverter * @since JavaFX 8u40 */ public class LocalDateStringConverter extends StringConverter<LocalDate> { /** * Create a {@link StringConverter} for {@link LocalDate} values, using a * default formatter and parser based on {@link IsoChronology}, * {@link FormatStyle#SHORT}, and the user's {@link Locale}. * * <p>This converter ensures symmetry between the toString() and * fromString() methods. Many of the default locale based patterns used by * {@link DateTimeFormatter} will display only two digits for the year when * formatting to a string. This would cause a value like 1955 to be * displayed as 55, which in turn would be parsed back as 2055. This * converter modifies two-digit year patterns to always use four digits. The * input parsing is not affected, so two digit year values can still be * parsed leniently as expected in these locales.</p> */ public LocalDateStringConverter(); /** * Create a {@link StringConverter} for {@link LocalDate} values, using a * default formatter and parser based on {@link IsoChronology}, * the specified {@link FormatStyle}, and the user's {@link Locale}. * * @param dateStyle The {@link FormatStyle} that will be used by the default * formatter and parser. If null then {@link FormatStyle#SHORT} will be used. */ public LocalDateStringConverter(FormatStyle dateStyle); /** * Create a {#link StringConverter} for {@link LocalDate} values using the supplied * formatter and parser. * * <p>For example, to use a fixed pattern for converting both ways:</p> * <blockquote><pre> * String pattern = "yyyy-MM-dd"; * DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); * StringConverter<LocalDate> converter = * DateTimeStringConverter.getLocalDateStringConverter(formatter, null); * </pre></blockquote> * * Note that the formatter and parser can be created to handle non-default * {@link Locale} and {@link Chronology} as needed. * * @param formatter An instance of {@link DateTimeFormatter} that will be * used for formatting by the toString() method. If null then a default * formatter will be used. * @param parser An instance of {@link DateTimeFormatter} that will be used * for parsing by the fromString() method. This can be identical to * formatter. If null then formatter will be used, and if that is also null, * then a default parser will be used. */ public LocalDateStringConverter(DateTimeFormatter formatter, DateTimeFormatter parser); /** * Create a StringConverter for {@link LocalDate} values using a default * formatter and parser, which will be based on the supplied * {@link FormatStyle}, {@link Locale}, and {@link Chronology}. * * @param dateStyle The {@link FormatStyle} that will be used by the default * formatter and parser. If null then {@link FormatStyle#SHORT} will be used. * @param locale The {@link Locale} that will be used by the default * formatter and parser. If null then * {@code Locale.getDefault(Locale.Category.FORMAT)} will be used. * @param chronology The {@link Chronology} that will be used by the default * formatter and parser. If null then {@link IsoChronology#INSTANCE} will be used. */ public LocalDateStringConverter(FormatStyle dateStyle, Locale locale, Chronology chronology); } ---------------- package javafx.util.converter; /** * <p>{@link StringConverter} implementation for {@link LocalTime} values.</p> * * @see LocalDateStringConverter * @see LocalDateTimeStringConverter * @since JavaFX 8u40 */ public class LocalTimeStringConverter extends StringConverter<LocalTime> { /** * Create a {@link StringConverter} for {@link LocalTime} values, using a * default formatter and parser with {@link FormatStyle#SHORT}, and the * user's {@link Locale}. */ public LocalTimeStringConverter(); /** * Create a {@link StringConverter} for {@link LocalTime} values, using a * default formatter and parser with the specified {@link FormatStyle} and * based on the user's {@link Locale}. * * @param timeStyle The {@link FormatStyle} that will be used by the default * formatter and parser. If null then {@link FormatStyle#SHORT} will be used. */ public LocalTimeStringConverter(FormatStyle timeStyle); /** * Create a StringConverter for {@link LocalTime} values, using a * default formatter and parser with the specified {@link FormatStyle} * and {@link Locale}. * * @param timeStyle The {@link FormatStyle} that will be used by the default * formatter and parser. If null then {@link FormatStyle#SHORT} will be used. * @param locale The {@link Locale} that will be used by the default * formatter and parser. If null then * {@code Locale.getDefault(Locale.Category.FORMAT)} will be used. */ public LocalTimeStringConverter(FormatStyle timeStyle, Locale locale); /** * Create a StringConverter for {@link LocalTime} values using the * supplied formatter and parser, which are responsible for * choosing the desired {@link Locale}. * * <p>For example, a fixed pattern can be used for converting both ways:</p> * <blockquote><pre> * String pattern = "HH:mm:ss"; * DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); * StringConverter<LocalTime> converter = * DateTimeStringConverter.getLocalTimeConverter(formatter, null); * </pre></blockquote> * * @param formatter An instance of {@link DateTimeFormatter} which * will be used for formatting by the toString() method. If null * then a default formatter will be used. * @param parser An instance of {@link DateTimeFormatter} which * will be used for parsing by the fromString() method. This can * be identical to formatter. If null, then formatter will be * used, and if that is also null, then a default parser will be * used. */ public LocalTimeStringConverter(DateTimeFormatter formatter, DateTimeFormatter parser); } ---------------- package javafx.util.converter; /** * <p>{@link StringConverter} implementation for {@link LocalDateTime} values.</p> * * @see LocalDateStringConverter * @see LocalTimeStringConverter * @since JavaFX 8u40 */ public class LocalDateTimeStringConverter extends StringConverter<LocalDateTime> { /** * Create a {@link StringConverter} for {@link LocalDateTime} values, using a * default formatter and parser based on {@link IsoChronology}, * {@link FormatStyle#SHORT} for both date and time, and the user's * {@link Locale}. * * <p>This converter ensures symmetry between the toString() and * fromString() methods. Many of the default locale based patterns used by * {@link DateTimeFormatter} will display only two digits for the year when * formatting to a string. This would cause a value like 1955 to be * displayed as 55, which in turn would be parsed back as 2055. This * converter modifies two-digit year patterns to always use four digits. The * input parsing is not affected, so two digit year values can still be * parsed as expected in these locales.</p> */ public LocalDateTimeStringConverter(); /** * Create a {@link StringConverter} for {@link LocalDateTime} values, using * a default formatter and parser based on {@link IsoChronology}, the * specified {@link FormatStyle} values for date and time, and the user's * {@link Locale}. * * @param dateStyle The {@link FormatStyle} that will be used by the default * formatter and parser for the date. If null then {@link FormatStyle#SHORT} * will be used. * @param timeStyle The {@link FormatStyle} that will be used by the default * formatter and parser for the time. If null then {@link FormatStyle#SHORT} * will be used. */ public LocalDateTimeStringConverter(FormatStyle dateStyle, FormatStyle timeStyle); /** * Create a {@link StringConverter} for {@link LocalDateTime} values using * the supplied formatter and parser. * * <p>For example, to use a fixed pattern for converting both ways:</p> * <blockquote><pre> * String pattern = "yyyy-MM-dd HH:mm"; * DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); * StringConverter<LocalDateTime> converter = * DateTimeStringConverter.getLocalDateTimeConverter(formatter, null); * </pre></blockquote> * * Note that the formatter and parser can be created to handle non-default * {@link Locale} and {@link Chronology} as needed. * * @param formatter An instance of {@link DateTimeFormatter} which will be * used for formatting by the toString() method. If null then a default * formatter will be used. * @param parser An instance of {@link DateTimeFormatter} which will be used * for parsing by the fromString() method. This can be identical to * formatter. If null then formatter will be used, and if that is also null, * then a default parser will be used. */ public LocalDateTimeStringConverter(DateTimeFormatter formatter, DateTimeFormatter parser); /** * Create a {@link StringConverter} for {@link LocalDateTime} values using a * default formatter and parser, which will be based on the supplied * {@link FormatStyle}s, {@link Locale}, and {@link Chronology}. * * @param dateStyle The {@link FormatStyle} that will be used by the default * formatter and parser for the date. If null then {@link FormatStyle#SHORT} * will be used. * @param timeStyle The {@link FormatStyle} that will be used by the default * formatter and parser for the time. If null then {@link FormatStyle#SHORT} * will be used. * @param locale The {@link Locale} that will be used by the * default formatter and parser. If null then * {@code Locale.getDefault(Locale.Category.FORMAT)} will be used. * @param chronology The {@link Chronology} that will be used by the default * formatter and parser. If null then {@link IsoChronology#INSTANCE} will be * used. */ public LocalDateTimeStringConverter(FormatStyle dateStyle, FormatStyle timeStyle, Locale locale, Chronology chronology); }
04-08-2014

Webrev for alternative #1: http://cr.openjdk.java.net/~leifs/rt38011/webrev.02/ I prefer this solution, so I'm submitting it for API review.
04-08-2014

Webrev for alternative #2: http://cr.openjdk.java.net/~leifs/rt38011/webrev.01/
24-07-2014

There are two alternatives for adding this API. 1. Create three new classes Local[Date|Time|DateTime]StringConverter with similar constructors as for the old classes. This will allow subclassing. Comments should be added to the old classes explaining that the use of the old API is obsolete. 2. Create static factory methods in the old [Date|Time|DateTime]StringConverter classes to return converters based on the new types. These would use a private inner class that can not be subclassed.
24-07-2014

See also RT-38012, which deals with the behavior of the existing date/time converters in javafx.util.converter.
24-07-2014