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