DateFormatter.java

package space.sunqian.common.base.date;

import space.sunqian.annotations.Immutable;
import space.sunqian.annotations.Nonnull;
import space.sunqian.annotations.Nullable;

import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.Date;

/**
 * Formatter for time and date. This formatter has a default zone information ({@link #zoneId()}), which will be used
 * when the object to be operated does not contain zone information. The implementation of this interface is immutable
 * and thread-safe.
 *
 * @author sunqian
 */
@Immutable
public interface DateFormatter {

    /**
     * Returns the default {@link DateFormatter} of which pattern is {@link DateKit#DEFAULT_PATTERN}, and zone is
     * {@link ZoneId#systemDefault()}.
     *
     * @return the default {@link DateFormatter} of which pattern is {@link DateKit#DEFAULT_PATTERN}
     */
    static @Nonnull DateFormatter defaultFormatter() {
        return DateKit.formatter;
    }

    /**
     * Returns a new instance of {@link DateFormatter} based on the given {@link DateTimeFormatter}. The default zone
     * info of the returned instance is {@link ZoneId#systemDefault()}.
     * <p>
     * The returned instance supports {@link Date}, {@link Instant}, {@link LocalDateTime}, {@link ZonedDateTime},
     * {@link OffsetDateTime}, {@link LocalDate} and {@link LocalTime}. But doesn't support pattern methods.
     *
     * @param formatter the given {@link DateTimeFormatter}
     * @return a new instance of {@link DateFormatter} based on the given {@link DateTimeFormatter}
     */
    static @Nonnull DateFormatter ofFormatter(@Nonnull DateTimeFormatter formatter) {
        return ofFormatter(formatter, ZoneId.systemDefault());
    }

    /**
     * Returns a new instance of {@link DateFormatter} based on the given pattern. The default zone info of the returned
     * instance is {@link ZoneId#systemDefault()}.
     * <p>
     * The returned instance supports {@link Date}, {@link Instant}, {@link LocalDateTime}, {@link ZonedDateTime},
     * {@link OffsetDateTime}, {@link LocalDate} and {@link LocalTime}. And its underlying formatter is from
     * {@link DateTimeFormatter#ofPattern(String)}.
     *
     * @param pattern the given pattern
     * @return a new instance of {@link DateFormatter} based on the given pattern
     * @throws DateTimeException if the pattern is invalid
     */
    static @Nonnull DateFormatter ofPattern(@Nonnull String pattern) throws DateTimeException {
        return ofPattern(pattern, ZoneId.systemDefault());
    }

    /**
     * Returns a new instance of {@link DateFormatter} based on the given {@link DateTimeFormatter}.
     * <p>
     * The returned instance supports {@link Date}, {@link Instant}, {@link LocalDateTime}, {@link ZonedDateTime},
     * {@link OffsetDateTime}, {@link LocalDate} and {@link LocalTime}. But doesn't support pattern methods.
     *
     * @param formatter the given {@link DateTimeFormatter}
     * @param zoneId    the default zone info of the returned instance
     * @return a new instance of {@link DateFormatter} based on the given {@link DateTimeFormatter}
     */
    static @Nonnull DateFormatter ofFormatter(
        @Nonnull DateTimeFormatter formatter, @Nonnull ZoneId zoneId
    ) {
        return DateBack.ofFormatter(formatter, zoneId);
    }

    /**
     * Returns a new instance of {@link DateFormatter} based on the given pattern.
     * <p>
     * The returned instance supports {@link Date}, {@link Instant}, {@link LocalDateTime}, {@link ZonedDateTime},
     * {@link OffsetDateTime}, {@link LocalDate} and {@link LocalTime}. And its underlying formatter is from
     * {@link DateTimeFormatter#ofPattern(String)}.
     *
     * @param pattern the given pattern
     * @param zoneId  the default zone info of the returned instance
     * @return a new instance of {@link DateFormatter} based on the given pattern
     * @throws DateTimeException if the pattern is invalid
     */
    static @Nonnull DateFormatter ofPattern(
        @Nonnull String pattern, @Nonnull ZoneId zoneId
    ) throws DateTimeException {
        return DateBack.ofPattern(pattern, zoneId);
    }

    /**
     * Returns the zone id of this time formatter.
     *
     * @return the zone id of this time formatter
     */
    @Nonnull
    ZoneId zoneId();

    /**
     * Returns the pattern of this time formatter.
     *
     * @return the pattern
     * @throws DateTimeException if the pattern is invalid
     */
    @Nonnull
    String pattern() throws DateTimeException;

    /**
     * Returns whether this time formatter has a pattern.
     *
     * @return whether this time formatter has a pattern
     */
    boolean hasPattern();

    /**
     * Formats the given date.
     *
     * @param date the given date to format
     * @return the formatted string
     * @throws DateTimeException if any error occurs
     */
    @Nonnull
    String format(@Nonnull Date date) throws DateTimeException;

    /**
     * Formats the given date. If the given date is {@code null}, or an exception thrown during formating, returns
     * {@code null}.
     *
     * @param date the given date to format, can be {@code null}
     * @return the formatted string, or {@code null} if the given date is {@code null} or an exception thrown
     * @throws DateTimeException if any error occurs
     */
    default @Nullable String formatSafe(@Nullable Date date) throws DateTimeException {
        if (date == null) {
            return null;
        }
        try {
            return format(date);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Formats the given time object.
     *
     * @param time the given time object to format
     * @return the formatted string
     * @throws DateTimeException if any error occurs
     */
    @Nonnull
    String format(@Nonnull TemporalAccessor time) throws DateTimeException;

    /**
     * Formats the given time object. If the given time object is {@code null}, or an exception thrown during formating,
     * returns {@code null}.
     *
     * @param time the given time object to format, can be {@code null}
     * @return the formatted string, or {@code null} if the given time object is {@code null} or an exception thrown
     * @throws DateTimeException if any error occurs
     */
    default @Nullable String formatSafe(@Nullable TemporalAccessor time) throws DateTimeException {
        if (time == null) {
            return null;
        }
        try {
            return format(time);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Parses the given date string to an instance of the specified time type.
     *
     * @param date     the given date string to parse
     * @param timeType the specified time type
     * @param <T>      the time type
     * @return the parsed time instance
     * @throws DateTimeException if any error occurs
     */
    <T> @Nonnull T parse(@Nonnull CharSequence date, @Nonnull Class<T> timeType) throws DateTimeException;

    /**
     * Parses the given date string to an instance of the specified time type. If the given date string is {@code null},
     * or an exception thrown during parsing, returns {@code null}.
     *
     * @param date     the given date string to parse, can be {@code null}
     * @param timeType the specified time type
     * @param <T>      the time type
     * @return the parsed time instance, or {@code null} if the given date string is {@code null} or an exception thrown
     * @throws DateTimeException if any error occurs
     */
    default <T> @Nullable T parseSafe(
        @Nullable CharSequence date, @Nonnull Class<T> timeType
    ) throws DateTimeException {
        if (date == null) {
            return null;
        }
        try {
            return parse(date, timeType);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Converts the given date to an instance of the specified time type.
     *
     * @param date     the given date to parse
     * @param timeType the specified time type
     * @param <T>      the time type
     * @return the converted time instance
     * @throws DateTimeException if any error occurs
     */
    <T> @Nonnull T convert(@Nonnull Date date, @Nonnull Class<T> timeType) throws DateTimeException;

    /**
     * Converts the given date to an instance of the specified time type. If the given date is {@code null}, or an
     * exception thrown during parsing, returns {@code null}.
     *
     * @param date     the given date to parse, can be {@code null}
     * @param timeType the specified time type
     * @param <T>      the time type
     * @return the converted time instance, or {@code null} if the given date is {@code null} or an exception thrown
     * @throws DateTimeException if any error occurs
     */
    default <T> @Nullable T convertSafe(@Nullable Date date, @Nonnull Class<T> timeType) throws DateTimeException {
        if (date == null) {
            return null;
        }
        try {
            return convert(date, timeType);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Converts the given time object to an instance of the specified time type.
     *
     * @param time     the given time object to parse
     * @param timeType the specified time type
     * @param <T>      the time type
     * @return the converted time instance
     * @throws DateTimeException if any error occurs
     */
    <T> @Nonnull T convert(@Nonnull TemporalAccessor time, @Nonnull Class<T> timeType) throws DateTimeException;

    /**
     * Converts the given time object to an instance of the specified time type. If the given time object is
     * {@code null}, or an exception thrown during parsing, returns {@code null}.
     *
     * @param time     the given time object to parse, can be {@code null}
     * @param timeType the specified time type
     * @param <T>      the time type
     * @return the converted time instance, or {@code null} if the given time object is {@code null} or an exception
     * thrown
     * @throws DateTimeException if any error occurs
     */
    default <T> @Nullable T convertSafe(
        @Nullable TemporalAccessor time, @Nonnull Class<T> timeType
    ) throws DateTimeException {
        if (time == null) {
            return null;
        }
        try {
            return convert(time, timeType);
        } catch (Exception e) {
            return null;
        }
    }
}