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