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