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