julian Date 计算 和实现

Qt库里CBSDate类的内部实现用_jd成员进行计算、比较等操作,而这个变量是通过static inline uint32 greg2jul(const int32 y, const int32 m, const int32 d)函数计算出来的。对于这个变量具体代表的含义,我弄不明白,在java里似乎也没发现直接对应的方法。尤其最终的计算表达式:

1721119 + day + (146097*c)/4 + (1461*ya)/4 + (153*month+2)/5;

这么多常数是什么含义???google了一下请教了朋友,大致明白了

这个计算,是算出一个日期对应的儒略日(Julian Date)。

儒略日是一种不用年和月的长期纪日法,计算距一个7980年周期的开始已逝去的天数,这个周期是1583年Joseph Scaliger发明的。这个系统的目的是要使计算一个日历日期和另一个日历日期之间的整数差变得容易。而7980年的周期是从取多个传统时间周期(太阳的,月亮的,还有一个很特殊的罗马的征税周期)的公倍数而得来的。第一个Julian周期的开始点是在儒略历的公元前4713年的一月一号格林尼治平午(即该地民用时12时,而不是平常的子夜开始),此时为“0”日的开始,至次日格林尼治平午为“1”日的开始,由此顺数而下,延续不断地记下来。第一个周期结束于3268年1月22号。接下来的那天就是第二个儒略日周期(7980年的周期)的第一天。

例如:

2451919.3423000001348555,它意味着距儒略周期的开始已经逝去了2451919天。“.3423000001348555”代表这天的时间(“15:12:54 EST”)。

 

儒略日的计算和使用:

公历转换为干支历

先看年干支的计算。在y年,如2005年则是y=2005;而公元前的年代要转为负号年代,如公元前2070年则为y=-2069。计算a值:a=y-4;再计算gz值:gz=a-[a/60]*60,其中[ ]表明是取整运算,如[8.75]=8。如果gz值为负,则gz=gz+60,使其为正值。这个gz值就是六十甲子的编号。例如,对2005年,y=2005,a=2001,a/60=33.35,[a/60]=33,gz=21,而21就是乙酉,说明2005年是乙酉年。对公元前2070年,y= -2069,a= -2073,a/60= -34.55,[a/60]= -34,gz= -33,gz= -33+60=27,则前2070年为辛卯年。

也可以从a值分别计算年天干或年地支。天干值为:g=a-[a/10]*10,若g是负数则g=g+10。地支值为:z=a-[a/12]*12,若z是负数则z=z+12。例如,公元前2070年,a= -2073,g = -3,g=7,z= -9,z=3。g值为7对应天干的“辛”,z值为3对应地支的“卯”,因此前2070年为辛卯年。

再看日干支的计算,主要是先将公历日期转换为儒略日,再由儒略日来计算日干支。因为儒略日是以正午开始新的一天,这里我们不妨约定某日的儒略日是对应于该日正午的。而计算儒略日的方法有很多种,这里只选一种适于编程的方法(括号内的是举例):

1)对于y年m月d日。(如2005年2月9日12时,则y = 2005,m = 2,d=9)

2)如果是1月和2月(m=1和m=2)则:m=m+12,y=y-1。(因m=2,则m=14,y=2004)

3)计算a值:a=[30.6001*(m+1)] 。(a=[30.6001*(14+1)]=[459.0015]=459)

4)计算b值:在1582年10月4日以前(含),b=0;在1582年10月15日以后(含),b=2-[y/100]+[y/400]。(因在1582年之后,b=2-[2004/100]+[2004/400]=2-20+5= -13)

5)计算c值:如果y<0,c=[365.25*y-0.75]-694025;如果y=0,c=[365.25*y-0.75]-694025;如果y>0,c=[365.25*y]-694025。(因y>0,c=[365.25*2004]-694025=731961-694025=37936)

6)计算约化儒略日mjd值(以1900年1月0.5日起算的天数):mjd=a+b+c+d。(mjd=459-13+37936+9=38391)

7)计算儒略日jd值: jd=mjd+2415020。(jd=38391+2415020=2453411)

从举例来看,2005年2月9日12时的儒略日为2453411,或略去12时而称2005年2月9日的儒略日为2453411。在计算儒略日时,对于公元前的年代,同样要注意先将其转为负号年代。

而将儒略日转换为日干支就非常简单了。对于儒略日jd值,计算ad值:ad=jd-11;再计算gz值:gz=ad-[ad/60]*60,这就是干支编号。例如儒略日为2453411,则ad=2453400,gz=0,对应干支为甲子,表明该日为甲子日。也可以从ad值分别计算日天干或日地支。天干值为:g=ad-[ad/10]*10,地支值为:z=ad-[ad/12]*12。

顺便提一句,用儒略日来计算星期几也很容易。对于儒略日jd值,计算ax值,ax=jd+1;再计算xq值:xq=ax-[ax/7]*7,xq值为0时是星期日,其余的值是几就是星期几。如2005年2月9日的儒略日为2453411,ax=2453412,xq=3,则该日为星期三。

而在java中,没有直接对应的计算儒略日的方法。。。java内部是使用unix的习惯,以1970年1月1日零时起经过的毫秒数来计算的。一般计算两个时间之间相隔天数,是

    long beginTime = beginDate.getTime();

    long endTime2 = endDate.getTime();

    long betweenDays = (long)((endTime - beginTime) / (1000 * 60 * 60 *24) + 0.5);

 

转者注:我要用的类库需要mjd时间。不得不google下了。

 

btw:儒略历转公历的算法。

double jd = mjd;//+2400000.5;

//计算year month,date
 int Y,M,D,hour,min,sec;
 int l= (int)jd + 68569;
 int N = ( 4 * l ) / 146097;
 l = l - ( 146097 *N + 3 ) / 4;
 int I = ( 4000 * ( l + 1 ) ) / 1461001;
 l = l - ( 1461 * I ) / 4 + 31;
 int J = ( 80 * l) / 2447;
 D = l - ( 2447 * J ) / 80;
 l = J / 11;
 M = J +2 - ( 12 *l );
 Y = 100 * ( N - 49 ) + I + l;

//计算hour minute second

 double hms = jd - (int)jd;
 hour = (int)(hms*24.0);
 min = (int)((hms - hour/24.0)*1440.0);
 sec = (int)((hms-hour/24.0-min/1444.0)*86400.0);

 

实现代码:

package com.easy.utils;

/**
 * Collected methods which allow easy implementation of <code>hashCode()</code>.
 * Based on items #7 and #8 from "Effective Java" book.
 * <p>
 * Usage scenario:<br>
 * <pre>
 * int result = HashCodeUtil.SEED;
 * result = HashCodeUtil.hash(result, fIsDecrepit);
 * ...
 * return result;
 * </pre>
 */
public class HashCode {

	/**
	 * An initial hash code value to which is added contributions from fields.
	 * Using a non-zero value decreases collisions of hash code values.
	 */
	public static final int SEED = 173;

	public static final int PRIME = 37;

	// ---------------------------------------------------------------- boolean

	/**
	 * Calculates hash code for booleans.
	 */
	public static int hash(int seed, boolean aBoolean) {
		return (PRIME * seed) + (aBoolean ? 1231 : 1237);
	}

	/**
	 * Calculates hash code for boolean array.
	 */
	public static int hash(int seed, boolean[] booleanArray) {
		if (booleanArray == null) {
			return 0;
		}
		for (boolean aBoolean : booleanArray) {
			seed = hash(seed, aBoolean);
		}
		return seed;
	}

	/**
	 * Calculates hash code for boolean array.
	 */
	public static int hashBooleanArray(int seed, boolean... booleanArray) {
		return hash(seed, booleanArray);
	}

	// ---------------------------------------------------------------- char

	/**
	 * Calculates hash code for chars.
	 */
	public static int hash(int seed, char aChar) {
		return (PRIME * seed) + (int) aChar;
	}

	/**
	 * Calculates hash code for char array.
	 */
	public static int hash(int seed, char[] charArray) {
		if (charArray == null) {
			return 0;
		}
		for (char aChar : charArray) {
			seed = hash(seed, aChar);
		}
		return seed;
	}

	/**
	 * Calculates hash code for char array.
	 */
	public static int hashCharArray(int seed, char... charArray) {
		return hash(seed, charArray);
	}

	// ---------------------------------------------------------------- ints

	/**
	 * Calculates hash code for ints.
	 */
	public static int hash(int seed, int anInt) {
		return (PRIME * seed) + anInt;
	}

	/**
	 * Calculates hash code for int array.
	 */
	public static int hash(int seed, int[] intArray) {
		if (intArray == null) {
			return 0;
		}
		for (int anInt : intArray) {
			seed = hash(seed, anInt);
		}
		return seed;
	}

	/**
	 * Calculates hash code for int array.
	 */
	public static int hashIntArray(int seed, int... intArray) {
	    return hash(seed, intArray);
	}


	/**
	 * Calculates hash code for short array.
	 */
	public static int hash(int seed, short[] shortArray) {
		if (shortArray == null) {
			return 0;
		}
		for (short aShort : shortArray) {
			seed = hash(seed, aShort);
		}
		return seed;
	}

	/**
	 * Calculates hash code for short array.
	 */
	public static int hashShortArray(int seed, short... shortArray) {
		return hash(seed, shortArray);
	}

	/**
	 * Calculates hash code for byte array.
	 */
	public static int hash(int seed, byte[] byteArray) {
		if (byteArray == null) {
			return 0;
		}
		for (byte aByte : byteArray) {
			seed = hash(seed, aByte);
		}
		return seed;
	}

	/**
	 * Calculates hash code for byte array.
	 */
	public static int hashByteArray(int seed, byte... byteArray) {
		return hash(seed, byteArray);
	}


	// ---------------------------------------------------------------- long

	/**
	 * Calculates hash code for longs.
	 */
	public static int hash(int seed, long aLong) {
		return (PRIME * seed) + (int) (aLong ^ (aLong >>> 32));
	}

	/**
	 * Calculates hash code for long array.
	 */
	public static int hash(int seed, long[] longArray) {
		if (longArray == null) {
			return 0;
		}
		for (long aLong : longArray) {
			seed = hash(seed, aLong);
		}
		return seed;
	}

	/**
	 * Calculates hash code for long array.
	 */
	public static int hashLongArray(int seed, long... longArray) {
		return hash(seed, longArray);
	}

	// ---------------------------------------------------------------- float

	/**
	 * Calculates hash code for floats.
	 */
	public static int hash(int seed, float aFloat) {
		return hash(seed, Float.floatToIntBits(aFloat));
	}

	/**
	 * Calculates hash code for float array.
	 */
	public static int hash(int seed, float[] floatArray) {
		if (floatArray == null) {
			return 0;
		}
		for (float aFloat : floatArray) {
			seed = hash(seed, aFloat);
		}
		return seed;
	}


	/**
	 * Calculates hash code for float array.
	 */
	public static int hashFloatArray(int seed, float... floatArray) {
		return hash(seed, floatArray);
	}

	// ---------------------------------------------------------------- double

	/**
	 * Calculates hash code for doubles.
	 */
	public static int hash(int seed, double aDouble) {
		return hash(seed, Double.doubleToLongBits(aDouble));
	}

	/**
	 * Calculates hash code for double array.
	 */
	public static int hash(int seed, double[] doubleArray) {
		if (doubleArray == null) {
			return 0;
		}
		for (double aDouble : doubleArray) {
			seed = hash(seed, aDouble);
		}
		return seed;
	}

	/**
	 * Calculates hash code for double array.
	 */
	public static int hashDoubleArray(int seed, double... doubleArray) {
	    return hash(seed, doubleArray);
	}

	// ---------------------------------------------------------------- object

	/**
	 * Calculates hash code for Objects. Object is a possibly-null object field, and possibly an array.
	 * <p>
	 * If <code>aObject</code> is an array, then each element may be a primitive
	 * or a possibly-null object.
	 */
	public static int hash(int seed, Object aObject) {
		int result = seed;
		if (aObject == null) {
			result = hash(result, 0);
		} else if (aObject.getClass().isArray() == false) {
			result = hash(result, aObject.hashCode());
		} else {
			Object[] objects = (Object[]) aObject;
			int length = objects.length;
			for (int idx = 0; idx < length; ++idx) {
				result = hash(result, objects[idx]);
			}
		}
		return result;
	}

}

 

package com.easy.utils;

import static com.easy.utils.HashCode.hash;

import java.io.Serializable;
import java.math.BigDecimal;

/**
 * Julian Date stamp, for high precision calculations. Julian date is a real
 * number and it basically consist of two parts: integer and fraction. Integer
 * part carries date information, fraction carries time information.
 *
 * <p>
 * The Julian day or Julian day number (JDN) is the (integer) number of days that
 * have elapsed since Monday, January 1, 4713 BC in the proleptic Julian calendar 1.
 * That day is counted as Julian day zero. Thus the multiples of 7 are Mondays.
 * Negative values can also be used.
 *
 * <p>
 * The Julian Date (JD) is the number of days (with decimal fraction of the day) that
 * have elapsed since 12 noon Greenwich Mean Time (UT or TT) of that day.
 * Rounding to the nearest integer gives the Julian day number.
 * <p>
 * For calculations that will have time precision of 1e-3 seconds, both
 * fraction and integer part must have enough digits in it. The problem is
 * that integer part is big and, on the other hand fractional is small, and
 * since final julian date is a sum of this two values, some fraction
 * numerals may be lost. Therefore, for higher precision both
 * fractional and integer part of julian date real number has to be
 * preserved.
 * <p>
 * This class stores the unmodified fraction part, but not all digits
 * are significant! For 1e-3 seconds precision, only 8 digits after
 * the decimal point are significant.
 *
 * @see TimeUtil
 * @see JDateTime
 * @see DateTimeStamp
 */
public class JulianDateStamp implements Serializable, Cloneable {

	/**
	 * Integer part of the Julian Date (JD).
	 */
	protected int integer;

	/**
	 * Returns integer part of the Julian Date (JD).
	 */
	public int getInteger() {
		return integer;
	}

	/**
	 * Fraction part of the Julian Date (JD).
	 * Should be always in [0.0, 1.0) range.
	 */
	protected double fraction;

	/**
	 * Returns the fraction part of Julian Date (JD).
	 * Returned value is always in [0.0, 1.0) range.
	 */
	public double getFraction() {
		return fraction;
	}

	/**
	 * Calculates and returns significant fraction only as an int.
	 */
	public int getSignificantFraction() {
		return (int) (fraction * 100000000);
	}

	/**
	 * Returns JDN. Note that JDN is not equal to {@link #integer}. It is calculated by
	 * rounding to the nearest integer.
	 */
	public int getJulianDayNumber() {
		if (fraction >= 0.5) {
			return integer + 1;
		}
		return integer;
	}

	// ---------------------------------------------------------------- ctors

	/**
	 * Default empty constructor.
	 */
	public JulianDateStamp() {
	}

	/**
	 * Creates JD from a <code>double</code>.
	 */
	public JulianDateStamp(double jd) {
		set(jd);
	}

	/**
	 * Creates JD from both integer and fractional part using normalization.
	 * Normalization occurs when fractional part is out of range. 
	 *
	 * @see #set(int, double)
	 *
	 * @param i      integer part
	 * @param f      fractional part should be in range [0.0, 1.0)
	 */
	public JulianDateStamp(int i, double f) {
		set(i, f);
	}

	/**
	 * Creates JD from <code>BigDecimal</code>.
	 */
	public JulianDateStamp(BigDecimal bd) {
		double d = bd.doubleValue();
		integer = (int) d;
		bd = bd.subtract(new BigDecimal(integer));
		fraction = bd.doubleValue();
	}


	// ---------------------------------------------------------------- conversion
	

	/**
	 * Returns <code>double</code> value of JD.
	 * <b>CAUTION</b>: double values may not be suit for precision math due to
	 * loss of precision.
	 */
	public double doubleValue() {
		return (double)integer + fraction;
	}

	/**
	 * Returns <code>BigDecimal</code> value of JD.
	 */
	@SuppressWarnings({"UnpredictableBigDecimalConstructorCall"})
	public BigDecimal toBigDecimal() {
		BigDecimal bd = new BigDecimal(integer);
		return bd.add(new BigDecimal(fraction));
	}

	/**
	 * Returns string representation of JD.
	 *
	 * @return julian integer as string
	 */
	@Override
	public String toString() {
		String s = Double.toString(fraction);
		int i = s.indexOf('.');
		s = s.substring(i);
		return integer + s;
	}


	// ---------------------------------------------------------------- math

	/**
	 * Adds a JD to current instance.
	 */
	public JulianDateStamp add(JulianDateStamp jds) {
		int i = this.integer + jds.integer;
		double f = this.fraction + jds.fraction;
		set(i, f);
		return this;
	}

	/**
	 * Adds a double to current instance.
	 */
	public JulianDateStamp add(double delta) {
		set(this.integer, this.fraction + delta);
		return this;
	}


	/**
	 * Subtracts a JD from current instance.
	 */
	public JulianDateStamp sub(JulianDateStamp jds) {
		int i = this.integer - jds.integer;
		double f = this.fraction -jds.fraction;
		set(i, f);
		return this;
	}

	/**
	 * Subtracts a double from current instance.
	 */
	public JulianDateStamp sub(double delta) {
		set(this.integer, this.fraction - delta);
		return this;
	}

	/**
	 * Sets integer and fractional part with normalization.
	 * Normalization means that if double is out of range,
	 * values will be correctly fixed. 
	 */
	public void set(int i, double f) {
		integer = i;
		int fi = (int) f;
		f -= fi;
		integer += fi;
		if (f < 0) {
			f += 1;
			integer--;
		}
		this.fraction = f;
	}

	public void set(double jd) {
		integer = (int)jd;
		fraction = jd - (double)integer;
	}


	// ---------------------------------------------------------------- between

	/**
	 * Calculates the number of days between two dates. Returned value is always positive.
	 */
	public int daysBetween(JulianDateStamp otherDate) {
		int difference = daysSpan(otherDate);
		return difference >= 0 ? difference : -difference;
	}

	/**
	 * Returns span between two days. Returned value may be positive (when this date
	 * is after the provided one) or negative (when comparing to future date).
	 */
	public int daysSpan(JulianDateStamp otherDate) {
		int now = getJulianDayNumber();
		int then = otherDate.getJulianDayNumber();
		return now - then;
	}

	// ---------------------------------------------------------------- equals & hashCode

	@Override
	public boolean equals(Object object) {
		if (this == object) {
			return true;
		}
		if (!(object instanceof JulianDateStamp)) {
			return false;
		}
		JulianDateStamp stamp = (JulianDateStamp) object;
		return  (stamp.integer == this.integer) &&
				(Double.compare(stamp.fraction, this.fraction) == 0);
	}

	@Override
	public int hashCode() {
		int result = HashCode.SEED;
		result = hash(result, integer);
		result = hash(result, fraction);
		return result;
	}

	// ---------------------------------------------------------------- clone

	@Override
	protected JulianDateStamp clone() {
		return new JulianDateStamp(this.integer, this.fraction);
	}

	// ---------------------------------------------------------------- conversion

	/**
	 * Returns Reduced Julian Date (RJD), used by astronomers.
	 * RJD = JD 鈭�2400000
	 */
	public JulianDateStamp getReducedJulianDate() {
		return new JulianDateStamp(integer - 2400000, fraction);
	}

	public void setReducedJulianDate(double rjd) {
		set(rjd + 2400000);
	}

	/**
	 * Returns Modified Julian Date (MJD), where date starts from midnight rather than noon.
	 * RJD = JD 鈭�2400000.5
	 */
	public JulianDateStamp getModifiedJulianDate() {
		return new JulianDateStamp(integer - 2400000, fraction - 0.5);
	}

	public void setModifiedJulianDate(double mjd) {
		set(mjd + 2400000.5);
	}

	/**
	 * Returns Truncated Julian Day (TJD), introduced by NASA for the space program.
	 * TJD began at midnight at the beginning of May 24, 1968 (Friday).
	 */
	public JulianDateStamp getTruncatedJulianDate() {
		return new JulianDateStamp(integer - 2440000, fraction - 0.5);
	}

	public void setTruncatedJulianDate(double tjd) {
		set(tjd + 2440000.5);
	}
}

 

你可能感兴趣的:(Date,实现,julian,儒略日)