001/** 002 * Copyright (c) 2010-2019 Mark Allen, Norbert Bartels. 003 * 004 * Permission is hereby granted, free of charge, to any person obtaining a copy 005 * of this software and associated documentation files (the "Software"), to deal 006 * in the Software without restriction, including without limitation the rights 007 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 008 * copies of the Software, and to permit persons to whom the Software is 009 * furnished to do so, subject to the following conditions: 010 * 011 * The above copyright notice and this permission notice shall be included in 012 * all copies or substantial portions of the Software. 013 * 014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 017 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 019 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 020 * THE SOFTWARE. 021 */ 022package com.restfb.util; 023 024import static com.restfb.logging.RestFBLogger.UTILS_LOGGER; 025 026import java.text.ParseException; 027import java.util.Date; 028 029/** 030 * A collection of date-handling utility methods. 031 * 032 * @author <a href="http://restfb.com">Mark Allen</a> 033 * @since 1.6 034 */ 035public final class DateUtils { 036 /** 037 * Facebook "long" date format (IETF RFC 3339). Example: {@code 2010-02-28T16:11:08+0000} 038 */ 039 public static final String FACEBOOK_LONG_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ"; 040 041 /** 042 * Facebook "long" date format (IETF RFC 3339) without a timezone component. Example: {@code 2010-02-28T16:11:08} 043 */ 044 public static final String FACEBOOK_LONG_DATE_FORMAT_WITHOUT_TIMEZONE = "yyyy-MM-dd'T'HH:mm:ss"; 045 046 /** 047 * Facebook "long" date format (IETF RFC 3339) without a timezone or seconds component. Example: 048 * {@code 2010-02-28T16:11} 049 */ 050 public static final String FACEBOOK_LONG_DATE_FORMAT_WITHOUT_TIMEZONE_OR_SECONDS = "yyyy-MM-dd'T'HH:mm"; 051 052 /** 053 * Facebook short date format. Example: {@code 04/15/1984} 054 */ 055 public static final String FACEBOOK_SHORT_DATE_FORMAT = "MM/dd/yyyy"; 056 057 /** 058 * Facebook alternate short date format. Example: {@code 2012-09-15} 059 */ 060 public static final String FACEBOOK_ALTERNATE_SHORT_DATE_FORMAT = "yyyy-MM-dd"; 061 062 /** 063 * Facebook month-year only date format. Example: {@code Example: 2007-03} 064 */ 065 public static final String FACEBOOK_MONTH_YEAR_DATE_FORMAT = "yyyy-MM"; 066 067 /** 068 * DateFormatStrategy (default: SimpleDateFormat). 069 */ 070 private static DateFormatStrategy strategy = new SimpleDateFormatStrategy(); 071 072 /** 073 * Prevents instantiation. 074 */ 075 private DateUtils() { 076 // Prevents instantiation 077 } 078 079 /** 080 * Returns a Java representation of a Facebook "long" {@code date} string, or the number of seconds since the epoch. 081 * <p> 082 * Supports dates with or without timezone information. 083 * 084 * @param date 085 * Facebook {@code date} string. 086 * @return Java date representation of the given Facebook "long" {@code date} string or {@code null} if {@code date} 087 * is {@code null} or invalid. 088 */ 089 public static Date toDateFromLongFormat(String date) { 090 if (date == null) { 091 return null; 092 } 093 094 // Is this an all-digit date? Then assume it's the "seconds since epoch" 095 // variant 096 if (date.trim().matches("\\d+")) { 097 return new Date(Long.parseLong(date) * 1000L); 098 } 099 100 Date parsedDate = toDateWithFormatString(date, FACEBOOK_LONG_DATE_FORMAT); 101 102 // Fall back to variant without timezone if the initial parse fails 103 if (parsedDate == null) { 104 parsedDate = toDateWithFormatString(date, FACEBOOK_LONG_DATE_FORMAT_WITHOUT_TIMEZONE); 105 } 106 107 // Fall back to variant without seconds if secondary parse fails 108 if (parsedDate == null) { 109 parsedDate = toDateWithFormatString(date, FACEBOOK_LONG_DATE_FORMAT_WITHOUT_TIMEZONE_OR_SECONDS); 110 } 111 112 return parsedDate; 113 } 114 115 /** 116 * Returns a Java representation of a Facebook "short" {@code date} string. 117 * 118 * @param date 119 * Facebook {@code date} string. 120 * @return Java date representation of the given Facebook "short" {@code date} string or {@code null} if {@code date} 121 * is {@code null} or invalid. 122 */ 123 public static Date toDateFromShortFormat(String date) { 124 if (date == null) { 125 return null; 126 } 127 128 Date parsedDate = toDateWithFormatString(date, FACEBOOK_SHORT_DATE_FORMAT); 129 130 // Fall back to variant if initial parse fails 131 if (parsedDate == null) { 132 parsedDate = toDateWithFormatString(date, FACEBOOK_ALTERNATE_SHORT_DATE_FORMAT); 133 } 134 135 return parsedDate; 136 } 137 138 /** 139 * Returns a Java representation of a Facebook "month-year" {@code date} string. 140 * 141 * @param date 142 * Facebook {@code date} string. 143 * @return Java date representation of the given Facebook "month-year" {@code date} string or {@code null} if 144 * {@code date} is {@code null} or invalid. 145 */ 146 public static Date toDateFromMonthYearFormat(String date) { 147 if (date == null) { 148 return null; 149 } 150 151 if ("0000-00".equals(date)) { 152 return null; 153 } 154 155 return toDateWithFormatString(date, FACEBOOK_MONTH_YEAR_DATE_FORMAT); 156 } 157 158 /** 159 * Returns a String representation of a {@code date} object 160 * 161 * @param date 162 * as Date 163 * @return String representation of a {@code date} object. The String is in the form {@code 2010-02-28T16:11:08} 164 */ 165 public static String toLongFormatFromDate(Date date) { 166 if (date == null) { 167 return null; 168 } 169 170 return strategy.formatFor(FACEBOOK_LONG_DATE_FORMAT_WITHOUT_TIMEZONE).format(date); 171 } 172 173 /** 174 * Returns a <strong>short</strong> String representation of a {@code date} object 175 * 176 * @param date 177 * as Date 178 * @return String representation of a {@code date} object. The String is in the form {@code 2019-06-14} 179 */ 180 public static String toShortFormatFromDate(Date date) { 181 if (date == null) { 182 return null; 183 } 184 185 return strategy.formatFor(FACEBOOK_ALTERNATE_SHORT_DATE_FORMAT).format(date); 186 } 187 188 /** 189 * Returns a Java representation of a {@code date} string. 190 * 191 * @param date 192 * Date in string format. 193 * @return Java date representation of the given {@code date} string or {@code null} if {@code date} is {@code null} 194 * or invalid. 195 */ 196 private static Date toDateWithFormatString(String date, String format) { 197 if (date == null) { 198 return null; 199 } 200 201 try { 202 return strategy.formatFor(format).parse(date); 203 } catch (ParseException e) { 204 UTILS_LOGGER.trace("Unable to parse date '{}' using format string '{}': {}", date, format, e); 205 206 return null; 207 } 208 } 209 210 /** 211 * get the current DateFormatStrategy. 212 * 213 * @return the current DateFormatStrategy 214 */ 215 public static DateFormatStrategy getDateFormatStrategy() { 216 return strategy; 217 } 218 219 /** 220 * set the {@link DateFormatStrategy}. 221 * 222 * default value: {@link SimpleDateFormatStrategy} 223 * 224 * @param dateFormatStrategy 225 * the used {@link DateFormatStrategy} 226 * 227 */ 228 public static void setDateFormatStrategy(DateFormatStrategy dateFormatStrategy) { 229 strategy = dateFormatStrategy; 230 } 231}