    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.lang3.time;
    019
    020import java.time.LocalDateTime;
    021import java.time.OffsetDateTime;
    022import java.time.ZoneId;
    023import java.time.ZonedDateTime;
    024import java.util.Calendar;
    025import java.util.Locale;
    026import java.util.Locale.Category;
    027import java.util.Map;
    028import java.util.Objects;
    029
    030/**
    031 * Helps use {@link Calendar}s.
    032 *
    033 * @since 3.10
    034 */
    035public class CalendarUtils {
    036
    037    /**
    038     * The singleton instance for {@link Calendar#getInstance()}. The instance is created when the class is initialized and is based on the current time in the
    039     * default time zone with the default {@link Category#FORMAT} locale.
    040     *
    041     * @see CalendarUtils#getInstance()
    042     */
    043    public static final CalendarUtils INSTANCE = getInstance();
    044
    045    /**
    046     * Creates a new instance based on the current time in the default time zone with the default {@link Category#FORMAT} locale.
    047     *
    048     * @return a new instance.
    049     * @since 3.14.0
    050     */
    051    public static CalendarUtils getInstance() {
    052        return new CalendarUtils(Calendar.getInstance());
    053    }
    054
    055    /**
    056     * Gets a CalendarUtils using the default time zone and specified locale. The {@code CalendarUtils} returned is based on the current time in the
    057     * default time zone with the given locale.
    058     *
    059     * @param locale the locale for the week data
    060     * @return a Calendar.
    061     */
    062    static CalendarUtils getInstance(final Locale locale) {
    063        return new CalendarUtils(Calendar.getInstance(locale), locale);
    064    }
    065
    066    /**
    067     * Converts a Calendar to a LocalDateTime.
    068     *
    069     * @param calendar the Calendar to convert.
    070     * @return a LocalDateTime.
    071     * @since 3.17.0
    072     */
    073    public static LocalDateTime toLocalDateTime(final Calendar calendar) {
    074        return LocalDateTime.ofInstant(calendar.toInstant(), toZoneId(calendar));
    075    }
    076
    077    /**
    078     * Converts a Calendar to a OffsetDateTime.
    079     *
    080     * @param calendar the Calendar to convert.
    081     * @return a OffsetDateTime.
    082     * @since 3.17.0
    083     */
    084    public static OffsetDateTime toOffsetDateTime(final Calendar calendar) {
    085        return OffsetDateTime.ofInstant(calendar.toInstant(), toZoneId(calendar));
    086    }
    087
    088    /**
    089     * Converts a Calendar to a ZonedDateTime.
    090     *
    091     * @param calendar the Calendar to convert.
    092     * @return a ZonedDateTime.
    093     * @since 3.17.0
    094     */
    095    public static ZonedDateTime toZonedDateTime(final Calendar calendar) {
    096        return ZonedDateTime.ofInstant(calendar.toInstant(), toZoneId(calendar));
    097    }
    098
    099    private static ZoneId toZoneId(final Calendar calendar) {
    100        return calendar.getTimeZone().toZoneId();
    101    }
    102
    103    private final Calendar calendar;
    104
    105    private final Locale locale;
    106
    107    /**
    108     * Creates an instance for the given Calendar.
    109     *
    110     * @param calendar A Calendar.
    111     */
    112    public CalendarUtils(final Calendar calendar) {
    113        this(calendar, Locale.getDefault());
    114    }
    115    /**
    116     * Creates an instance for the given Calendar.
    117     *
    118     * @param calendar A Calendar.
    119     * @param locale A Locale.
    120     */
    121    CalendarUtils(final Calendar calendar, final Locale locale) {
    122        this.calendar = Objects.requireNonNull(calendar, "calendar");
    123        this.locale = Objects.requireNonNull(locale, "locale");
    124    }
    125
    126    /**
    127     * Gets the current day of month.
    128     *
    129     * @return the current day of month.
    130     */
    131    public int getDayOfMonth() {
    132        return calendar.get(Calendar.DAY_OF_MONTH);
    133    }
    134
    135    /**
    136     * Gets the current day of year.
    137     *
    138     * @return the current day of year.
    139     * @since 3.13.0
    140     */
    141    public int getDayOfYear() {
    142        return calendar.get(Calendar.DAY_OF_YEAR);
    143    }
    144
    145    /**
    146     * Gets the current month.
    147     *
    148     * @return the current month.
    149     */
    150    public int getMonth() {
    151        return calendar.get(Calendar.MONTH);
    152    }
    153
    154    /**
    155     * Gets month names in the requested style.
    156     * @param style Must be a valid {@link Calendar#getDisplayNames(int, int, Locale)} month style.
    157     * @return Styled names of months
    158     */
    159    String[] getMonthDisplayNames(final int style) {
    160        // Unfortunately standalone month names are not available in DateFormatSymbols,
    161        // so we have to extract them.
    162        final Map<String, Integer> displayNames = calendar.getDisplayNames(Calendar.MONTH, style, locale);
    163        if (displayNames == null) {
    164            return null;
    165        }
    166        final String[] monthNames = new String[displayNames.size()];
    167        displayNames.forEach((k, v) -> monthNames[v] = k);
    168        return monthNames;
    169    }
    170
    171    /**
    172     * Gets full standalone month names as used in "LLLL" date formatting.
    173     * @return Long names of months
    174     */
    175    String[] getStandaloneLongMonthNames() {
    176        return getMonthDisplayNames(Calendar.LONG_STANDALONE);
    177    }
    178
    179    /**
    180     * Gets short standalone month names as used in "LLLL" date formatting.
    181     * @return Short names of months
    182     */
    183    String[] getStandaloneShortMonthNames() {
    184        return getMonthDisplayNames(Calendar.SHORT_STANDALONE);
    185    }
    186
    187    /**
    188     * Gets the current year.
    189     *
    190     * @return the current year.
    191     */
    192    public int getYear() {
    193        return calendar.get(Calendar.YEAR);
    194    }
    195
    196    /**
    197     * Converts this instance to a {@link LocalDateTime}.
    198     *
    199     * @return a LocalDateTime.
    200     * @since 3.17.0
    201     */
    202    public LocalDateTime toLocalDateTime() {
    203        return toLocalDateTime(calendar);
    204    }
    205
    206    /**
    207     * Converts this instance to a {@link OffsetDateTime}.
    208     *
    209     * @return a OffsetDateTime.
    210     * @since 3.17.0
    211     */
    212    public OffsetDateTime toOffsetDateTime() {
    213        return toOffsetDateTime(calendar);
    214    }
    215
    216    /**
    217     * Converts this instance to a {@link ZonedDateTime}.
    218     *
    219     * @return a ZonedDateTime.
    220     * @since 3.17.0
    221     */
    222    public ZonedDateTime toZonedDateTime() {
    223        return toZonedDateTime(calendar);
    224    }
    225
    226}