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 }