/* Analysation of algorithm of Hồ Ngọc Đức * --------------------------------------- * double off = LocalToJD(31, 12, Y) - 2415021.076998695; * ^ returns Julian Date of 31st December #Year, 00:00:00 UTC+7 * i.e. 30th December #Year, 17:00:00 UTC * int k = INT(off / 29.530588853); * double jd = NewMoon(k); * ^ returns the new moon date *time* in Julian Dater, *UTC* * int[] ret = LocalFromJD(jd); * ^ converts (jd + timezone/24) back to Calendar Date, * disregrading the time (i.e. the begining of day in local time zone * [00:00:00 local] that contains the new moon.) * * // sun longitude at local midnight * double sunLong = SunLongitude(LocalToJD(ret[0], ret[1], ret[2])); * if (sunLong > 3 * PI / 2) * { * jd = NewMoon(k - 1); * } * return LocalFromJD(jd); * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * e.g.: * NewMoon(1236) = 2451520.4393767994 * = 7/12/1999 10:32:42 UTC * = 8/12/1999 17:32:42 UTC+7, thus, * * [ Note: UniversalFromJD(2451520.4393767994) = (7, 12, 1999) ] * LocalFromJD(2451520.4393767994) = * UniversalFromJD(JD + LOCAL_TIMEZONE/24.0) = (8, 12, 1999) * * LocalToJD(8, 12, 1999) = * UniversalToJD(D, M, Y) - LOCAL_TIMEZONE/24.0 = 8/12/1999 00:00:00 - 7 * ^ ^ = 7/12/1999 17:00:00 UTC <---+ * | +---[ Shift back to local midnight ] | * +---[ Julius Date of D/M/Y 00:00:00 UTC (midnight), 8/12/1999 00:00:00] | * [ This value is used to determine the sun longitude ]---+ */ /// <summary> /// Returns the local date time of the new moon just before /// the winter solstice in the given lunar year. /// </summary> /// <param name="lunarYear"></param> /// <param name="timeZone"></param> /// <returns></returns> public static DateTime GetNewMoon11(int lunarYear, double timeZone) { // number of days from J1900 to local midnight of 31st December #Year // (lunarYear, 12, 31): local // (lunarYear, 12, 31, timeZone): universal var offset = JulianDateConverter.UniversalDateTimeToJulianDate(lunarYear, 12, 31, timeZone) - Constants.J1900; var k = (int)(offset / Constants.SynodicMonth); // local date & *time* of new moon var newMoonLocalDateTime = JulianDateConverter.JulianDateToUniversalDateTime(GetNewMoon(k)).AddHours(timeZone); // beginning of that day (i.e. midnight, i.e. strip off the time) var newMoonMidnightLocal = newMoonLocalDateTime.Date; // Julian Date at the beginning of that day var julianDate = newMoonMidnightLocal.AddHours(-timeZone).UniversalDateTimeToJulianDate(); var sunLongitude = Sun.GetSunLongitudeAtJulianDate(julianDate); // Check the winter solstice is after the beginning of that day: // If the winter soltice is *before* the beginning of that day, // the previous new moon is the new moon just before winter solstice, // else the current new moon is the new moon just before winter solstice. if (sunLongitude > (3 * Math.PI / 2)) { newMoonLocalDateTime = JulianDateConverter.JulianDateToUniversalDateTime(GetNewMoon(k - 1)).AddHours(timeZone); } return(newMoonLocalDateTime); }
public static DateTime GetDateTimeOfSolarTerm(int termIndex, int year) { int[] trialMonths = { 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 1, 1, 2, 2, 3 }; int[] trialDays = { 21, 5, 20, 6, 21, 6, 21, 7, 23, 7, 23, 8, 23, 8, 23, 7, 22, 7, 22, 6, 21, 4, 19, 5 }; double desiredSunLong = termIndex * (Math.PI / 12); DateTime estimatedDateTime = new DateTime(year, trialMonths[termIndex], trialDays[termIndex]); double currentSunLong = Sun.GetSunLongitudeAtJulianDate( estimatedDateTime.UniversalDateTimeToJulianDate()); currentSunLong = Sun.GetSunLongitudeAtJulianDate( estimatedDateTime.UniversalDateTimeToJulianDate()); var error = (currentSunLong - desiredSunLong).ToNormalizedArc(); var direction = 1; if (error > Math.PI) { direction = -1; } double resolution = 1; // days double previousSunLong = currentSunLong; var count = 0; do { estimatedDateTime = estimatedDateTime.AddDays(resolution * direction); currentSunLong = Sun.GetSunLongitudeAtJulianDate(estimatedDateTime.UniversalDateTimeToJulianDate()); double error1 = (currentSunLong - desiredSunLong).ToNormalizedArc(); double error2 = (desiredSunLong + 2 * Math.PI - currentSunLong).ToNormalizedArc(); if (error1 > error && error2 > error) { direction = direction * (-1); resolution = resolution / 2; } error = error1; if (error2 > error1) { error = error1; } else { error = error2; } count++; } while (resolution > (1f / 86400) && Math.Abs(error) > 0.00001); return(estimatedDateTime); }