2
This code is derived from jgit (http://eclipse.org/jgit).
3
Copyright owners are documented in jgit's IP log.
5
This program and the accompanying materials are made available
6
under the terms of the Eclipse Distribution License v1.0 which
7
accompanies this distribution, is reproduced below, and is
8
available at http://www.eclipse.org/org/documents/edl-v10.php
12
Redistribution and use in source and binary forms, with or
13
without modification, are permitted provided that the following
16
- Redistributions of source code must retain the above copyright
17
notice, this list of conditions and the following disclaimer.
19
- Redistributions in binary form must reproduce the above
20
copyright notice, this list of conditions and the following
21
disclaimer in the documentation and/or other materials provided
22
with the distribution.
24
- Neither the name of the Eclipse Foundation, Inc. nor the
25
names of its contributors may be used to endorse or promote
26
products derived from this software without specific prior
29
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45
using System.Collections.Generic;
50
using System.Threading;
51
using System.Globalization;
56
/// Parses strings with time and date specifications into
57
/// <see cref="System.DateTime">System.DateTime</see>
59
/// When git needs to parse strings specified by the user this parser can be
60
/// used. One example is the parsing of the config parameter gc.pruneexpire. The
61
/// parser can handle only subset of what native gits approxidate parser
64
public class GitDateParser
66
/// <summary>The Date representing never.</summary>
68
/// The Date representing never. Though this is a concrete value, most
69
/// callers are adviced to avoid depending on the actual value.
71
public static readonly DateTime NEVER = Sharpen.Extensions.CreateDate(long.MaxValue
74
private sealed class _ThreadLocal_74 : ThreadLocal<IDictionary<GitDateParser.ParseableSimpleDateFormat
77
public _ThreadLocal_74()
78
: base (() => new Dictionary<GitDateParser.ParseableSimpleDateFormat, SimpleDateFormat>())
83
private static ThreadLocal<IDictionary<GitDateParser.ParseableSimpleDateFormat, SimpleDateFormat
84
>> formatCache = new _ThreadLocal_74();
86
// Gets an instance of a SimpleDateFormat. If there is not already an
87
// appropriate instance in the (ThreadLocal) cache the create one and put in
89
private static SimpleDateFormat GetDateFormat(GitDateParser.ParseableSimpleDateFormat
92
IDictionary<GitDateParser.ParseableSimpleDateFormat, SimpleDateFormat> map = formatCache
94
SimpleDateFormat dateFormat = map.Get(f);
95
if (dateFormat != null)
99
SimpleDateFormat df = SystemReader.GetInstance().GetSimpleDateFormat(f.formatStr);
104
public class ParseableSimpleDateFormat
106
public static GitDateParser.ParseableSimpleDateFormat ISO = new GitDateParser.ParseableSimpleDateFormat
107
("yyyy-MM-dd HH:mm:ss Z");
109
public static GitDateParser.ParseableSimpleDateFormat RFC = new GitDateParser.ParseableSimpleDateFormat
110
("EEE, dd MMM yyyy HH:mm:ss Z");
112
public static GitDateParser.ParseableSimpleDateFormat SHORT = new GitDateParser.ParseableSimpleDateFormat
115
public static GitDateParser.ParseableSimpleDateFormat SHORT_WITH_DOTS_REVERSE = new
116
GitDateParser.ParseableSimpleDateFormat("dd.MM.yyyy");
118
public static GitDateParser.ParseableSimpleDateFormat SHORT_WITH_DOTS = new GitDateParser.ParseableSimpleDateFormat
121
public static GitDateParser.ParseableSimpleDateFormat SHORT_WITH_SLASH = new GitDateParser.ParseableSimpleDateFormat
124
public static GitDateParser.ParseableSimpleDateFormat DEFAULT = new GitDateParser.ParseableSimpleDateFormat
125
("EEE MMM dd HH:mm:ss yyyy Z");
127
public static GitDateParser.ParseableSimpleDateFormat LOCAL = new GitDateParser.ParseableSimpleDateFormat
128
("EEE MMM dd HH:mm:ss yyyy");
130
// An enum of all those formats which this parser can parse with the help of
131
// a SimpleDateFormat. There are other formats (e.g. the relative formats
132
// like "yesterday" or "1 week ago") which this parser can parse but which
133
// are not listed here because they are parsed without the help of a
142
public static GitDateParser.ParseableSimpleDateFormat[] Values()
144
return new GitDateParser.ParseableSimpleDateFormat[] { ISO, RFC, SHORT, SHORT_WITH_DOTS_REVERSE
145
, SHORT_WITH_DOTS, SHORT_WITH_SLASH, DEFAULT, LOCAL };
148
public string formatStr;
150
private ParseableSimpleDateFormat(string formatStr)
152
this.formatStr = formatStr;
157
/// Parses a string into a
158
/// <see cref="System.DateTime">System.DateTime</see>
159
/// . Since this parser also supports
160
/// relative formats (e.g. "yesterday") the caller can specify the reference
161
/// date. These types of strings can be parsed:
165
/// <li>"yesterday"</li>
166
/// <li>"(x) years|months|weeks|days|hours|minutes|seconds ago"<br />
167
/// Multiple specs can be combined like in "2 weeks 3 days ago". Instead of
168
/// ' ' one can use '.' to seperate the words</li>
169
/// <li>"yyyy-MM-dd HH:mm:ss Z" (ISO)</li>
170
/// <li>"EEE, dd MMM yyyy HH:mm:ss Z" (RFC)</li>
171
/// <li>"yyyy-MM-dd"</li>
172
/// <li>"yyyy.MM.dd"</li>
173
/// <li>"MM/dd/yyyy",</li>
174
/// <li>"dd.MM.yyyy"</li>
175
/// <li>"EEE MMM dd HH:mm:ss yyyy Z" (DEFAULT)</li>
176
/// <li>"EEE MMM dd HH:mm:ss yyyy" (LOCAL)</li>
179
/// <param name="dateStr">the string to be parsed</param>
180
/// <param name="now">
181
/// the base date which is used for the calculation of relative
182
/// formats. E.g. if baseDate is "25.8.2012" then parsing of the
183
/// string "1 week ago" would result in a date corresponding to
184
/// "18.8.2012". This is used when a JGit command calls this
185
/// parser often but wants a consistent starting point for calls.<br />
186
/// If set to <code>null</code> then the current time will be used
191
/// <see cref="System.DateTime">System.DateTime</see>
193
/// <exception cref="Sharpen.ParseException">if the given dateStr was not recognized</exception>
194
public static DateTime Parse(string dateStr, JavaCalendar now)
196
dateStr = dateStr.Trim();
198
if (Sharpen.Runtime.EqualsIgnoreCase("never", dateStr))
202
ret = Parse_relative(dateStr, now);
207
foreach (GitDateParser.ParseableSimpleDateFormat f in GitDateParser.ParseableSimpleDateFormat
212
return Parse_simple(dateStr, f);
214
catch (ParseException)
218
// simply proceed with the next parser
219
GitDateParser.ParseableSimpleDateFormat[] values = GitDateParser.ParseableSimpleDateFormat
221
StringBuilder allFormats = new StringBuilder("\"").Append(values[0].formatStr);
222
for (int i = 1; i < values.Length; i++)
224
allFormats.Append("\", \"").Append(values[i].formatStr);
226
allFormats.Append("\"");
227
throw new ParseException(MessageFormat.Format(JGitText.Get().cannotParseDate, dateStr
228
, allFormats.ToString()), 0);
231
// tries to parse a string with the formats supported by SimpleDateFormat
232
/// <exception cref="Sharpen.ParseException"></exception>
233
private static DateTime Parse_simple(string dateStr, GitDateParser.ParseableSimpleDateFormat
236
SimpleDateFormat dateFormat = GetDateFormat(f);
237
dateFormat.SetLenient(false);
238
return dateFormat.Parse(dateStr);
241
// tries to parse a string with a relative time specification
242
private static DateTime? Parse_relative(string dateStr, JavaCalendar now)
245
SystemReader sysRead = SystemReader.GetInstance();
246
// check for the static words "yesterday" or "now"
247
if ("now".Equals(dateStr))
249
return ((now == null) ? Sharpen.Extensions.CreateDate(sysRead.GetCurrentTime()) :
254
cal = new JavaGregorianCalendar(sysRead.GetTimeZone(), sysRead.GetLocale());
255
cal.SetTimeInMillis(sysRead.GetCurrentTime());
259
cal = (JavaCalendar)now.Clone();
261
if ("yesterday".Equals(dateStr))
263
cal.Add(JavaCalendar.DATE, -1);
264
cal.Set(JavaCalendar.HOUR_OF_DAY, 0);
265
cal.Set(JavaCalendar.MINUTE, 0);
266
cal.Set(JavaCalendar.SECOND, 0);
267
cal.Set(JavaCalendar.MILLISECOND, 0);
268
cal.Set(JavaCalendar.MILLISECOND, 0);
269
return cal.GetTime();
271
// parse constructs like "3 days ago", "5.week.2.day.ago"
272
string[] parts = dateStr.Split("\\.| ");
273
int partsLength = parts.Length;
274
// check we have an odd number of parts (at least 3) and that the last
276
if (partsLength < 3 || (partsLength & 1) == 0 || !"ago".Equals(parts[parts.Length
282
for (int i = 0; i < parts.Length - 2; i += 2)
286
number = System.Convert.ToInt32(parts[i]);
288
catch (FormatException)
292
if ("year".Equals(parts[i + 1]) || "years".Equals(parts[i + 1]))
294
cal.Add(JavaCalendar.YEAR, -number);
298
if ("month".Equals(parts[i + 1]) || "months".Equals(parts[i + 1]))
300
cal.Add(JavaCalendar.MONTH, -number);
304
if ("week".Equals(parts[i + 1]) || "weeks".Equals(parts[i + 1]))
306
cal.Add(JavaCalendar.WEEK_OF_YEAR, -number);
310
if ("day".Equals(parts[i + 1]) || "days".Equals(parts[i + 1]))
312
cal.Add(JavaCalendar.DATE, -number);
316
if ("hour".Equals(parts[i + 1]) || "hours".Equals(parts[i + 1]))
318
cal.Add(JavaCalendar.HOUR_OF_DAY, -number);
322
if ("minute".Equals(parts[i + 1]) || "minutes".Equals(parts[i + 1]))
324
cal.Add(JavaCalendar.MINUTE, -number);
328
if ("second".Equals(parts[i + 1]) || "seconds".Equals(parts[i + 1]))
330
cal.Add(JavaCalendar.SECOND, -number);
343
return cal.GetTime();