-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDateOnlyParser.java
191 lines (164 loc) · 5.15 KB
/
DateOnlyParser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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
141
142
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
186
187
188
189
190
191
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
/**
* Strict date only parser (String to Date conversion)
*
* Use 'd', 'm' or 'y' to pattern match date parts
* 'd': day of month
* 'm': month
* 'y': year
*
* - 1 char will match a variable length date part
* - 2 or more chars will match a fixed length date part
* - If date part is missing in the pattern the date part will get default 1
* - Delimiters can be everything but 'd', 'm' or 'y'
* - Calendar is always GregorianCalendar
* - Uppercase 'D', 'M' or 'Y' also matches date parts
* - Not thread safe
*
* Parseable samples
* "d-m-y" "1-11-1974"
* "d.m.y" "01.11.1974"
* "d m y" "1 11 1974"
* "dd/mm-yyyy" "01/11-1974"
* "yyyymmdd" "19741101"
* "y" "1974"
*
* Unparsable samples (throws ParseException)
* "d-m-y" "1-11/1974"
* "dd-mm-yyyy" "1-11-1974"
* "d-m-y" "19-11-1974 "
* "d-m-y" " 19-11-1974"
* "y" "1-11-1974"
*
* @version 1.0
*/
public class DateOnlyParser
{
private final int patternLength;
private final char[] patternChars;
private final GregorianCalendar cal = new GregorianCalendar();
private final int maxYear = cal.getMaximum(Calendar.YEAR);
public DateOnlyParser(String pattern) throws NullPointerException, IllegalArgumentException
{
if (pattern == null)
throw new NullPointerException(pattern);
boolean styleDetected = false;
patternChars = pattern.toCharArray();
patternLength = patternChars.length;
for (int i = 0; i<patternLength; i++)
{
char c = Character.toLowerCase(patternChars[i]);
if (c == 'y' || c == 'm' || c == 'd')
{
patternChars[i] = c;
styleDetected = true;
}
}
if (!styleDetected)
throw new IllegalArgumentException("Format should contain d, m or y");
cal.clear(); //removes time
}
private final boolean isDigit(char c)
{
return c >= '0' && c <= '9';
}
public Date parseExact(String val) throws NullPointerException, ParseException
{
if (val == null)
throw new NullPointerException(val);
final char[] vchars = val.toCharArray();
final int vlength = vchars.length;
int vindx = 0, year = 1, month = 1, day = 1;
for (int pidx = 0; pidx<patternLength; pidx++)
{
if (vindx == vlength)
throwParseException("Unexpected end of String", val, vindx);
char c = patternChars[pidx];
if (c == 'y' || c == 'm' || c == 'd')
{
int tokenLength = 1;
//detect repeating matching patterns e.g yyyy
while (pidx<patternLength-1 && patternChars[pidx+1] == c)
{
pidx++;
tokenLength++;
}
//read first digit
if (!isDigit(vchars[vindx]))
throwParseException("Expected digit", val, vindx);
int n = vchars[vindx] - '0';
vindx++;
//read fixed digits
if (tokenLength > 1)
{
while (tokenLength > 1)
{
if (vindx == vlength || !isDigit(vchars[vindx]))
throwParseException("Expected digit", val, vindx);
n *= 10;
n += vchars[vindx] - '0';
if (n < 0)
throwParseException("Number overflow", val, vindx);
tokenLength--;
vindx++;
}
}
//read variable digits
else
{
while(vindx < vlength && isDigit(vchars[vindx]))
{
n *= 10;
n += vchars[vindx] - '0';
if (n < 0)
throwParseException("Number overflow", val, vindx);
vindx++;
}
}
if (c == 'y')
year = n;
else if (c == 'm')
month = n;
else if (c == 'd')
day = n;
}
else
{
if (vchars[vindx] == c)
vindx++;
else
throwParseException("Expected '" + c + "'", val, vindx);
}
}
if (vindx < vlength)
throwParseException("Unexpected trailing string '" + val.substring(vindx) + "'", val, vindx);
if (day == 0 || day > 31)
throwParseException("Illegal day " + day, val);
if (month == 0 || month > 12)
throwParseException("Illegal month " + month, val);
if (year == 0 || year > maxYear)
throwParseException("Illegal year " + year, val);
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month-1);
cal.set(Calendar.DAY_OF_MONTH, day);
if (cal.get(Calendar.DAY_OF_MONTH) != day)
throwParseException("Illegal day " + day, val);
return cal.getTime();
}
private void throwParseException(String msg, String val) throws ParseException
{
throwParseException(msg, val, -1);
}
private void throwParseException(String msg, String val, int vindx) throws ParseException
{
StringBuilder sb = new StringBuilder("Unparseable date \"" + val + "\"");
sb.append(" using pattern \"" + new String(patternChars) + "\"");
sb.append(". " + msg);
if (vindx > -1)
sb.append(" at index " + vindx);
throw new ParseException(sb.toString(), vindx);
}
}