/*
 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javafx.util.converter;

import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.chrono.Chronology;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.IsoChronology;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DecimalStyle;
import java.time.format.FormatStyle;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.util.Locale;

import javafx.util.StringConverter;

import com.sun.javafx.binding.Logging;

StringConverter implementation for LocalDateTime values.

See Also:
Since:JavaFX 8u40
/** * <p>{@link StringConverter} implementation for {@link LocalDateTime} values.</p> * * @see LocalDateStringConverter * @see LocalTimeStringConverter * @since JavaFX 8u40 */
public class LocalDateTimeStringConverter extends StringConverter<LocalDateTime> { LdtConverter<LocalDateTime> ldtConverter; // ------------------------------------------------------------ Constructors
Create a StringConverter for LocalDateTime values, using a default formatter and parser based on IsoChronology, FormatStyle.SHORT for both date and time, and the user's Locale.

This converter ensures symmetry between the toString() and fromString() methods. Many of the default locale based patterns used by 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.

/** * 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() { ldtConverter = new LdtConverter<LocalDateTime>(LocalDateTime.class, null, null, null, null, null, null); }
Create a StringConverter for LocalDateTime values, using a default formatter and parser based on IsoChronology, the specified FormatStyle values for date and time, and the user's Locale.
Params:
  • dateStyle – The FormatStyle that will be used by the default formatter and parser for the date. If null then FormatStyle.SHORT will be used.
  • timeStyle – The FormatStyle that will be used by the default formatter and parser for the time. If null then FormatStyle.SHORT will be used.
/** * 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) { ldtConverter = new LdtConverter<LocalDateTime>(LocalDateTime.class, null, null, dateStyle, timeStyle, null, null); }
Create a StringConverter for LocalDateTime values using the supplied formatter and parser.

For example, to use a fixed pattern for converting both ways:

String pattern = "yyyy-MM-dd HH:mm";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
StringConverter<LocalDateTime> converter =
    DateTimeStringConverter.getLocalDateTimeConverter(formatter, null);
Note that the formatter and parser can be created to handle non-default Locale and Chronology as needed.
Params:
  • formatter – An instance of DateTimeFormatter which will be used for formatting by the toString() method. If null then a default formatter will be used.
  • parser – An instance of 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.
/** * 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&lt;LocalDateTime&gt; 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) { ldtConverter = new LdtConverter<LocalDateTime>(LocalDateTime.class, formatter, parser, null, null, null, null); }
Create a StringConverter for LocalDateTime values using a default formatter and parser, which will be based on the supplied FormatStyles, Locale, and Chronology.
Params:
  • dateStyle – The FormatStyle that will be used by the default formatter and parser for the date. If null then FormatStyle.SHORT will be used.
  • timeStyle – The FormatStyle that will be used by the default formatter and parser for the time. If null then FormatStyle.SHORT will be used.
  • locale – The Locale that will be used by the default formatter and parser. If null then Locale.getDefault(Locale.Category.FORMAT) will be used.
  • chronology – The Chronology that will be used by the default formatter and parser. If null then IsoChronology.INSTANCE will be used.
/** * 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) { ldtConverter = new LdtConverter<LocalDateTime>(LocalDateTime.class, null, null, dateStyle, timeStyle, locale, chronology); } // ------------------------------------------------------- Converter Methods
{@inheritDoc}
/** {@inheritDoc} */
@Override public LocalDateTime fromString(String value) { return ldtConverter.fromString(value); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public String toString(LocalDateTime value) { return ldtConverter.toString(value); } static class LdtConverter<T extends Temporal> extends StringConverter<T> { private Class<T> type; Locale locale; Chronology chronology; DateTimeFormatter formatter; DateTimeFormatter parser; FormatStyle dateStyle; FormatStyle timeStyle; LdtConverter(Class<T> type, DateTimeFormatter formatter, DateTimeFormatter parser, FormatStyle dateStyle, FormatStyle timeStyle, Locale locale, Chronology chronology) { this.type = type; this.formatter = formatter; this.parser = (parser != null) ? parser : formatter; this.locale = (locale != null) ? locale : Locale.getDefault(Locale.Category.FORMAT); this.chronology = (chronology != null) ? chronology : IsoChronology.INSTANCE; if (type == LocalDate.class || type == LocalDateTime.class) { this.dateStyle = (dateStyle != null) ? dateStyle : FormatStyle.SHORT; } if (type == LocalTime.class || type == LocalDateTime.class) { this.timeStyle = (timeStyle != null) ? timeStyle : FormatStyle.SHORT; } }
{@inheritDoc}
/** {@inheritDoc} */
@SuppressWarnings({"unchecked"}) @Override public T fromString(String text) { if (text == null || text.isEmpty()) { return null; } text = text.trim(); if (parser == null) { parser = getDefaultParser(); } TemporalAccessor temporal = parser.parse(text); if (type == LocalDate.class) { return (T)LocalDate.from(chronology.date(temporal)); } else if (type == LocalTime.class) { return (T)LocalTime.from(temporal); } else { return (T)LocalDateTime.from(chronology.localDateTime(temporal)); } }
{@inheritDoc}
/** {@inheritDoc} */
@Override public String toString(T value) { // If the specified value is null, return a zero-length String if (value == null) { return ""; } if (formatter == null) { formatter = getDefaultFormatter(); } if (value instanceof LocalDate) { ChronoLocalDate cDate; try { cDate = chronology.date(value); } catch (DateTimeException ex) { Logging.getLogger().warning("Converting LocalDate " + value + " to " + chronology + " failed, falling back to IsoChronology.", ex); chronology = IsoChronology.INSTANCE; cDate = (LocalDate)value; } return formatter.format(cDate); } else if (value instanceof LocalDateTime) { ChronoLocalDateTime<? extends ChronoLocalDate> cDateTime; try { cDateTime = chronology.localDateTime(value); } catch (DateTimeException ex) { Logging.getLogger().warning("Converting LocalDateTime " + value + " to " + chronology + " failed, falling back to IsoChronology.", ex); chronology = IsoChronology.INSTANCE; cDateTime = (LocalDateTime)value; } return formatter.format(cDateTime); } else { return formatter.format(value); } } private DateTimeFormatter getDefaultParser() { String pattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(dateStyle, timeStyle, chronology, locale); return new DateTimeFormatterBuilder().parseLenient() .appendPattern(pattern) .toFormatter() .withChronology(chronology) .withDecimalStyle(DecimalStyle.of(locale)); }

Return a default DateTimeFormatter instance to use for formatting and parsing in this StringConverter.

/** * <p>Return a default <code>DateTimeFormatter</code> instance to use for formatting * and parsing in this {@link StringConverter}.</p> */
private DateTimeFormatter getDefaultFormatter() { DateTimeFormatter formatter; if (dateStyle != null && timeStyle != null) { formatter = DateTimeFormatter.ofLocalizedDateTime(dateStyle, timeStyle); } else if (dateStyle != null) { formatter = DateTimeFormatter.ofLocalizedDate(dateStyle); } else { formatter = DateTimeFormatter.ofLocalizedTime(timeStyle); } formatter = formatter.withLocale(locale) .withChronology(chronology) .withDecimalStyle(DecimalStyle.of(locale)); if (dateStyle != null) { formatter = fixFourDigitYear(formatter, dateStyle, timeStyle, chronology, locale); } return formatter; } private DateTimeFormatter fixFourDigitYear(DateTimeFormatter formatter, FormatStyle dateStyle, FormatStyle timeStyle, Chronology chronology, Locale locale) { String pattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(dateStyle, timeStyle, chronology, locale); if (pattern.contains("yy") && !pattern.contains("yyy")) { // Modify pattern to show four-digit year, including leading zeros. String newPattern = pattern.replace("yy", "yyyy"); formatter = DateTimeFormatter.ofPattern(newPattern) .withDecimalStyle(DecimalStyle.of(locale)); } return formatter; } } }