001package com.box.sdk;
002
003import java.text.DateFormat;
004import java.text.ParseException;
005import java.text.SimpleDateFormat;
006import java.time.Instant;
007import java.util.Date;
008import java.util.TimeZone;
009
010/**
011 * Contains methods for parsing and formatting dates for use with the Box API.
012 */
013public final class BoxDateFormat {
014    private static final ThreadLocal<DateFormat> THREAD_LOCAL_DATE_FORMAT_SECONDS = ThreadLocal.withInitial(() -> {
015        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
016        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
017        return sdf;
018    });
019
020    private static final ThreadLocal<DateFormat> THREAD_LOCAL_DATE_FORMAT_MILLISECONDS = ThreadLocal.withInitial(() -> {
021        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
022        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
023        return sdf;
024    });
025
026    private static final ThreadLocal<DateFormat> THREAD_LOCAL_DATE_ONLY = ThreadLocal.withInitial(() -> {
027        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
028        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
029        return sdf;
030    });
031
032    private BoxDateFormat() {
033    }
034
035    /**
036     * Parses a date string returned by the Box API into a {@link Date} object.
037     *
038     * @param dateString a string containing the date.
039     * @return the parsed date.
040     * @throws ParseException if the string cannot be parsed into a valid date.
041     */
042    public static Date parse(String dateString) throws ParseException {
043        try {
044            return THREAD_LOCAL_DATE_FORMAT_SECONDS.get().parse(dateString);
045        } catch (ParseException pe) {
046            return THREAD_LOCAL_DATE_FORMAT_MILLISECONDS.get().parse(dateString);
047        }
048    }
049
050    /**
051     * Parses a date in format of yyyy-MM-dd.
052     *
053     * @param date date to parse.
054     * @return parsed date.
055     * @throws ParseException if the string cannot be parsed into a valid date.
056     */
057    public static Date parseDateOnly(String date) throws ParseException {
058        return THREAD_LOCAL_DATE_ONLY.get().parse(date);
059    }
060
061    /**
062     * Formats a date as a string that can be sent to the Box API.
063     *
064     * @param date the date to format.
065     * @return a string containing the formatted date.
066     */
067    public static String format(Date date) {
068        return THREAD_LOCAL_DATE_FORMAT_SECONDS.get().format(date);
069    }
070
071    /**
072     * Formats an Instant as a string that can be sent to the Box API.
073     *
074     * @param instant the instant to format.
075     * @return a string containing the formatted instant.
076     */
077    public static String format(Instant instant) {
078        return THREAD_LOCAL_DATE_FORMAT_SECONDS.get().format(Date.from(instant));
079    }
080
081    /**
082     * Formats a date as a string yyyy-MM-dd that can be sent to the Box API.
083     *
084     * @param date the date to format.
085     * @return a yyyy-MM-dd string containing the formatted date.
086     */
087    public static String formatAsDateOnly(Date date) {
088        return THREAD_LOCAL_DATE_ONLY.get().format(date);
089    }
090}