1   /* Copyright 2002-2016 CS Systèmes d'Information
2    * Licensed to CS Systèmes d'Information (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      /** J2000.0 Reference epoch: 2000-01-01. */
69      public static final DateComponents J2000_EPOCH;
70  
71      /** Java Reference epoch: 1970-01-01. */
72      public static final DateComponents JAVA_EPOCH;
73  
74      /** Serializable UID. */
75      private static final long serialVersionUID = -2462694707837970938L;
76  
77      /** Factory for proleptic julian calendar (up to 0000-12-31). */
78      private static final YearFactory PROLEPTIC_JULIAN_FACTORY = new ProlepticJulianFactory();
79  
80      /** Factory for julian calendar (from 0001-01-01 to 1582-10-04). */
81      private static final YearFactory JULIAN_FACTORY           = new JulianFactory();
82  
83      /** Factory for gregorian calendar (from 1582-10-15). */
84      private static final YearFactory GREGORIAN_FACTORY        = new GregorianFactory();
85  
86      /** Factory for leap years. */
87      private static final MonthDayFactory LEAP_YEAR_FACTORY    = new LeapYearFactory();
88  
89      /** Factory for non-leap years. */
90      private static final MonthDayFactory COMMON_YEAR_FACTORY  = new CommonYearFactory();
91  
92      /** Format for years. */
93      private static final DecimalFormat FOUR_DIGITS = new DecimalFormat("0000");
94  
95      /** Format for months and days. */
96      private static final DecimalFormat TWO_DIGITS  = new DecimalFormat("00");
97  
98      /** Offset between J2000 epoch and modified julian day epoch. */
99      private static final int MJD_TO_J2000 = 51544;
100 
101     /** Basic and extended format calendar date. */
102     private static Pattern CALENDAR_FORMAT = Pattern.compile("^(-?\\d\\d\\d\\d)-?(\\d\\d)-?(\\d\\d)$");
103 
104     /** Basic and extended format ordinal date. */
105     private static Pattern ORDINAL_FORMAT = Pattern.compile("^(-?\\d\\d\\d\\d)-?(\\d\\d\\d)$");
106 
107     /** Basic and extended format week date. */
108     private static Pattern WEEK_FORMAT = Pattern.compile("^(-?\\d\\d\\d\\d)-?W(\\d\\d)-?(\\d)$");
109 
110     static {
111         // this static statement makes sure the reference epoch are initialized
112         // once AFTER the various factories have been set up
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     /** Year number. */
124     private final int year;
125 
126     /** Month number. */
127     private final int month;
128 
129     /** Day number. */
130     private final int day;
131 
132     /** Build a date from its components.
133      * @param year year number (may be 0 or negative for BC years)
134      * @param month month number from 1 to 12
135      * @param day day number from 1 to 31
136      * @exception IllegalArgumentException if inconsistent arguments
137      * are given (parameters out of range, february 29 for non-leap years,
138      * dates during the gregorian leap in 1582 ...)
139      */
140     public DateComponents(final int year, final int month, final int day)
141         throws IllegalArgumentException {
142 
143         // very rough range check
144         // (just to avoid ArrayOutOfboundException in MonthDayFactory later)
145         if ((month < 1) || (month > 12)) {
146             throw new OrekitIllegalArgumentException(OrekitMessages.NON_EXISTENT_MONTH, month);
147         }
148 
149         // start by trusting the parameters
150         this.year  = year;
151         this.month = month;
152         this.day   = day;
153 
154         // build a check date from the J2000 day
155         final DateComponents check = new DateComponents(getJ2000Day());
156 
157         // check the parameters for mismatch
158         // (i.e. invalid date components, like 29 february on non-leap years)
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     /** Build a date from its components.
167      * @param year year number (may be 0 or negative for BC years)
168      * @param month month enumerate
169      * @param day day number from 1 to 31
170      * @exception IllegalArgumentException if inconsistent arguments
171      * are given (parameters out of range, february 29 for non-leap years,
172      * dates during the gregorian leap in 1582 ...)
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     /** Build a date from a year and day number.
180      * @param year year number (may be 0 or negative for BC years)
181      * @param dayNumber day number in the year from 1 to 366
182      * @exception IllegalArgumentException if dayNumber is out of range
183      * with respect to year
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     /** Build a date from its offset with respect to a {@link #J2000_EPOCH}.
195      * @param offset offset with respect to a {@link #J2000_EPOCH}
196      * @see #getJ2000Day()
197      */
198     public DateComponents(final int offset) {
199 
200         // we follow the astronomical convention for calendars:
201         // we consider a year zero and 10 days are missing in 1582
202         // from 1582-10-15: gregorian calendar
203         // from 0001-01-01 to 1582-10-04: julian calendar
204         // up to 0000-12-31 : proleptic julian calendar
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         // handle month/day according to the year being a common or leap year
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     /** Build a date from its offset with respect to a reference epoch.
225      * <p>This constructor is mainly useful to build a date from a modified
226      * julian day (using {@link #MODIFIED_JULIAN_EPOCH}) or a GPS week number
227      * (using {@link #GPS_EPOCH}).</p>
228      * @param epoch reference epoch
229      * @param offset offset with respect to a reference epoch
230      * @see #DateComponents(int)
231      * @see #getMJD()
232      */
233     public DateComponents(final DateComponents epoch, final int offset) {
234         this(epoch.getJ2000Day() + offset);
235     }
236 
237     /** Build a date from week components.
238      * <p>The calendar week number is a number between 1 and 52 or 53 depending
239      * on the year. Week 1 is defined by ISO as the one that includes the first
240      * Thursday of a year. Week 1 may therefore start the previous year and week
241      * 52 or 53 may end in the next year. As an example calendar date 1995-01-01
242      * corresponds to week date 1994-W52-7 (i.e. Sunday in the last week of 1994
243      * is in fact the first day of year 1995). This date would beAnother example is calendar date
244      * 1996-12-31 which corresponds to week date 1997-W01-2 (i.e. Tuesday in the
245      * first week of 1997 is in fact the last day of year 1996).</p>
246      * @param wYear year associated to week numbering
247      * @param week week number in year,from 1 to 52 or 53
248      * @param dayOfWeek day of week, from 1 (Monday) to 7 (Sunday)
249      * @return a builded date
250      * @exception IllegalArgumentException if inconsistent arguments
251      * are given (parameters out of range, week 53 on a 52 weeks year ...)
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         // check the parameters for invalid date components
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     /** Parse a string in ISO-8601 format to build a date.
270      * <p>The supported formats are:
271      * <ul>
272      *   <li>basic format calendar date: YYYYMMDD</li>
273      *   <li>extended format calendar date: YYYY-MM-DD</li>
274      *   <li>basic format ordinal date: YYYYDDD</li>
275      *   <li>extended format ordinal date: YYYY-DDD</li>
276      *   <li>basic format week date: YYYYWwwD</li>
277      *   <li>extended format week date: YYYY-Www-D</li>
278      * </ul>
279      * As shown by the list above, only the complete representations defined in section 4.1
280      * of ISO-8601 standard are supported, neither expended representations nor representations
281      * with reduced accuracy are supported.
282      * </p>
283      * <p>
284      * Parsing a single integer as a julian day is <em>not</em> supported as it may be ambiguous
285      * with either the basic format calendar date or the basic format ordinal date depending
286      * on the number of digits.
287      * </p>
288      * @param string string to parse
289      * @return a parsed date
290      * @exception IllegalArgumentException if string cannot be parsed
291      */
292     public static  DateComponents parseDate(final String string) {
293 
294         // is the date a calendar date ?
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         // is the date an ordinal date ?
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         // is the date a week date ?
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     /** Get the year number.
322      * @return year number (may be 0 or negative for BC years)
323      */
324     public int getYear() {
325         return year;
326     }
327 
328     /** Get the month.
329      * @return month number from 1 to 12
330      */
331     public int getMonth() {
332         return month;
333     }
334 
335     /** Get the month as an enumerate.
336      * @return month as an enumerate
337      */
338     public Month getMonthEnum() {
339         return Month.getMonth(month);
340     }
341 
342     /** Get the day.
343      * @return day number from 1 to 31
344      */
345     public int getDay() {
346         return day;
347     }
348 
349     /** Get the day number with respect to J2000 epoch.
350      * @return day number with respect to J2000 epoch
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     /** Get the modified julian day.
368      * @return modified julian day
369      */
370     public int getMJD() {
371         return MJD_TO_J2000 + getJ2000Day();
372     }
373 
374     /** Get the calendar week number.
375      * <p>The calendar week number is a number between 1 and 52 or 53 depending
376      * on the year. Week 1 is defined by ISO as the one that includes the first
377      * Thursday of a year. Week 1 may therefore start the previous year and week
378      * 52 or 53 may end in the next year. As an example calendar date 1995-01-01
379      * corresponds to week date 1994-W52-7 (i.e. Sunday in the last week of 1994
380      * is in fact the first day of year 1995). Another example is calendar date
381      * 1996-12-31 which corresponds to week date 1997-W01-2 (i.e. Tuesday in the
382      * first week of 1997 is in fact the last day of year 1996).</p>
383      * @return calendar week number
384      */
385     public int getCalendarWeek() {
386         final int firstWeekMonday = getFirstWeekMonday(year);
387         int daysSincefirstMonday = getJ2000Day() - firstWeekMonday;
388         if (daysSincefirstMonday < 0) {
389             // we are still in a week from previous year
390             daysSincefirstMonday += firstWeekMonday - getFirstWeekMonday(year - 1);
391         } else if (daysSincefirstMonday > 363) {
392             // up to three days at end of year may belong to first week of next year
393             // (by chance, there is no need for a specific check in year 1582 ...)
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     /** Get the monday of a year first week.
403      * @param year year to consider
404      * @return day of the monday of the first weak of year
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     /** Get the day of week.
413      * <p>Day of week is a number between 1 (Monday) and 7 (Sunday).</p>
414      * @return day of week
415      */
416     public int getDayOfWeek() {
417         final int dow = (getJ2000Day() + 6) % 7; // result is between -6 and +6
418         return (dow < 1) ? (dow + 7) : dow;
419     }
420 
421     /** Get the day number in year.
422      * <p>Day number in year is between 1 (January 1st) and either 365 or
423      * 366 inclusive depending on year.</p>
424      * @return day number in year
425      */
426     public int getDayOfYear() {
427         return getJ2000Day() - new DateComponents(year - 1, 12, 31).getJ2000Day();
428     }
429 
430     /** Get a string representation (ISO-8601) of the date.
431      * @return string representation of the date.
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     /** {@inheritDoc} */
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     /** {@inheritDoc} */
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     /** {@inheritDoc} */
465     public int hashCode() {
466         return (year << 16) ^ (month << 8) ^ day;
467     }
468 
469     /** Interface for dealing with years sequences according to some calendar. */
470     private interface YearFactory {
471 
472         /** Get the year number for a given day number with respect to J2000 epoch.
473          * @param j2000Day day number with respect to J2000 epoch
474          * @return year number
475          */
476         int getYear(int j2000Day);
477 
478         /** Get the day number with respect to J2000 epoch for new year's Eve.
479          * @param year year number
480          * @return day number with respect to J2000 epoch for new year's Eve
481          */
482         int getLastJ2000DayOfYear(int year);
483 
484         /** Check if a year is a leap or common year.
485          * @param year year number
486          * @return true if year is a leap year
487          */
488         boolean isLeap(int year);
489 
490     }
491 
492     /** Class providing a years sequence compliant with the proleptic julian calendar. */
493     private static class ProlepticJulianFactory implements YearFactory {
494 
495         /** {@inheritDoc} */
496         public int getYear(final int j2000Day) {
497             return  (int) -((-4l * j2000Day - 2920488l) / 1461l);
498         }
499 
500         /** {@inheritDoc} */
501         public int getLastJ2000DayOfYear(final int year) {
502             return (1461 * year + 1) / 4 - 730123;
503         }
504 
505         /** {@inheritDoc} */
506         public boolean isLeap(final int year) {
507             return (year % 4) == 0;
508         }
509 
510     }
511 
512     /** Class providing a years sequence compliant with the julian calendar. */
513     private static class JulianFactory implements YearFactory {
514 
515         /** {@inheritDoc} */
516         public int getYear(final int j2000Day) {
517             return  (int) ((4l * j2000Day + 2921948l) / 1461l);
518         }
519 
520         /** {@inheritDoc} */
521         public int getLastJ2000DayOfYear(final int year) {
522             return (1461 * year) / 4 - 730122;
523         }
524 
525         /** {@inheritDoc} */
526         public boolean isLeap(final int year) {
527             return (year % 4) == 0;
528         }
529 
530     }
531 
532     /** Class providing a years sequence compliant with the gregorian calendar. */
533     private static class GregorianFactory implements YearFactory {
534 
535         /** {@inheritDoc} */
536         public int getYear(final int j2000Day) {
537 
538             // year estimate
539             int year = (int) ((400l * j2000Day + 292194288l) / 146097l);
540 
541             // the previous estimate is one unit too high in some rare cases
542             // (240 days in the 400 years gregorian cycle, about 0.16%)
543             if (j2000Day <= getLastJ2000DayOfYear(year - 1)) {
544                 --year;
545             }
546 
547             // exact year
548             return year;
549 
550         }
551 
552         /** {@inheritDoc} */
553         public int getLastJ2000DayOfYear(final int year) {
554             return (1461 * year) / 4 - year / 100 + year / 400 - 730120;
555         }
556 
557         /** {@inheritDoc} */
558         public boolean isLeap(final int year) {
559             return ((year % 4) == 0) && (((year % 400) == 0) || ((year % 100) != 0));
560         }
561 
562     }
563 
564     /** Interface for dealing with months sequences according to leap/common years. */
565     private interface MonthDayFactory {
566 
567         /** Get the month number for a given day number within year.
568          * @param dayInYear day number within year
569          * @return month number
570          */
571         int getMonth(int dayInYear);
572 
573         /** Get the day number for given month and day number within year.
574          * @param dayInYear day number within year
575          * @param month month number
576          * @return day number
577          */
578         int getDay(int dayInYear, int month);
579 
580         /** Get the day number within year for given month and day numbers.
581          * @param month month number
582          * @param day day number
583          * @return day number within year
584          */
585         int getDayInYear(int month, int day);
586 
587     }
588 
589     /** Class providing the months sequence for leap years. */
590     private static class LeapYearFactory implements MonthDayFactory {
591 
592         /** Months succession definition. */
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         /** {@inheritDoc} */
598         public int getMonth(final int dayInYear) {
599             return (dayInYear < 32) ? 1 : (10 * dayInYear + 313) / 306;
600         }
601 
602         /** {@inheritDoc} */
603         public int getDay(final int dayInYear, final int month) {
604             return dayInYear - PREVIOUS_MONTH_END_DAY[month];
605         }
606 
607         /** {@inheritDoc} */
608         public int getDayInYear(final int month, final int day) {
609             return day + PREVIOUS_MONTH_END_DAY[month];
610         }
611 
612     }
613 
614     /** Class providing the months sequence for common years. */
615     private static class CommonYearFactory implements MonthDayFactory {
616 
617         /** Months succession definition. */
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         /** {@inheritDoc} */
623         public int getMonth(final int dayInYear) {
624             return (dayInYear < 32) ? 1 : (10 * dayInYear + 323) / 306;
625         }
626 
627         /** {@inheritDoc} */
628         public int getDay(final int dayInYear, final int month) {
629             return dayInYear - PREVIOUS_MONTH_END_DAY[month];
630         }
631 
632         /** {@inheritDoc} */
633         public int getDayInYear(final int month, final int day) {
634             return day + PREVIOUS_MONTH_END_DAY[month];
635         }
636 
637     }
638 
639 }