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}