1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.util;
11
12 import java.text.MessageFormat;
13 import java.text.ParseException;
14 import java.text.SimpleDateFormat;
15 import java.util.Calendar;
16 import java.util.Date;
17 import java.util.GregorianCalendar;
18 import java.util.HashMap;
19 import java.util.Locale;
20 import java.util.Map;
21
22 import org.eclipse.jgit.internal.JGitText;
23
24
25
26
27
28
29
30
31
32 public class GitDateParser {
33
34
35
36
37 public static final Date NEVER = new Date(Long.MAX_VALUE);
38
39
40
41
42 private static ThreadLocal<Map<Locale, Map<ParseableSimpleDateFormat, SimpleDateFormat>>> formatCache =
43 new ThreadLocal<>() {
44
45 @Override
46 protected Map<Locale, Map<ParseableSimpleDateFormat, SimpleDateFormat>> initialValue() {
47 return new HashMap<>();
48 }
49 };
50
51
52
53
54 private static SimpleDateFormat getDateFormat(ParseableSimpleDateFormat f,
55 Locale locale) {
56 Map<Locale, Map<ParseableSimpleDateFormat, SimpleDateFormat>> cache = formatCache
57 .get();
58 Map<ParseableSimpleDateFormat, SimpleDateFormat> map = cache
59 .get(locale);
60 if (map == null) {
61 map = new HashMap<>();
62 cache.put(locale, map);
63 return getNewSimpleDateFormat(f, locale, map);
64 }
65 SimpleDateFormat dateFormat = map.get(f);
66 if (dateFormat != null)
67 return dateFormat;
68 SimpleDateFormat df = getNewSimpleDateFormat(f, locale, map);
69 return df;
70 }
71
72 private static SimpleDateFormat getNewSimpleDateFormat(
73 ParseableSimpleDateFormat f, Locale locale,
74 Map<ParseableSimpleDateFormat, SimpleDateFormat> map) {
75 SimpleDateFormat df = SystemReader.getInstance().getSimpleDateFormat(
76 f.formatStr, locale);
77 map.put(f, df);
78 return df;
79 }
80
81
82
83
84
85
86 enum ParseableSimpleDateFormat {
87 ISO("yyyy-MM-dd HH:mm:ss Z"),
88 RFC("EEE, dd MMM yyyy HH:mm:ss Z"),
89 SHORT("yyyy-MM-dd"),
90 SHORT_WITH_DOTS_REVERSE("dd.MM.yyyy"),
91 SHORT_WITH_DOTS("yyyy.MM.dd"),
92 SHORT_WITH_SLASH("MM/dd/yyyy"),
93 DEFAULT("EEE MMM dd HH:mm:ss yyyy Z"),
94 LOCAL("EEE MMM dd HH:mm:ss yyyy");
95
96 private final String formatStr;
97
98 private ParseableSimpleDateFormat(String formatStr) {
99 this.formatStr = formatStr;
100 }
101 }
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140 public static Date parse(String dateStr, Calendar now)
141 throws ParseException {
142 return parse(dateStr, now, Locale.getDefault());
143 }
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185 public static Date parse(String dateStr, Calendar now, Locale locale)
186 throws ParseException {
187 dateStr = dateStr.trim();
188 Date ret;
189
190 if ("never".equalsIgnoreCase(dateStr))
191 return NEVER;
192 ret = parse_relative(dateStr, now);
193 if (ret != null)
194 return ret;
195 for (ParseableSimpleDateFormat f : ParseableSimpleDateFormat.values()) {
196 try {
197 return parse_simple(dateStr, f, locale);
198 } catch (ParseException e) {
199
200 }
201 }
202 ParseableSimpleDateFormat[] values = ParseableSimpleDateFormat.values();
203 StringBuilder allFormats = new StringBuilder("\"")
204 .append(values[0].formatStr);
205 for (int i = 1; i < values.length; i++)
206 allFormats.append("\", \"").append(values[i].formatStr);
207 allFormats.append("\"");
208 throw new ParseException(MessageFormat.format(
209 JGitText.get().cannotParseDate, dateStr, allFormats.toString()), 0);
210 }
211
212
213 private static Date parse_simple(String dateStr,
214 ParseableSimpleDateFormat f, Locale locale)
215 throws ParseException {
216 SimpleDateFormat dateFormat = getDateFormat(f, locale);
217 dateFormat.setLenient(false);
218 return dateFormat.parse(dateStr);
219 }
220
221
222 @SuppressWarnings("nls")
223 private static Date parse_relative(String dateStr, Calendar now) {
224 Calendar cal;
225 SystemReader sysRead = SystemReader.getInstance();
226
227
228 if ("now".equals(dateStr)) {
229 return ((now == null) ? new Date(sysRead.getCurrentTime()) : now
230 .getTime());
231 }
232
233 if (now == null) {
234 cal = new GregorianCalendar(sysRead.getTimeZone(),
235 sysRead.getLocale());
236 cal.setTimeInMillis(sysRead.getCurrentTime());
237 } else
238 cal = (Calendar) now.clone();
239
240 if ("yesterday".equals(dateStr)) {
241 cal.add(Calendar.DATE, -1);
242 cal.set(Calendar.HOUR_OF_DAY, 0);
243 cal.set(Calendar.MINUTE, 0);
244 cal.set(Calendar.SECOND, 0);
245 cal.set(Calendar.MILLISECOND, 0);
246 cal.set(Calendar.MILLISECOND, 0);
247 return cal.getTime();
248 }
249
250
251 String[] parts = dateStr.split("\\.| ");
252 int partsLength = parts.length;
253
254
255 if (partsLength < 3 || (partsLength & 1) == 0
256 || !"ago".equals(parts[parts.length - 1]))
257 return null;
258 int number;
259 for (int i = 0; i < parts.length - 2; i += 2) {
260 try {
261 number = Integer.parseInt(parts[i]);
262 } catch (NumberFormatException e) {
263 return null;
264 }
265 if (parts[i + 1] == null){
266 return null;
267 }
268 switch (parts[i + 1]) {
269 case "year":
270 case "years":
271 cal.add(Calendar.YEAR, -number);
272 break;
273 case "month":
274 case "months":
275 cal.add(Calendar.MONTH, -number);
276 break;
277 case "week":
278 case "weeks":
279 cal.add(Calendar.WEEK_OF_YEAR, -number);
280 break;
281 case "day":
282 case "days":
283 cal.add(Calendar.DATE, -number);
284 break;
285 case "hour":
286 case "hours":
287 cal.add(Calendar.HOUR_OF_DAY, -number);
288 break;
289 case "minute":
290 case "minutes":
291 cal.add(Calendar.MINUTE, -number);
292 break;
293 case "second":
294 case "seconds":
295 cal.add(Calendar.SECOND, -number);
296 break;
297 default:
298 return null;
299 }
300 }
301 return cal.getTime();
302 }
303 }