1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.time;
18
19 import java.io.Serializable;
20 import java.text.DecimalFormat;
21 import java.util.regex.Matcher;
22 import java.util.regex.Pattern;
23
24 import org.orekit.errors.OrekitIllegalArgumentException;
25 import org.orekit.errors.OrekitMessages;
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 public class DateComponents implements Serializable, Comparable<DateComponents> {
43
44
45
46
47
48
49
50
51 public static final DateComponents JULIAN_EPOCH;
52
53
54 public static final DateComponents MODIFIED_JULIAN_EPOCH;
55
56
57 public static final DateComponents FIFTIES_EPOCH;
58
59
60 public static final DateComponents CCSDS_EPOCH;
61
62
63 public static final DateComponents GALILEO_EPOCH;
64
65
66 public static final DateComponents GPS_EPOCH;
67
68
69 public static final DateComponents J2000_EPOCH;
70
71
72 public static final DateComponents JAVA_EPOCH;
73
74
75 private static final long serialVersionUID = -2462694707837970938L;
76
77
78 private static final YearFactory PROLEPTIC_JULIAN_FACTORY = new ProlepticJulianFactory();
79
80
81 private static final YearFactory JULIAN_FACTORY = new JulianFactory();
82
83
84 private static final YearFactory GREGORIAN_FACTORY = new GregorianFactory();
85
86
87 private static final MonthDayFactory LEAP_YEAR_FACTORY = new LeapYearFactory();
88
89
90 private static final MonthDayFactory COMMON_YEAR_FACTORY = new CommonYearFactory();
91
92
93 private static final DecimalFormat FOUR_DIGITS = new DecimalFormat("0000");
94
95
96 private static final DecimalFormat TWO_DIGITS = new DecimalFormat("00");
97
98
99 private static final int MJD_TO_J2000 = 51544;
100
101
102 private static Pattern CALENDAR_FORMAT = Pattern.compile("^(-?\\d\\d\\d\\d)-?(\\d\\d)-?(\\d\\d)$");
103
104
105 private static Pattern ORDINAL_FORMAT = Pattern.compile("^(-?\\d\\d\\d\\d)-?(\\d\\d\\d)$");
106
107
108 private static Pattern WEEK_FORMAT = Pattern.compile("^(-?\\d\\d\\d\\d)-?W(\\d\\d)-?(\\d)$");
109
110 static {
111
112
113 JULIAN_EPOCH = new DateComponents(-4712, 1, 1);
114 MODIFIED_JULIAN_EPOCH = new DateComponents(1858, 11, 17);
115 FIFTIES_EPOCH = new DateComponents(1950, 1, 1);
116 CCSDS_EPOCH = new DateComponents(1958, 1, 1);
117 GALILEO_EPOCH = new DateComponents(1999, 8, 22);
118 GPS_EPOCH = new DateComponents(1980, 1, 6);
119 J2000_EPOCH = new DateComponents(2000, 1, 1);
120 JAVA_EPOCH = new DateComponents(1970, 1, 1);
121 }
122
123
124 private final int year;
125
126
127 private final int month;
128
129
130 private final int day;
131
132
133
134
135
136
137
138
139
140 public DateComponents(final int year, final int month, final int day)
141 throws IllegalArgumentException {
142
143
144
145 if ((month < 1) || (month > 12)) {
146 throw new OrekitIllegalArgumentException(OrekitMessages.NON_EXISTENT_MONTH, month);
147 }
148
149
150 this.year = year;
151 this.month = month;
152 this.day = day;
153
154
155 final DateComponents check = new DateComponents(getJ2000Day());
156
157
158
159 if ((year != check.year) || (month != check.month) || (day != check.day)) {
160 throw new OrekitIllegalArgumentException(OrekitMessages.NON_EXISTENT_YEAR_MONTH_DAY,
161 year, month, day);
162 }
163
164 }
165
166
167
168
169
170
171
172
173
174 public DateComponents(final int year, final Month month, final int day)
175 throws IllegalArgumentException {
176 this(year, month.getNumber(), day);
177 }
178
179
180
181
182
183
184
185 public DateComponents(final int year, final int dayNumber)
186 throws IllegalArgumentException {
187 this(J2000_EPOCH, new DateComponents(year - 1, 12, 31).getJ2000Day() + dayNumber);
188 if (dayNumber != getDayOfYear()) {
189 throw new OrekitIllegalArgumentException(OrekitMessages.NON_EXISTENT_DAY_NUMBER_IN_YEAR,
190 dayNumber, year);
191 }
192 }
193
194
195
196
197
198 public DateComponents(final int offset) {
199
200
201
202
203
204
205 YearFactory yFactory = GREGORIAN_FACTORY;
206 if (offset < -152384) {
207 if (offset > -730122) {
208 yFactory = JULIAN_FACTORY;
209 } else {
210 yFactory = PROLEPTIC_JULIAN_FACTORY;
211 }
212 }
213 year = yFactory.getYear(offset);
214 final int dayInYear = offset - yFactory.getLastJ2000DayOfYear(year - 1);
215
216
217 final MonthDayFactory mdFactory =
218 yFactory.isLeap(year) ? LEAP_YEAR_FACTORY : COMMON_YEAR_FACTORY;
219 month = mdFactory.getMonth(dayInYear);
220 day = mdFactory.getDay(dayInYear, month);
221
222 }
223
224
225
226
227
228
229
230
231
232
233 public DateComponents(final DateComponents epoch, final int offset) {
234 this(epoch.getJ2000Day() + offset);
235 }
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253 public static DateComponents createFromWeekComponents(final int wYear, final int week, final int dayOfWeek)
254 throws IllegalArgumentException {
255
256 final DateComponents firstWeekMonday = new DateComponents(getFirstWeekMonday(wYear));
257 final DateComponents d = new DateComponents(firstWeekMonday, 7 * week + dayOfWeek - 8);
258
259
260 if ((week != d.getCalendarWeek()) || (dayOfWeek != d.getDayOfWeek())) {
261 throw new OrekitIllegalArgumentException(OrekitMessages.NON_EXISTENT_WEEK_DATE,
262 wYear, week, dayOfWeek);
263 }
264
265 return d;
266
267 }
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293 public static DateComponents parseDate(final String string) {
294
295
296 final Matcher calendarMatcher = CALENDAR_FORMAT.matcher(string);
297 if (calendarMatcher.matches()) {
298 return new DateComponents(Integer.parseInt(calendarMatcher.group(1)),
299 Integer.parseInt(calendarMatcher.group(2)),
300 Integer.parseInt(calendarMatcher.group(3)));
301 }
302
303
304 final Matcher ordinalMatcher = ORDINAL_FORMAT.matcher(string);
305 if (ordinalMatcher.matches()) {
306 return new DateComponents(Integer.parseInt(ordinalMatcher.group(1)),
307 Integer.parseInt(ordinalMatcher.group(2)));
308 }
309
310
311 final Matcher weekMatcher = WEEK_FORMAT.matcher(string);
312 if (weekMatcher.matches()) {
313 return createFromWeekComponents(Integer.parseInt(weekMatcher.group(1)),
314 Integer.parseInt(weekMatcher.group(2)),
315 Integer.parseInt(weekMatcher.group(3)));
316 }
317
318 throw new OrekitIllegalArgumentException(OrekitMessages.NON_EXISTENT_DATE, string);
319
320 }
321
322
323
324
325 public int getYear() {
326 return year;
327 }
328
329
330
331
332 public int getMonth() {
333 return month;
334 }
335
336
337
338
339 public Month getMonthEnum() {
340 return Month.getMonth(month);
341 }
342
343
344
345
346 public int getDay() {
347 return day;
348 }
349
350
351
352
353 public int getJ2000Day() {
354 YearFactory yFactory = GREGORIAN_FACTORY;
355 if (year < 1583) {
356 if (year < 1) {
357 yFactory = PROLEPTIC_JULIAN_FACTORY;
358 } else if ((year < 1582) || (month < 10) || ((month < 11) && (day < 5))) {
359 yFactory = JULIAN_FACTORY;
360 }
361 }
362 final MonthDayFactory mdFactory =
363 yFactory.isLeap(year) ? LEAP_YEAR_FACTORY : COMMON_YEAR_FACTORY;
364 return yFactory.getLastJ2000DayOfYear(year - 1) +
365 mdFactory.getDayInYear(month, day);
366 }
367
368
369
370
371 public int getMJD() {
372 return MJD_TO_J2000 + getJ2000Day();
373 }
374
375
376
377
378
379
380
381
382
383
384
385
386 public int getCalendarWeek() {
387 final int firstWeekMonday = getFirstWeekMonday(year);
388 int daysSincefirstMonday = getJ2000Day() - firstWeekMonday;
389 if (daysSincefirstMonday < 0) {
390
391 daysSincefirstMonday += firstWeekMonday - getFirstWeekMonday(year - 1);
392 } else if (daysSincefirstMonday > 363) {
393
394
395 final int weekYearLength = getFirstWeekMonday(year + 1) - firstWeekMonday;
396 if (daysSincefirstMonday >= weekYearLength) {
397 daysSincefirstMonday -= weekYearLength;
398 }
399 }
400 return 1 + daysSincefirstMonday / 7;
401 }
402
403
404
405
406
407 private static int getFirstWeekMonday(final int year) {
408 final int yearFirst = new DateComponents(year, 1, 1).getJ2000Day();
409 final int offsetToMonday = 4 - (yearFirst + 2) % 7;
410 return yearFirst + offsetToMonday + ((offsetToMonday > 3) ? -7 : 0);
411 }
412
413
414
415
416
417 public int getDayOfWeek() {
418 final int dow = (getJ2000Day() + 6) % 7;
419 return (dow < 1) ? (dow + 7) : dow;
420 }
421
422
423
424
425
426
427 public int getDayOfYear() {
428 return getJ2000Day() - new DateComponents(year - 1, 12, 31).getJ2000Day();
429 }
430
431
432
433
434 public String toString() {
435 return new StringBuffer().
436 append(FOUR_DIGITS.format(year)).append('-').
437 append(TWO_DIGITS.format(month)).append('-').
438 append(TWO_DIGITS.format(day)).
439 toString();
440 }
441
442
443 public int compareTo(final DateComponents other) {
444 final int j2000Day = getJ2000Day();
445 final int otherJ2000Day = other.getJ2000Day();
446 if (j2000Day < otherJ2000Day) {
447 return -1;
448 } else if (j2000Day > otherJ2000Day) {
449 return 1;
450 }
451 return 0;
452 }
453
454
455 public boolean equals(final Object other) {
456 try {
457 final DateComponents otherDate = (DateComponents) other;
458 return (otherDate != null) && (year == otherDate.year) &&
459 (month == otherDate.month) && (day == otherDate.day);
460 } catch (ClassCastException cce) {
461 return false;
462 }
463 }
464
465
466 public int hashCode() {
467 return (year << 16) ^ (month << 8) ^ day;
468 }
469
470
471 private interface YearFactory {
472
473
474
475
476
477 int getYear(int j2000Day);
478
479
480
481
482
483 int getLastJ2000DayOfYear(int year);
484
485
486
487
488
489 boolean isLeap(int year);
490
491 }
492
493
494 private static class ProlepticJulianFactory implements YearFactory {
495
496
497 public int getYear(final int j2000Day) {
498 return (int) -((-4l * j2000Day - 2920488l) / 1461l);
499 }
500
501
502 public int getLastJ2000DayOfYear(final int year) {
503 return (1461 * year + 1) / 4 - 730123;
504 }
505
506
507 public boolean isLeap(final int year) {
508 return (year % 4) == 0;
509 }
510
511 }
512
513
514 private static class JulianFactory implements YearFactory {
515
516
517 public int getYear(final int j2000Day) {
518 return (int) ((4l * j2000Day + 2921948l) / 1461l);
519 }
520
521
522 public int getLastJ2000DayOfYear(final int year) {
523 return (1461 * year) / 4 - 730122;
524 }
525
526
527 public boolean isLeap(final int year) {
528 return (year % 4) == 0;
529 }
530
531 }
532
533
534 private static class GregorianFactory implements YearFactory {
535
536
537 public int getYear(final int j2000Day) {
538
539
540 int year = (int) ((400l * j2000Day + 292194288l) / 146097l);
541
542
543
544 if (j2000Day <= getLastJ2000DayOfYear(year - 1)) {
545 --year;
546 }
547
548
549 return year;
550
551 }
552
553
554 public int getLastJ2000DayOfYear(final int year) {
555 return (1461 * year) / 4 - year / 100 + year / 400 - 730120;
556 }
557
558
559 public boolean isLeap(final int year) {
560 return ((year % 4) == 0) && (((year % 400) == 0) || ((year % 100) != 0));
561 }
562
563 }
564
565
566 private interface MonthDayFactory {
567
568
569
570
571
572 int getMonth(int dayInYear);
573
574
575
576
577
578
579 int getDay(int dayInYear, int month);
580
581
582
583
584
585
586 int getDayInYear(int month, int day);
587
588 }
589
590
591 private static class LeapYearFactory implements MonthDayFactory {
592
593
594 private static final int[] PREVIOUS_MONTH_END_DAY = {
595 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335
596 };
597
598
599 public int getMonth(final int dayInYear) {
600 return (dayInYear < 32) ? 1 : (10 * dayInYear + 313) / 306;
601 }
602
603
604 public int getDay(final int dayInYear, final int month) {
605 return dayInYear - PREVIOUS_MONTH_END_DAY[month];
606 }
607
608
609 public int getDayInYear(final int month, final int day) {
610 return day + PREVIOUS_MONTH_END_DAY[month];
611 }
612
613 }
614
615
616 private static class CommonYearFactory implements MonthDayFactory {
617
618
619 private static final int[] PREVIOUS_MONTH_END_DAY = {
620 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
621 };
622
623
624 public int getMonth(final int dayInYear) {
625 return (dayInYear < 32) ? 1 : (10 * dayInYear + 323) / 306;
626 }
627
628
629 public int getDay(final int dayInYear, final int month) {
630 return dayInYear - PREVIOUS_MONTH_END_DAY[month];
631 }
632
633
634 public int getDayInYear(final int month, final int day) {
635 return day + PREVIOUS_MONTH_END_DAY[month];
636 }
637
638 }
639
640 }