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