001 /*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * SonarQube is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
019 */
020 package org.sonar.api.utils;
021
022 import javax.annotation.Nullable;
023
024 import java.io.NotSerializableException;
025 import java.io.ObjectInputStream;
026 import java.io.ObjectOutputStream;
027 import java.lang.ref.Reference;
028 import java.lang.ref.SoftReference;
029 import java.text.DateFormat;
030 import java.text.FieldPosition;
031 import java.text.ParsePosition;
032 import java.text.SimpleDateFormat;
033 import java.util.Date;
034
035 /**
036 * Parses and formats <a href="http://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a> dates.
037 * This class is thread-safe.
038 *
039 * @since 2.7
040 */
041 public final class DateUtils {
042 public static final String DATE_FORMAT = "yyyy-MM-dd";
043 public static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";
044
045 private static final ThreadSafeDateFormat THREAD_SAFE_DATE_FORMAT = new ThreadSafeDateFormat(DATE_FORMAT);
046 private static final ThreadSafeDateFormat THREAD_SAFE_DATETIME_FORMAT = new ThreadSafeDateFormat(DATETIME_FORMAT);
047
048 private DateUtils() {
049 }
050
051 public static String formatDate(Date d) {
052 return THREAD_SAFE_DATE_FORMAT.format(d);
053 }
054
055 public static String formatDateTime(Date d) {
056 return THREAD_SAFE_DATETIME_FORMAT.format(d);
057 }
058
059 public static String formatDateTimeNullSafe(@Nullable Date date) {
060 if (date == null) {
061 return "";
062 }
063
064 return THREAD_SAFE_DATETIME_FORMAT.format(date);
065 }
066
067 /**
068 * @param s string in format {@link #DATE_FORMAT}
069 * @throws SonarException when string cannot be parsed
070 */
071 public static Date parseDate(String s) {
072 ParsePosition pos = new ParsePosition(0);
073 Date result = THREAD_SAFE_DATE_FORMAT.parse(s, pos);
074 if (pos.getIndex() != s.length()) {
075 throw new SonarException("The date '" + s + "' does not respect format '" + DATE_FORMAT + "'");
076 }
077 return result;
078 }
079
080 /**
081 * Parse format {@link #DATE_FORMAT}. This method never throws exception.
082 *
083 * @param s any string
084 * @return the date, null if parsing error or null string
085 * @since 3.0
086 */
087 public static Date parseDateQuietly(@Nullable String s) {
088 Date date = null;
089 if (s != null) {
090 try {
091 date = parseDate(s);
092 } catch (RuntimeException e) {
093 // ignore
094 }
095
096 }
097 return date;
098 }
099
100 /**
101 * @param s string in format {@link #DATETIME_FORMAT}
102 * @throws SonarException when string cannot be parsed
103 */
104
105 public static Date parseDateTime(String s) {
106 ParsePosition pos = new ParsePosition(0);
107 Date result = THREAD_SAFE_DATETIME_FORMAT.parse(s, pos);
108 if (pos.getIndex() != s.length()) {
109 throw new SonarException("The date '" + s + "' does not respect format '" + DATETIME_FORMAT + "'");
110 }
111 return result;
112 }
113
114 /**
115 * Parse format {@link #DATETIME_FORMAT}. This method never throws exception.
116 *
117 * @param s any string
118 * @return the datetime, null if parsing error or null string
119 */
120 public static Date parseDateTimeQuietly(@Nullable String s) {
121 Date datetime = null;
122 if (s != null) {
123 try {
124 datetime = parseDateTime(s);
125 } catch (RuntimeException e) {
126 // ignore
127 }
128
129 }
130 return datetime;
131 }
132
133 public static Date addDays(Date date, int numberOfDays) {
134 return org.apache.commons.lang.time.DateUtils.addDays(date, numberOfDays);
135 }
136
137 static class ThreadSafeDateFormat extends DateFormat {
138 private final String format;
139 private final ThreadLocal<Reference<DateFormat>> cache = new ThreadLocal<Reference<DateFormat>>() {
140 @Override
141 public Reference<DateFormat> get() {
142 Reference<DateFormat> softRef = super.get();
143 if (softRef == null || softRef.get() == null) {
144 softRef = new SoftReference<DateFormat>(new SimpleDateFormat(format));
145 super.set(softRef);
146 }
147 return softRef;
148 }
149 };
150
151 ThreadSafeDateFormat(String format) {
152 this.format = format;
153 }
154
155 private DateFormat getDateFormat() {
156 return (DateFormat) ((Reference) cache.get()).get();
157 }
158
159 @Override
160 public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
161 return getDateFormat().format(date, toAppendTo, fieldPosition);
162 }
163
164 @Override
165 public Date parse(String source, ParsePosition pos) {
166 return getDateFormat().parse(source, pos);
167 }
168
169 private void readObject(ObjectInputStream ois) throws NotSerializableException {
170 throw new NotSerializableException();
171 }
172
173 private void writeObject(ObjectOutputStream ois) throws NotSerializableException {
174 throw new NotSerializableException();
175 }
176 }
177 }