001: // Copyright (c) 2003-2007, Jodd Team (jodd.sf.net). All Rights Reserved.
002:
003: package jodd.datetime;
004:
005: import jodd.util.ObjectUtil;
006: import jodd.util.HashCodeUtil;
007:
008: import java.math.BigDecimal;
009:
010: /**
011: * Julian Date stamp, for high precision calculations. Julian date is a real
012: * number and it basicly consist of two parts: integer and fraction. Integer
013: * part carries date information, fraction carries time information.
014: *
015: * <p>
016: * The Julian day or Julian day number (JDN) is the (integer) number of days that
017: * have elapsed since Monday, January 1, 4713 BC in the proleptic Julian calendar 1.
018: * That day is counted as Julian day zero. Thus the multiples of 7 are Mondays.
019: * Negative values can also be used.
020: *
021: * <p>
022: * The Julian Date (JD) is the number of days (with decimal fraction of the day) that
023: * have elapsed since 12 noon Greenwich Mean Time (UT or TT) of that day.
024: * Rounding to the nearest integer gives the Julian day number.
025: *
026: *
027: * For calculations that will have time precision of 1e-3 seconds, both
028: * fraction and integer part must have enough numerics in it. The problem is
029: * that integer part is big and, on the other hand fractional is small, and
030: * since final julian date is a sum of this two values, some fraction
031: * numerals may be lost. Therefore, for higher precision both
032: * fractional and intger part of julian date real number has to be
033: * preserved.
034: *
035: * @see TimeUtil
036: * @see JDateTime
037: * @see DateTimeStamp
038: *
039: * todo Add switches to MJD, TJD...
040: */
041: public class JulianDateStamp implements java.io.Serializable, Cloneable {
042:
043: /**
044: * Integer part of the Julian Date (JD).
045: */
046: public int integer;
047:
048: /**
049: * Fraction part of the Julian Date (JD).
050: * Should be always in [0.0, 1.0) range.
051: */
052: public double fraction;
053:
054: /**
055: * Returns JDN. Note that JDN is not equals to {@link #integer}. It is calculated by
056: * rounding to the nearest integer.
057: */
058: public int getJulianDayNumber() {
059: if (fraction >= 0.5) {
060: return integer + 1;
061: }
062: return integer;
063: }
064:
065: // ---------------------------------------------------------------- ctors
066:
067: /**
068: * Default empty constructor.
069: */
070: public JulianDateStamp() {
071: }
072:
073: /**
074: * Creates JD from a <code>double</code>.
075: */
076: public JulianDateStamp(double jd) {
077: integer = (int) jd;
078: fraction = jd - (double) integer;
079: }
080:
081: /**
082: * Creates JD from both integer and fractional part using normalization.
083: * Normalization occurs when fractional part is out of range.
084: *
085: * @see #set(int, double)
086: *
087: * @param i integer part
088: * @param f fractional part should be in range [0.0, 1.0)
089: */
090: public JulianDateStamp(int i, double f) {
091: set(i, f);
092: }
093:
094: /**
095: * Creates JD from <code>BigDecimal</code>.
096: */
097: public JulianDateStamp(BigDecimal bd) {
098: double d = bd.doubleValue();
099: integer = (int) d;
100: bd = bd.subtract(new BigDecimal((double) integer));
101: fraction = bd.doubleValue();
102: }
103:
104: // ---------------------------------------------------------------- conversion
105:
106: /**
107: * Returns <code>double</code> value of JD.
108: * <b>CAUTION</b>: double values may not be suit for precision math due to
109: * loss of precission.
110: */
111: public double doubleValue() {
112: return (double) integer + fraction;
113: }
114:
115: /**
116: * Returns <code>BigDecimal</code> value of JD.
117: */
118: public BigDecimal toBigDecimal() {
119: BigDecimal bd = new BigDecimal((double) integer);
120: return bd.add(new BigDecimal(fraction));
121: }
122:
123: /**
124: * Returns string representation of JD.
125: *
126: * @return julian integer as string
127: */
128: @Override
129: public String toString() {
130: String s = Double.toString(fraction);
131: int i = s.indexOf('.');
132: s = s.substring(i);
133: return integer + s;
134: }
135:
136: // ---------------------------------------------------------------- math
137:
138: /**
139: * Adds a JD to current instance.
140: */
141: public JulianDateStamp add(JulianDateStamp jds) {
142: int i = this .integer + jds.integer;
143: double f = this .fraction + jds.fraction;
144: set(i, f);
145: return this ;
146: }
147:
148: /**
149: * Adds a double to current instance.
150: */
151: public JulianDateStamp add(double delta) {
152: set(this .integer, this .fraction + delta);
153: return this ;
154: }
155:
156: /**
157: * Subtracts a JD from current instance.
158: */
159: public JulianDateStamp sub(JulianDateStamp jds) {
160: int i = this .integer - jds.integer;
161: double f = this .fraction - jds.fraction;
162: set(i, f);
163: return this ;
164: }
165:
166: /**
167: * Subtracts a double from current instance.
168: */
169: public JulianDateStamp sub(double delta) {
170: set(this .integer, this .fraction - delta);
171: return this ;
172: }
173:
174: /**
175: * Sets integer and fractional part with normalization.
176: * Normalization means that if double is out of range,
177: * values will be correctly fixed.
178: */
179: public void set(int i, double f) {
180: int fi = (int) f;
181: f -= fi;
182: integer += fi;
183: if (f < 0) {
184: f += 1;
185: integer--;
186: }
187: this .fraction = f;
188: }
189:
190: // ---------------------------------------------------------------- equals & hashCode
191:
192: @Override
193: public boolean equals(Object object) {
194: if (this == object) {
195: return true;
196: }
197: if (ObjectUtil.equalsType(object, this ) == false) {
198: return false;
199: }
200: final JulianDateStamp stamp = (JulianDateStamp) object;
201:
202: return (stamp.integer == this .integer)
203: && (Double.doubleToLongBits(stamp.fraction) == Double
204: .doubleToLongBits(this .fraction));
205: }
206:
207: @Override
208: public int hashCode() {
209: int result = HashCodeUtil.SEED;
210: result = HashCodeUtil.hash(result, this .integer);
211: result = HashCodeUtil.hash(result, this .fraction);
212: return result;
213: }
214:
215: // ---------------------------------------------------------------- clone
216:
217: @Override
218: protected Object clone() {
219: JulianDateStamp jds = new JulianDateStamp();
220: jds.integer = this.integer;
221: jds.fraction = this.fraction;
222: return jds;
223: }
224: }
|