1 /* Copyright 2002-2020 CS GROUP
2 * Licensed to CS GROUP (CS) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * CS licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
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 /** Class representing a date broken up as year, month and day components.
28 * <p>This class uses the astronomical convention for calendars,
29 * which is also the convention used by <code>java.util.Date</code>:
30 * a year zero is present between years -1 and +1, and 10 days are
31 * missing in 1582. The calendar used around these special dates are:</p>
32 * <ul>
33 * <li>up to 0000-12-31 : proleptic julian calendar</li>
34 * <li>from 0001-01-01 to 1582-10-04: julian calendar</li>
35 * <li>from 1582-10-15: gregorian calendar</li>
36 * </ul>
37 * <p>Instances of this class are guaranteed to be immutable.</p>
38 * @see TimeComponents
39 * @see DateTimeComponents
40 * @author Luc Maisonobe
41 */
42 public class DateComponents implements Serializable, Comparable<DateComponents> {
43
44 /** Reference epoch for julian dates: -4712-01-01.
45 * <p>Both <code>java.util.Date</code> and {@link DateComponents} classes
46 * follow the astronomical conventions and consider a year 0 between
47 * years -1 and +1, hence this reference date lies in year -4712 and not
48 * in year -4713 as can be seen in other documents or programs that obey
49 * a different convention (for example the <code>convcal</code> utility).</p>
50 */
51 public static final DateComponents JULIAN_EPOCH;
52
53 /** Reference epoch for modified julian dates: 1858-11-17. */
54 public static final DateComponents MODIFIED_JULIAN_EPOCH;
55
56 /** Reference epoch for 1950 dates: 1950-01-01. */
57 public static final DateComponents FIFTIES_EPOCH;
58
59 /** Reference epoch for CCSDS Time Code Format (CCSDS 301.0-B-4): 1958-01-01. */
60 public static final DateComponents CCSDS_EPOCH;
61
62 /** Reference epoch for Galileo System Time: 1999-08-22. */
63 public static final DateComponents GALILEO_EPOCH;
64
65 /** Reference epoch for GPS weeks: 1980-01-06. */
66 public static final DateComponents GPS_EPOCH;
67
68 /** Reference epoch for QZSS weeks: 1980-01-06. */
69 public static final DateComponents QZSS_EPOCH;
70
71 /** Reference epoch for IRNSS weeks: 1999-08-22. */
72 public static final DateComponents IRNSS_EPOCH;
73
74 /** Reference epoch for BeiDou weeks: 2006-01-01. */
75 public static final DateComponents BEIDOU_EPOCH;
76
77 /** Reference epoch for GLONASS four-year interval number: 1996-01-01. */
78 public static final DateComponents GLONASS_EPOCH;
79
80 /** J2000.0 Reference epoch: 2000-01-01. */
81 public static final DateComponents J2000_EPOCH;
82
83 /** Java Reference epoch: 1970-01-01. */
84 public static final DateComponents JAVA_EPOCH;
85
86 /** Maximum supported date.
87 * <p>
88 * This is date 5881610-07-11 which corresponds to {@code Integer.MAX_VALUE}
89 * days after {@link #J2000_EPOCH}.
90 * </p>
91 * @since 9.0
92 */
93 public static final DateComponents MAX_EPOCH;
94
95 /** Maximum supported date.
96 * <p>
97 * This is date -5877490-03-03, which corresponds to {@code Integer.MIN_VALUE}
98 * days before {@link #J2000_EPOCH}.
99 * </p>
100 * @since 9.0
101 */
102 public static final DateComponents MIN_EPOCH;
103
104 /** Serializable UID. */
105 private static final long serialVersionUID = -2462694707837970938L;
106
107 /** Factory for proleptic julian calendar (up to 0000-12-31). */
108 private static final YearFactory PROLEPTIC_JULIAN_FACTORY = new ProlepticJulianFactory();
109
110 /** Factory for julian calendar (from 0001-01-01 to 1582-10-04). */
111 private static final YearFactory JULIAN_FACTORY = new JulianFactory();
112
113 /** Factory for gregorian calendar (from 1582-10-15). */
114 private static final YearFactory GREGORIAN_FACTORY = new GregorianFactory();
115
116 /** Factory for leap years. */
117 private static final MonthDayFactory LEAP_YEAR_FACTORY = new LeapYearFactory();
118
119 /** Factory for non-leap years. */
120 private static final MonthDayFactory COMMON_YEAR_FACTORY = new CommonYearFactory();
121
122 /** Format for years. */
123 private static final DecimalFormat FOUR_DIGITS = new DecimalFormat("0000");
124
125 /** Format for months and days. */
126 private static final DecimalFormat TWO_DIGITS = new DecimalFormat("00");
127
128 /** Offset between J2000 epoch and modified julian day epoch. */
129 private static final int MJD_TO_J2000 = 51544;
130
131 /** Basic and extended format calendar date. */
132 private static final Pattern CALENDAR_FORMAT = Pattern.compile("^(-?\\d\\d\\d\\d)-?(\\d\\d)-?(\\d\\d)$");
133
134 /** Basic and extended format ordinal date. */
135 private static final Pattern ORDINAL_FORMAT = Pattern.compile("^(-?\\d\\d\\d\\d)-?(\\d\\d\\d)$");
136
137 /** Basic and extended format week date. */
138 private static final Pattern WEEK_FORMAT = Pattern.compile("^(-?\\d\\d\\d\\d)-?W(\\d\\d)-?(\\d)$");
139
140 static {
141 // this static statement makes sure the reference epoch are initialized
142 // once AFTER the various factories have been set up
143 JULIAN_EPOCH = new DateComponents(-4712, 1, 1);
144 MODIFIED_JULIAN_EPOCH = new DateComponents(1858, 11, 17);
145 FIFTIES_EPOCH = new DateComponents(1950, 1, 1);
146 CCSDS_EPOCH = new DateComponents(1958, 1, 1);
147 GALILEO_EPOCH = new DateComponents(1999, 8, 22);
148 GPS_EPOCH = new DateComponents(1980, 1, 6);
149 QZSS_EPOCH = new DateComponents(1980, 1, 6);
150 IRNSS_EPOCH = new DateComponents(1999, 8, 22);
151 BEIDOU_EPOCH = new DateComponents(2006, 1, 1);
152 GLONASS_EPOCH = new DateComponents(1996, 1, 1);
153 J2000_EPOCH = new DateComponents(2000, 1, 1);
154 JAVA_EPOCH = new DateComponents(1970, 1, 1);
155 MAX_EPOCH = new DateComponents(Integer.MAX_VALUE);
156 MIN_EPOCH = new DateComponents(Integer.MIN_VALUE);
157 }
158
159 /** Year number. */
160 private final int year;
161
162 /** Month number. */
163 private final int month;
164
165 /** Day number. */
166 private final int day;
167
168 /** Build a date from its components.
169 * @param year year number (may be 0 or negative for BC years)
170 * @param month month number from 1 to 12
171 * @param day day number from 1 to 31
172 * @exception IllegalArgumentException if inconsistent arguments
173 * are given (parameters out of range, february 29 for non-leap years,
174 * dates during the gregorian leap in 1582 ...)
175 */
176 public DateComponents(final int year, final int month, final int day)
177 throws IllegalArgumentException {
178
179 // very rough range check
180 // (just to avoid ArrayOutOfboundException in MonthDayFactory later)
181 if ((month < 1) || (month > 12)) {
182 throw new OrekitIllegalArgumentException(OrekitMessages.NON_EXISTENT_MONTH, month);
183 }
184
185 // start by trusting the parameters
186 this.year = year;
187 this.month = month;
188 this.day = day;
189
190 // build a check date from the J2000 day
191 final DateComponentsteComponents">DateComponents check = new DateComponents(getJ2000Day());
192
193 // check the parameters for mismatch
194 // (i.e. invalid date components, like 29 february on non-leap years)
195 if ((year != check.year) || (month != check.month) || (day != check.day)) {
196 throw new OrekitIllegalArgumentException(OrekitMessages.NON_EXISTENT_YEAR_MONTH_DAY,
197 year, month, day);
198 }
199
200 }
201
202 /** Build a date from its components.
203 * @param year year number (may be 0 or negative for BC years)
204 * @param month month enumerate
205 * @param day day number from 1 to 31
206 * @exception IllegalArgumentException if inconsistent arguments
207 * are given (parameters out of range, february 29 for non-leap years,
208 * dates during the gregorian leap in 1582 ...)
209 */
210 public DateComponents(final int year, final Month month, final int day)
211 throws IllegalArgumentException {
212 this(year, month.getNumber(), day);
213 }
214
215 /** Build a date from a year and day number.
216 * @param year year number (may be 0 or negative for BC years)
217 * @param dayNumber day number in the year from 1 to 366
218 * @exception IllegalArgumentException if dayNumber is out of range
219 * with respect to year
220 */
221 public DateComponents(final int year, final int dayNumber)
222 throws IllegalArgumentException {
223 this(J2000_EPOCH, new DateComponents(year - 1, 12, 31).getJ2000Day() + dayNumber);
224 if (dayNumber != getDayOfYear()) {
225 throw new OrekitIllegalArgumentException(OrekitMessages.NON_EXISTENT_DAY_NUMBER_IN_YEAR,
226 dayNumber, year);
227 }
228 }
229
230 /** Build a date from its offset with respect to a {@link #J2000_EPOCH}.
231 * @param offset offset with respect to a {@link #J2000_EPOCH}
232 * @see #getJ2000Day()
233 */
234 public DateComponents(final int offset) {
235
236 // we follow the astronomical convention for calendars:
237 // we consider a year zero and 10 days are missing in 1582
238 // from 1582-10-15: gregorian calendar
239 // from 0001-01-01 to 1582-10-04: julian calendar
240 // up to 0000-12-31 : proleptic julian calendar
241 YearFactory yFactory = GREGORIAN_FACTORY;
242 if (offset < -152384) {
243 if (offset > -730122) {
244 yFactory = JULIAN_FACTORY;
245 } else {
246 yFactory = PROLEPTIC_JULIAN_FACTORY;
247 }
248 }
249 year = yFactory.getYear(offset);
250 final int dayInYear = offset - yFactory.getLastJ2000DayOfYear(year - 1);
251
252 // handle month/day according to the year being a common or leap year
253 final MonthDayFactory mdFactory =
254 yFactory.isLeap(year) ? LEAP_YEAR_FACTORY : COMMON_YEAR_FACTORY;
255 month = mdFactory.getMonth(dayInYear);
256 day = mdFactory.getDay(dayInYear, month);
257
258 }
259
260 /** Build a date from its offset with respect to a reference epoch.
261 * <p>This constructor is mainly useful to build a date from a modified
262 * julian day (using {@link #MODIFIED_JULIAN_EPOCH}) or a GPS week number
263 * (using {@link #GPS_EPOCH}).</p>
264 * @param epoch reference epoch
265 * @param offset offset with respect to a reference epoch
266 * @see #DateComponents(int)
267 * @see #getMJD()
268 */
269 public DateComponentstml#DateComponents">DateComponents(final DateComponents epoch, final int offset) {
270 this(epoch.getJ2000Day() + offset);
271 }
272
273 /** Build a date from week components.
274 * <p>The calendar week number is a number between 1 and 52 or 53 depending
275 * on the year. Week 1 is defined by ISO as the one that includes the first
276 * Thursday of a year. Week 1 may therefore start the previous year and week
277 * 52 or 53 may end in the next year. As an example calendar date 1995-01-01
278 * corresponds to week date 1994-W52-7 (i.e. Sunday in the last week of 1994
279 * is in fact the first day of year 1995). This date would beAnother example is calendar date
280 * 1996-12-31 which corresponds to week date 1997-W01-2 (i.e. Tuesday in the
281 * first week of 1997 is in fact the last day of year 1996).</p>
282 * @param wYear year associated to week numbering
283 * @param week week number in year, from 1 to 52 or 53
284 * @param dayOfWeek day of week, from 1 (Monday) to 7 (Sunday)
285 * @return a builded date
286 * @exception IllegalArgumentException if inconsistent arguments
287 * are given (parameters out of range, week 53 on a 52 weeks year ...)
288 */
289 public static DateComponents createFromWeekComponents(final int wYear, final int week, final int dayOfWeek)
290 throws IllegalArgumentException {
291
292 final DateComponentsts">DateComponents firstWeekMonday = new DateComponents(getFirstWeekMonday(wYear));
293 final DateComponentsl#DateComponents">DateComponents d = new DateComponents(firstWeekMonday, 7 * week + dayOfWeek - 8);
294
295 // check the parameters for invalid date components
296 if ((week != d.getCalendarWeek()) || (dayOfWeek != d.getDayOfWeek())) {
297 throw new OrekitIllegalArgumentException(OrekitMessages.NON_EXISTENT_WEEK_DATE,
298 wYear, week, dayOfWeek);
299 }
300
301 return d;
302
303 }
304
305 /** Parse a string in ISO-8601 format to build a date.
306 * <p>The supported formats are:
307 * <ul>
308 * <li>basic format calendar date: YYYYMMDD</li>
309 * <li>extended format calendar date: YYYY-MM-DD</li>
310 * <li>basic format ordinal date: YYYYDDD</li>
311 * <li>extended format ordinal date: YYYY-DDD</li>
312 * <li>basic format week date: YYYYWwwD</li>
313 * <li>extended format week date: YYYY-Www-D</li>
314 * </ul>
315 *
316 * <p> As shown by the list above, only the complete representations defined in section 4.1
317 * of ISO-8601 standard are supported, neither expended representations nor representations
318 * with reduced accuracy are supported.
319 *
320 * <p>
321 * Parsing a single integer as a julian day is <em>not</em> supported as it may be ambiguous
322 * with either the basic format calendar date or the basic format ordinal date depending
323 * on the number of digits.
324 * </p>
325 * @param string string to parse
326 * @return a parsed date
327 * @exception IllegalArgumentException if string cannot be parsed
328 */
329 public static DateComponents parseDate(final String string) {
330
331 // is the date a calendar date ?
332 final Matcher calendarMatcher = CALENDAR_FORMAT.matcher(string);
333 if (calendarMatcher.matches()) {
334 return new DateComponents(Integer.parseInt(calendarMatcher.group(1)),
335 Integer.parseInt(calendarMatcher.group(2)),
336 Integer.parseInt(calendarMatcher.group(3)));
337 }
338
339 // is the date an ordinal date ?
340 final Matcher ordinalMatcher = ORDINAL_FORMAT.matcher(string);
341 if (ordinalMatcher.matches()) {
342 return new DateComponents(Integer.parseInt(ordinalMatcher.group(1)),
343 Integer.parseInt(ordinalMatcher.group(2)));
344 }
345
346 // is the date a week date ?
347 final Matcher weekMatcher = WEEK_FORMAT.matcher(string);
348 if (weekMatcher.matches()) {
349 return createFromWeekComponents(Integer.parseInt(weekMatcher.group(1)),
350 Integer.parseInt(weekMatcher.group(2)),
351 Integer.parseInt(weekMatcher.group(3)));
352 }
353
354 throw new OrekitIllegalArgumentException(OrekitMessages.NON_EXISTENT_DATE, string);
355
356 }
357
358 /** Get the year number.
359 * @return year number (may be 0 or negative for BC years)
360 */
361 public int getYear() {
362 return year;
363 }
364
365 /** Get the month.
366 * @return month number from 1 to 12
367 */
368 public int getMonth() {
369 return month;
370 }
371
372 /** Get the month as an enumerate.
373 * @return month as an enumerate
374 */
375 public Month getMonthEnum() {
376 return Month.getMonth(month);
377 }
378
379 /** Get the day.
380 * @return day number from 1 to 31
381 */
382 public int getDay() {
383 return day;
384 }
385
386 /** Get the day number with respect to J2000 epoch.
387 * @return day number with respect to J2000 epoch
388 */
389 public int getJ2000Day() {
390 YearFactory yFactory = GREGORIAN_FACTORY;
391 if (year < 1583) {
392 if (year < 1) {
393 yFactory = PROLEPTIC_JULIAN_FACTORY;
394 } else if ((year < 1582) || (month < 10) || ((month < 11) && (day < 5))) {
395 yFactory = JULIAN_FACTORY;
396 }
397 }
398 final MonthDayFactory mdFactory =
399 yFactory.isLeap(year) ? LEAP_YEAR_FACTORY : COMMON_YEAR_FACTORY;
400 return yFactory.getLastJ2000DayOfYear(year - 1) +
401 mdFactory.getDayInYear(month, day);
402 }
403
404 /** Get the modified julian day.
405 * @return modified julian day
406 */
407 public int getMJD() {
408 return MJD_TO_J2000 + getJ2000Day();
409 }
410
411 /** Get the calendar week number.
412 * <p>The calendar week number is a number between 1 and 52 or 53 depending
413 * on the year. Week 1 is defined by ISO as the one that includes the first
414 * Thursday of a year. Week 1 may therefore start the previous year and week
415 * 52 or 53 may end in the next year. As an example calendar date 1995-01-01
416 * corresponds to week date 1994-W52-7 (i.e. Sunday in the last week of 1994
417 * is in fact the first day of year 1995). Another example is calendar date
418 * 1996-12-31 which corresponds to week date 1997-W01-2 (i.e. Tuesday in the
419 * first week of 1997 is in fact the last day of year 1996).</p>
420 * @return calendar week number
421 */
422 public int getCalendarWeek() {
423 final int firstWeekMonday = getFirstWeekMonday(year);
424 int daysSincefirstMonday = getJ2000Day() - firstWeekMonday;
425 if (daysSincefirstMonday < 0) {
426 // we are still in a week from previous year
427 daysSincefirstMonday += firstWeekMonday - getFirstWeekMonday(year - 1);
428 } else if (daysSincefirstMonday > 363) {
429 // up to three days at end of year may belong to first week of next year
430 // (by chance, there is no need for a specific check in year 1582 ...)
431 final int weekYearLength = getFirstWeekMonday(year + 1) - firstWeekMonday;
432 if (daysSincefirstMonday >= weekYearLength) {
433 daysSincefirstMonday -= weekYearLength;
434 }
435 }
436 return 1 + daysSincefirstMonday / 7;
437 }
438
439 /** Get the monday of a year first week.
440 * @param year year to consider
441 * @return day of the monday of the first weak of year
442 */
443 private static int getFirstWeekMonday(final int year) {
444 final int yearFirst = new DateComponents(year, 1, 1).getJ2000Day();
445 final int offsetToMonday = 4 - (yearFirst + 2) % 7;
446 return yearFirst + offsetToMonday + ((offsetToMonday > 3) ? -7 : 0);
447 }
448
449 /** Get the day of week.
450 * <p>Day of week is a number between 1 (Monday) and 7 (Sunday).</p>
451 * @return day of week
452 */
453 public int getDayOfWeek() {
454 final int dow = (getJ2000Day() + 6) % 7; // result is between -6 and +6
455 return (dow < 1) ? (dow + 7) : dow;
456 }
457
458 /** Get the day number in year.
459 * <p>Day number in year is between 1 (January 1st) and either 365 or
460 * 366 inclusive depending on year.</p>
461 * @return day number in year
462 */
463 public int getDayOfYear() {
464 return getJ2000Day() - new DateComponents(year - 1, 12, 31).getJ2000Day();
465 }
466
467 /** Get a string representation (ISO-8601) of the date.
468 * @return string representation of the date.
469 */
470 public String toString() {
471 return new StringBuffer().
472 append(FOUR_DIGITS.format(year)).append('-').
473 append(TWO_DIGITS.format(month)).append('-').
474 append(TWO_DIGITS.format(day)).
475 toString();
476 }
477
478 /** {@inheritDoc} */
479 public int compareTo(final DateComponents other) {
480 final int j2000Day = getJ2000Day();
481 final int otherJ2000Day = other.getJ2000Day();
482 if (j2000Day < otherJ2000Day) {
483 return -1;
484 } else if (j2000Day > otherJ2000Day) {
485 return 1;
486 }
487 return 0;
488 }
489
490 /** {@inheritDoc} */
491 public boolean equals(final Object other) {
492 try {
493 final DateComponentsg/orekit/time/DateComponents.html#DateComponents">DateComponents otherDate = (DateComponents) other;
494 return (otherDate != null) && (year == otherDate.year) &&
495 (month == otherDate.month) && (day == otherDate.day);
496 } catch (ClassCastException cce) {
497 return false;
498 }
499 }
500
501 /** {@inheritDoc} */
502 public int hashCode() {
503 return (year << 16) ^ (month << 8) ^ day;
504 }
505
506 /** Interface for dealing with years sequences according to some calendar. */
507 private interface YearFactory {
508
509 /** Get the year number for a given day number with respect to J2000 epoch.
510 * @param j2000Day day number with respect to J2000 epoch
511 * @return year number
512 */
513 int getYear(int j2000Day);
514
515 /** Get the day number with respect to J2000 epoch for new year's Eve.
516 * @param year year number
517 * @return day number with respect to J2000 epoch for new year's Eve
518 */
519 int getLastJ2000DayOfYear(int year);
520
521 /** Check if a year is a leap or common year.
522 * @param year year number
523 * @return true if year is a leap year
524 */
525 boolean isLeap(int year);
526
527 }
528
529 /** Class providing a years sequence compliant with the proleptic Julian calendar. */
530 private static class ProlepticJulianFactory implements YearFactory {
531
532 /** {@inheritDoc} */
533 public int getYear(final int j2000Day) {
534 return (int) -((-4l * j2000Day - 2920488l) / 1461l);
535 }
536
537 /** {@inheritDoc} */
538 public int getLastJ2000DayOfYear(final int year) {
539 return 365 * year + (year + 1) / 4 - 730123;
540 }
541
542 /** {@inheritDoc} */
543 public boolean isLeap(final int year) {
544 return (year % 4) == 0;
545 }
546
547 }
548
549 /** Class providing a years sequence compliant with the Julian calendar. */
550 private static class JulianFactory implements YearFactory {
551
552 /** {@inheritDoc} */
553 public int getYear(final int j2000Day) {
554 return (int) ((4l * j2000Day + 2921948l) / 1461l);
555 }
556
557 /** {@inheritDoc} */
558 public int getLastJ2000DayOfYear(final int year) {
559 return 365 * year + year / 4 - 730122;
560 }
561
562 /** {@inheritDoc} */
563 public boolean isLeap(final int year) {
564 return (year % 4) == 0;
565 }
566
567 }
568
569 /** Class providing a years sequence compliant with the Gregorian calendar. */
570 private static class GregorianFactory implements YearFactory {
571
572 /** {@inheritDoc} */
573 public int getYear(final int j2000Day) {
574
575 // year estimate
576 int year = (int) ((400l * j2000Day + 292194288l) / 146097l);
577
578 // the previous estimate is one unit too high in some rare cases
579 // (240 days in the 400 years gregorian cycle, about 0.16%)
580 if (j2000Day <= getLastJ2000DayOfYear(year - 1)) {
581 --year;
582 }
583
584 // exact year
585 return year;
586
587 }
588
589 /** {@inheritDoc} */
590 public int getLastJ2000DayOfYear(final int year) {
591 return 365 * year + year / 4 - year / 100 + year / 400 - 730120;
592 }
593
594 /** {@inheritDoc} */
595 public boolean isLeap(final int year) {
596 return ((year % 4) == 0) && (((year % 400) == 0) || ((year % 100) != 0));
597 }
598
599 }
600
601 /** Interface for dealing with months sequences according to leap/common years. */
602 private interface MonthDayFactory {
603
604 /** Get the month number for a given day number within year.
605 * @param dayInYear day number within year
606 * @return month number
607 */
608 int getMonth(int dayInYear);
609
610 /** Get the day number for given month and day number within year.
611 * @param dayInYear day number within year
612 * @param month month number
613 * @return day number
614 */
615 int getDay(int dayInYear, int month);
616
617 /** Get the day number within year for given month and day numbers.
618 * @param month month number
619 * @param day day number
620 * @return day number within year
621 */
622 int getDayInYear(int month, int day);
623
624 }
625
626 /** Class providing the months sequence for leap years. */
627 private static class LeapYearFactory implements MonthDayFactory {
628
629 /** Months succession definition. */
630 private static final int[] PREVIOUS_MONTH_END_DAY = {
631 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335
632 };
633
634 /** {@inheritDoc} */
635 public int getMonth(final int dayInYear) {
636 return (dayInYear < 32) ? 1 : (10 * dayInYear + 313) / 306;
637 }
638
639 /** {@inheritDoc} */
640 public int getDay(final int dayInYear, final int month) {
641 return dayInYear - PREVIOUS_MONTH_END_DAY[month];
642 }
643
644 /** {@inheritDoc} */
645 public int getDayInYear(final int month, final int day) {
646 return day + PREVIOUS_MONTH_END_DAY[month];
647 }
648
649 }
650
651 /** Class providing the months sequence for common years. */
652 private static class CommonYearFactory implements MonthDayFactory {
653
654 /** Months succession definition. */
655 private static final int[] PREVIOUS_MONTH_END_DAY = {
656 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
657 };
658
659 /** {@inheritDoc} */
660 public int getMonth(final int dayInYear) {
661 return (dayInYear < 32) ? 1 : (10 * dayInYear + 323) / 306;
662 }
663
664 /** {@inheritDoc} */
665 public int getDay(final int dayInYear, final int month) {
666 return dayInYear - PREVIOUS_MONTH_END_DAY[month];
667 }
668
669 /** {@inheritDoc} */
670 public int getDayInYear(final int month, final int day) {
671 return day + PREVIOUS_MONTH_END_DAY[month];
672 }
673
674 }
675
676 }