1 package nom.tam.fits;
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 import java.text.DecimalFormat;
35 import java.util.Calendar;
36 import java.util.Date;
37 import java.util.TimeZone;
38 import java.util.regex.Matcher;
39 import java.util.regex.Pattern;
40
41
42
43
44
45 public class FitsDate implements Comparable<FitsDate> {
46
47
48
49
50
51 private static final int FIRST_THREE_CHARACTER_VALUE = 100;
52
53 private static final int FIRST_TWO_CHARACTER_VALUE = 10;
54
55 private static final int FITS_DATE_STRING_SIZE = 23;
56
57 private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
58
59 private static final int NEW_FORMAT_DAY_OF_MONTH_GROUP = 4;
60
61 private static final int NEW_FORMAT_HOUR_GROUP = 6;
62
63 private static final int NEW_FORMAT_MILLISECOND_GROUP = 10;
64
65 private static final int NEW_FORMAT_MINUTE_GROUP = 7;
66
67 private static final int NEW_FORMAT_MONTH_GROUP = 3;
68
69 private static final int NEW_FORMAT_SECOND_GROUP = 8;
70
71 private static final int NEW_FORMAT_YEAR_GROUP = 2;
72
73 private static final Pattern NORMAL_REGEX = Pattern.compile(
74 "\\s*(([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9]))(T([0-9][0-9]):([0-9][0-9]):([0-9][0-9])(\\.([0-9]+))?)?\\s*");
75
76 private static final int OLD_FORMAT_DAY_OF_MONTH_GROUP = 1;
77
78 private static final int OLD_FORMAT_MONTH_GROUP = 2;
79
80 private static final int OLD_FORMAT_YEAR_GROUP = 3;
81
82 private static final Pattern OLD_REGEX = Pattern.compile("\\s*([0-9][0-9])/([0-9][0-9])/([0-9][0-9])\\s*");
83
84 private static final int YEAR_OFFSET = 1900;
85
86 private static final int NB_DIGITS_MILLIS = 3;
87
88 private static final int POW_TEN = 10;
89
90
91
92
93
94
95
96
97 public static String getFitsDateString() {
98 return getFitsDateString(new Date(), true);
99 }
100
101
102
103
104
105
106
107
108
109
110
111 public static String getFitsDateString(Date epoch) {
112 return getFitsDateString(epoch, true);
113 }
114
115
116
117
118
119
120
121
122
123
124
125
126 public static String getFitsDateString(Date epoch, boolean timeOfDay) {
127 Calendar cal = Calendar.getInstance(UTC);
128 cal.setTime(epoch);
129 StringBuilder fitsDate = new StringBuilder();
130 DecimalFormat df = new DecimalFormat("0000");
131 fitsDate.append(df.format(cal.get(Calendar.YEAR)));
132 fitsDate.append("-");
133 df = new DecimalFormat("00");
134
135 fitsDate.append(df.format(cal.get(Calendar.MONTH) + 1));
136 fitsDate.append("-");
137 fitsDate.append(df.format(cal.get(Calendar.DAY_OF_MONTH)));
138
139 if (timeOfDay) {
140 fitsDate.append("T");
141 fitsDate.append(df.format(cal.get(Calendar.HOUR_OF_DAY)));
142 fitsDate.append(":");
143 fitsDate.append(df.format(cal.get(Calendar.MINUTE)));
144 fitsDate.append(":");
145 fitsDate.append(df.format(cal.get(Calendar.SECOND)));
146 fitsDate.append(".");
147 df = new DecimalFormat("000");
148 fitsDate.append(df.format(cal.get(Calendar.MILLISECOND)));
149 }
150 return fitsDate.toString();
151 }
152
153 private int hour = -1;
154
155 private int mday = -1;
156
157 private int millisecond = -1;
158
159 private int minute = -1;
160
161 private int month = -1;
162
163 private int second = -1;
164
165 private int year = -1;
166
167
168
169
170
171
172
173
174 public FitsDate(String dStr) throws FitsException {
175
176 if (dStr == null || dStr.isEmpty()) {
177 return;
178 }
179 Matcher match = FitsDate.NORMAL_REGEX.matcher(dStr);
180 if (match.matches()) {
181 year = getInt(match, FitsDate.NEW_FORMAT_YEAR_GROUP);
182 month = getInt(match, FitsDate.NEW_FORMAT_MONTH_GROUP);
183 mday = getInt(match, FitsDate.NEW_FORMAT_DAY_OF_MONTH_GROUP);
184 hour = getInt(match, FitsDate.NEW_FORMAT_HOUR_GROUP);
185 minute = getInt(match, FitsDate.NEW_FORMAT_MINUTE_GROUP);
186 second = getInt(match, FitsDate.NEW_FORMAT_SECOND_GROUP);
187 millisecond = getMilliseconds(match, FitsDate.NEW_FORMAT_MILLISECOND_GROUP);
188 } else {
189 match = FitsDate.OLD_REGEX.matcher(dStr);
190 if (!match.matches()) {
191 if (dStr.trim().isEmpty()) {
192 return;
193 }
194 throw new FitsException("Bad FITS date string \"" + dStr + '"');
195 }
196 year = getInt(match, FitsDate.OLD_FORMAT_YEAR_GROUP) + FitsDate.YEAR_OFFSET;
197 month = getInt(match, FitsDate.OLD_FORMAT_MONTH_GROUP);
198 mday = getInt(match, FitsDate.OLD_FORMAT_DAY_OF_MONTH_GROUP);
199 }
200 }
201
202 private static int getInt(Matcher match, int groupIndex) {
203 String value = match.group(groupIndex);
204 if (value != null) {
205 return Integer.parseInt(value);
206 }
207 return -1;
208 }
209
210 private static int getMilliseconds(Matcher match, int groupIndex) {
211 String value = match.group(groupIndex);
212 if (value != null) {
213 value = String.format("%-3s", value).replace(' ', '0');
214 int num = Integer.parseInt(value);
215 if (value.length() > NB_DIGITS_MILLIS) {
216 num = (int) Math.round(num / Math.pow(POW_TEN, value.length() - NB_DIGITS_MILLIS));
217 }
218 return num;
219 }
220 return -1;
221 }
222
223
224
225
226
227
228 public Date toDate() {
229 if (year == -1) {
230 return null;
231 }
232
233 Calendar cal = Calendar.getInstance(UTC);
234
235 cal.set(Calendar.YEAR, year);
236 cal.set(Calendar.MONTH, month - 1);
237 cal.set(Calendar.DAY_OF_MONTH, mday);
238
239 if (hour == -1) {
240 cal.set(Calendar.HOUR_OF_DAY, 0);
241 cal.set(Calendar.MINUTE, 0);
242 cal.set(Calendar.SECOND, 0);
243 cal.set(Calendar.MILLISECOND, 0);
244 } else {
245 cal.set(Calendar.HOUR_OF_DAY, hour);
246 cal.set(Calendar.MINUTE, minute);
247 cal.set(Calendar.SECOND, second);
248 if (millisecond == -1) {
249 cal.set(Calendar.MILLISECOND, 0);
250 } else {
251 cal.set(Calendar.MILLISECOND, millisecond);
252 }
253 }
254 return cal.getTime();
255 }
256
257 @Override
258 public String toString() {
259 if (year == -1) {
260 return "";
261 }
262 StringBuilder buf = new StringBuilder(FitsDate.FITS_DATE_STRING_SIZE);
263 buf.append(year);
264 buf.append('-');
265 appendTwoDigitValue(buf, month);
266 buf.append('-');
267 appendTwoDigitValue(buf, mday);
268 if (hour != -1) {
269 buf.append('T');
270 appendTwoDigitValue(buf, hour);
271 buf.append(':');
272 appendTwoDigitValue(buf, minute);
273 buf.append(':');
274 appendTwoDigitValue(buf, second);
275 if (millisecond != -1) {
276 buf.append('.');
277 appendThreeDigitValue(buf, millisecond);
278 }
279 }
280 return buf.toString();
281 }
282
283 @Override
284 public boolean equals(Object o) {
285 if (o == this) {
286 return true;
287 }
288 if (!(o instanceof FitsDate)) {
289 return false;
290 }
291
292 return compareTo((FitsDate) o) == 0;
293 }
294
295 @Override
296 public int hashCode() {
297 return Integer.hashCode(year) ^ Integer.hashCode(month) ^ Integer.hashCode(mday) ^ Integer.hashCode(hour)
298 ^ Integer.hashCode(minute) ^ Integer.hashCode(second) ^ Integer.hashCode(millisecond);
299 }
300
301 @Override
302 public int compareTo(FitsDate fitsDate) {
303 int result = Integer.compare(year, fitsDate.year);
304 if (result != 0) {
305 return result;
306 }
307
308 result = Integer.compare(month, fitsDate.month);
309 if (result != 0) {
310 return result;
311 }
312
313 result = Integer.compare(mday, fitsDate.mday);
314 if (result != 0) {
315 return result;
316 }
317
318 result = Integer.compare(hour, fitsDate.hour);
319 if (result != 0) {
320 return result;
321 }
322
323 result = Integer.compare(minute, fitsDate.minute);
324 if (result != 0) {
325 return result;
326 }
327
328 result = Integer.compare(second, fitsDate.second);
329 if (result != 0) {
330 return result;
331 }
332
333 return Integer.compare(millisecond, fitsDate.millisecond);
334 }
335
336 private void appendThreeDigitValue(StringBuilder buf, int value) {
337 if (value < FitsDate.FIRST_THREE_CHARACTER_VALUE) {
338 buf.append('0');
339 }
340 appendTwoDigitValue(buf, value);
341 }
342
343 private void appendTwoDigitValue(StringBuilder buf, int value) {
344 if (value < FitsDate.FIRST_TWO_CHARACTER_VALUE) {
345 buf.append('0');
346 }
347 buf.append(value);
348 }
349 }