001    /*
002     *  Copyright 2001-2005 Stephen Colebourne
003     *
004     *  Licensed under the Apache License, Version 2.0 (the "License");
005     *  you may not use this file except in compliance with the License.
006     *  You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     *  Unless required by applicable law or agreed to in writing, software
011     *  distributed under the License is distributed on an "AS IS" BASIS,
012     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     *  See the License for the specific language governing permissions and
014     *  limitations under the License.
015     */
016    package org.joda.time.chrono.gj;
017    
018    import org.joda.time.DateTimeField;
019    
020    /**
021     * A reference Julian chronology implementation, intended for testing purposes
022     * only. Correctness is favored over performance. The key functions for date
023     * calculations are based on ones provided in "Calendrical Calculations", ISBN
024     * 0-521-77752-6.
025     *
026     * @author Brian S O'Neill
027     */
028    public final class TestJulianChronology extends TestGJChronology {
029    
030        private static final long JULIAN_EPOCH;
031    
032        static {
033            // Constant as defined in book.
034            JULIAN_EPOCH = new TestGregorianChronology().fixedFromGJ(0, 12, 30);
035        }
036    
037        /**
038         * Constructs with an epoch of 1969-12-19.
039         */
040        public TestJulianChronology() {
041            super(1969, 12, 19);
042        }
043    
044        public TestJulianChronology(int epochYear, int epochMonth, int epochDay) {
045            super(epochYear, epochMonth, epochDay);
046        }
047    
048        public DateTimeField dayOfMonth() {
049            return new TestJulianDayOfMonthField(this); 
050        }
051    
052        public DateTimeField weekyear() {
053            return new TestJulianWeekyearField(this);
054        }
055    
056        public DateTimeField monthOfYear() {
057            return new TestJulianMonthOfYearField(this);
058        }
059    
060        public DateTimeField year() {
061            return new TestJulianYearField(this);
062        }
063    
064        public String toString() {
065            return "TestJulianChronology";
066        }
067    
068        long millisPerYear() {
069            return (long)(365.25 * MILLIS_PER_DAY);
070        }
071    
072        long millisPerMonth() {
073            return (long)(365.25 * MILLIS_PER_DAY / 12);
074        }
075    
076        boolean isLeapYear(int year) {
077            if (year == 0) {
078                throw new IllegalArgumentException("Illegal year: " + year);
079            }
080            return mod(year, 4) == (year > 0 ? 0 : 3);
081        }
082    
083        /**
084         * @return days from 0001-01-01
085         */
086        long fixedFromGJ(int year, int monthOfYear, int dayOfMonth) {
087            if (year == 0) {
088                throw new IllegalArgumentException("Illegal year: " + year);
089            }
090            int y = (year < 0) ? year + 1 : year;
091            long y_m1 = y - 1;
092            long f = JULIAN_EPOCH - 1 + 365 * y_m1 + div(y_m1, 4)
093                + div(367 * monthOfYear - 362, 12) + dayOfMonth;
094            if (monthOfYear > 2) {
095                f += isLeapYear(year) ? -1 : -2;
096            }
097            return f;
098        }
099    
100        /**
101         * @param date days from 0001-01-01
102         * @return gj year
103         */
104        int gjYearFromFixed(long date) {
105            return gjFromFixed(date)[0];
106        }
107    
108        /**
109         * @param date days from 0001-01-01
110         * @return gj year, monthOfYear, dayOfMonth
111         */
112        int[] gjFromFixed(long date) {
113            long approx = div(4 * (date - JULIAN_EPOCH) + 1464, 1461);
114            long year = (approx <= 0) ? approx - 1 : approx;
115            int year_i = (int)year;
116            if (year_i != year) {
117                throw new RuntimeException("year cannot be cast to an int: " + year);
118            }
119            long priorDays = date - fixedFromGJ(year_i, 1, 1);
120            long correction;
121            if (date < fixedFromGJ(year_i, 3, 1)) {
122                correction = 0;
123            } else if (isLeapYear(year_i)) {
124                correction = 1;
125            } else {
126                correction = 2;
127            }
128            int monthOfYear = (int)div(12 * (priorDays + correction) + 373, 367);
129            int day = (int)(date - fixedFromGJ(year_i, monthOfYear, 1) + 1);
130    
131            return new int[]{year_i, monthOfYear, day};
132        }
133    
134        long fixedFromISO(int weekyear, int weekOfWeekyear, int dayOfWeek) {
135            if (weekyear == 0) {
136                throw new IllegalArgumentException("Illegal weekyear: " + weekyear);
137            }
138            if (weekyear == 1) {
139                weekyear = -1;
140            } else {
141                weekyear--;
142            }
143            return nthWeekday(weekOfWeekyear, 0, weekyear, 12, 28) + dayOfWeek;
144        }
145    
146        /**
147         * @param date days from 0001-01-01
148         * @return iso weekyear, weekOfWeekyear, dayOfWeek (1=Monday to 7)
149         */
150        int[] isoFromFixed(long date) {
151            int weekyear = gjYearFromFixed(date - 3);
152            int nextWeekyear;
153            if (weekyear == -1) {
154                nextWeekyear = 1;
155            } else {
156                nextWeekyear = weekyear + 1;
157            }
158            if (date >= fixedFromISO(nextWeekyear, 1, 1)) {
159                weekyear = nextWeekyear;
160            }
161            int weekOfWeekyear = (int)(div(date - fixedFromISO(weekyear, 1, 1), 7) + 1);
162            int dayOfWeek = (int)amod(date, 7);
163            return new int[]{weekyear, weekOfWeekyear, dayOfWeek};
164        }
165    }