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