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