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