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