Chinesischer Kalender implementiert
5 files modified
3 files added
New file |
| | |
| | | package de.uhilger.zeitrechnung; |
| | | |
| | | /** |
| | | * |
| | | * @author Ulrich Hilger |
| | | */ |
| | | public class ChinesischesDatum extends Datum { |
| | | |
| | | private long zyklus; |
| | | private boolean schaltmonat; |
| | | |
| | | public ChinesischesDatum() { |
| | | super(); |
| | | } |
| | | |
| | | public ChinesischesDatum(long zyklus, long jahr, int monat, boolean schaltmonat, int tag) { |
| | | super(jahr, monat, tag); |
| | | this.zyklus = zyklus; |
| | | this.schaltmonat = schaltmonat; |
| | | } |
| | | |
| | | public long getZyklus() { |
| | | return zyklus; |
| | | } |
| | | |
| | | public void setZyklus(long zyklus) { |
| | | this.zyklus = zyklus; |
| | | } |
| | | |
| | | public boolean isSchaltmonat() { |
| | | return schaltmonat; |
| | | } |
| | | |
| | | public void setSchaltmonat(boolean schaltmonat) { |
| | | this.schaltmonat = schaltmonat; |
| | | } |
| | | } |
New file |
| | |
| | | /* |
| | | Zeitrechnung - a class library to determine calendar events |
| | | Copyright (c) 1984-2023 Ulrich Hilger, http://uhilger.de |
| | | |
| | | This program is free software: you can redistribute it and/or modify |
| | | it under the terms of the GNU Affero General Public License as published by |
| | | the Free Software Foundation, either version 3 of the License, or |
| | | (at your option) any later version. |
| | | |
| | | This program is distributed in the hope that it will be useful, |
| | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| | | GNU Affero General Public License for more details. |
| | | |
| | | You should have received a copy of the GNU Affero General Public License |
| | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
| | | */ |
| | | package de.uhilger.zeitrechnung.ereignis; |
| | | |
| | | import de.uhilger.zeitrechnung.ChinesischesDatum; |
| | | import de.uhilger.zeitrechnung.Datum; |
| | | import de.uhilger.zeitrechnung.Definition; |
| | | import de.uhilger.zeitrechnung.kalender.ChinesischerKalender; |
| | | import de.uhilger.zeitrechnung.kalender.ISOKalender; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * Ein Ereignis, das durch ein Datum im traditionellen |
| | | * chinesischen Kalender definiert ist. |
| | | * |
| | | * <p>Soll nicht ein Ereignis bestimmt sondern in den oder vom chinesischen |
| | | * Kalender umgerechnet werden, muss die Klasse ChinesischerKalender genutzt |
| | | * werden.</p> |
| | | * |
| | | * <p> |
| | | * Die Ereignis-Definition lautet:<br> |
| | | * ChinesischesEreignis.getDefinition.setp1(Monat des chinesischen Kalenders);<br> |
| | | * ChinesischesEreignis.getDefinition.setp2(Tag im Monat des chinesischen Kalenders);<br> |
| | | * wobei Monat einer Ganzzahl aus |
| | | * [ChinesischerKalender.noch-angeben .. ChinesischerKalender.noch-angeben] |
| | | * entspricht. |
| | | * </p> |
| | | * |
| | | * @author Ulrich Hilger |
| | | */ |
| | | public class ChinesischesEreignis extends EreignisBasis { |
| | | |
| | | @Override |
| | | public int getTyp() { |
| | | return EreignisBasis.TYP_CHINESISCH; |
| | | } |
| | | |
| | | @Override |
| | | public List<Datum> getZeitpunkte(long isoJahr) { |
| | | Definition def = getDefinition(); |
| | | ChinesischerKalender j = new ChinesischerKalender(); |
| | | long tag = j.cDatumZuGenerisch(isoJahr, (int) def.getp1(), (int) def.getp2()); |
| | | List daten = new ArrayList(); |
| | | daten.add(new ISOKalender().vonTagen(tag)); |
| | | return daten; |
| | | } |
| | | |
| | | public List<Datum> getZeitpunkte2(long isoJahr) { |
| | | ISOKalender w = new ISOKalender(); |
| | | long start = w.zuTagen(isoJahr, Definition.JANUAR, 1); |
| | | long ende = w.zuTagen(isoJahr, Definition.DEZEMBER, 31); |
| | | |
| | | ChinesischerKalender j = new ChinesischerKalender(); |
| | | Definition def = getDefinition(); |
| | | List daten = new ArrayList(); |
| | | long y = 0; |
| | | long tag = start; |
| | | long ersterJanuar = start; |
| | | while(tag < ende) { |
| | | |
| | | // Neujahr vor dem 1.1. als generisches Datum |
| | | long cNeujahr = j.neujahrAmOderVor(ersterJanuar); |
| | | |
| | | // Neujahrstag als chinesisches Datum, z.B. 7. Feb 2022 für Neujahr vor 1.1.2023 |
| | | ChinesischesDatum cdNeujahr = j.vonTagen(cNeujahr); |
| | | |
| | | // generisches Datum fuer den Tag des Ereignisses bestimmen, |
| | | // Ereignisse fallen nie auf Schaltmonate, deshalb schaltmonat auf false |
| | | tag = j.zuTagen(cdNeujahr.getZyklus(), (int) cdNeujahr.getJahr(), (int) def.getp1(), |
| | | false, (int) def.getp2()); |
| | | |
| | | // der Liste hinzufügen, wenn das Ereignis ins isoJahr fällt |
| | | if (tag >= start && tag <= ende) { |
| | | Datum d = w.vonTagen(tag); |
| | | daten.add(d); |
| | | } |
| | | |
| | | // für nächstes Jahr wiederholen, bis das Ereignis nicht mehr im isoJahr liegt |
| | | ++y; |
| | | ersterJanuar = w.zuTagen(isoJahr+y, Definition.JANUAR, 1); |
| | | } |
| | | return daten; |
| | | } |
| | | } |
| | |
| | | |
| | | import de.uhilger.zeitrechnung.Datum; |
| | | import de.uhilger.zeitrechnung.Definition; |
| | | import de.uhilger.zeitrechnung.kalender.ChinesischerKalender; |
| | | import de.uhilger.zeitrechnung.kalender.HebraeischerKalender; |
| | | import de.uhilger.zeitrechnung.kalender.ISOKalender; |
| | | import java.util.ArrayList; |
| | |
| | | |
| | | /** Typnummer fuer Jom Ha Zikaron */ |
| | | public static final int EE_JOM_HA_ZIKARON = 4; |
| | | |
| | | public static final int EE_CHINESISCHES_NEUJAHR = 5; |
| | | |
| | | public static final int EE_QINGMING = 6; |
| | | |
| | | /** |
| | | * Den Typ des Ereignisses ermitteln |
| | |
| | | Definition def = getDefinition(); |
| | | int typ = (int) def.getp1(); |
| | | ISOKalender g = new ISOKalender(); |
| | | HebraeischerKalender h = new HebraeischerKalender(); |
| | | List daten = new ArrayList(); |
| | | long tage; |
| | | switch(typ) { |
| | | case EE_SCHAVUOT: |
| | | tage = h.passah(isoJahr); |
| | | tage = new HebraeischerKalender().passah(isoJahr); |
| | | daten.add(g.vonTagen(tage + 50)); |
| | | break; |
| | | case EE_PURIM: |
| | | tage = h.purim(isoJahr); |
| | | tage = new HebraeischerKalender().purim(isoJahr); |
| | | daten.add(g.vonTagen(tage)); |
| | | break; |
| | | case EE_TA_ANIT_ESTHER: |
| | | tage = h.taAnitEsther(isoJahr); |
| | | tage = new HebraeischerKalender().taAnitEsther(isoJahr); |
| | | daten.add(g.vonTagen(tage)); |
| | | break; |
| | | case EE_JOM_HA_ZIKARON: |
| | | tage = h.yomHaZikkaron(isoJahr); |
| | | tage = new HebraeischerKalender().yomHaZikkaron(isoJahr); |
| | | daten.add(g.vonTagen(tage)); |
| | | break; |
| | | case EE_CHINESISCHES_NEUJAHR: |
| | | daten.add(g.vonTagen(new ChinesischerKalender().neujahr(isoJahr))); |
| | | break; |
| | | case EE_QINGMING: |
| | | daten.add(g.vonTagen(new ChinesischerKalender().qingMing(isoJahr))); |
| | | break; |
| | | } |
| | | return daten; |
| | | } |
| | |
| | | /** Typnummer fuer JulianischesEreignis */ |
| | | public static final int TYP_JULIANISCH = 9; |
| | | |
| | | /** Typnummer fuer ChinesischesEreignis */ |
| | | public static final int TYP_CHINESISCH = 10; |
| | | |
| | | public static final int GREGORIANISCHER_KALENDER = 1; |
| | | public static final int JULIANISCHER_KALENDER = 2; |
| | | |
| | |
| | | TagDatumEreignis.class.getName(), OsterEreignis.class.getName(), |
| | | JahreszeitEreignis.class.getName(), HebraeischesEreignis.class.getName(), |
| | | EinzelEreignis.class.getName(), MuslimischesEreignis.class.getName(), |
| | | JulianischesEreignis.class.getName() |
| | | JulianischesEreignis.class.getName(), ChinesischesEreignis.class.getName() |
| | | }; |
| | | |
| | | /** |
| | |
| | | long anzahlJahre = (jEndJahr - jStartJahr) + (long) 1; |
| | | ArrayList list = new ArrayList(); |
| | | for (long y = 0; y < anzahlJahre; y++) { |
| | | Datum jDatum = new Datum(isoJahr + y, (int) definition.getp1(), (int) definition.getp2()); |
| | | long tage = j.zuTagen(jDatum); |
| | | long tage = j.zuTagen(jStartJahr + y, (int) definition.getp1(), (int) definition.getp2()); |
| | | if(tage >= start && tage <= end) { |
| | | list.add(g.vonTagen(tage)); |
| | | } |
| | |
| | | import de.uhilger.zeitrechnung.Zeit; |
| | | |
| | | /** |
| | | * Abstrakte Basisklasse fuer Klassen, die ein Kalendersystem implementieren |
| | | * Abstrakte Basisklasse fuer Klassen, die ein Kalendersystem implementieren. |
| | | * |
| | | * Hier sind neben allerlei relevanten Rechenmethoden die grundlegenden |
| | | * astronomischen Algorithmen für die Zeit- und Kalenderrechnung implementiert. |
| | | * |
| | | * @author Ulrich Hilger |
| | | * @version 2, 1.10.2022 |
| | | */ |
| | | public abstract class BasisKalender implements Zeitrechnung { |
| | | |
| | |
| | | return nterTag(-1, t, datum); |
| | | } |
| | | |
| | | /* ----- */ |
| | | |
| | | public double moduloAngepasst(double x, double y) { |
| | | return y + modulo(x, -y); |
| | | } |
| | | |
| | | /* ---- Zeit ----- */ |
| | | |
| | | public double zuMoment(int stunde, int minute, double sekunde) { |
| | |
| | | return z; |
| | | } |
| | | |
| | | /* ----------- Mondphase ----------- */ |
| | | /* ----------- Mond ----------- */ |
| | | |
| | | /** durchschnittliche Dauer eines Mondphasenzyklus (synodischer Monat) in Tagen */ |
| | | public static final double MITTLERER_SYNODISCHER_MONAT = 29.530588853; |
| | | |
| | | public double mondphase(double t) { |
| | | return modulo(mondLaenge(t) - solareLaenge(t), 360); |
| | |
| | | public double mondHoehe(double t, Ort ort) { |
| | | double phi = ort.getBreite(); |
| | | double psi = ort.getLaenge(); |
| | | double varepsilon = obliquity(t); |
| | | double varepsilon = schiefstand(t); |
| | | double lambda = mondLaenge(t); |
| | | double beta = mondBreite(t); |
| | | double alpha = arcTanGrad( |
| | |
| | | private static final double[] siderealCoeff = new double[] {280.46061837, 36525 * 360.98564736629, 0.000387933, 1d/38710000}; |
| | | } |
| | | |
| | | public double neumondNach(double tee) { |
| | | return nterNeumond(1 + Math.round(tee / MITTLERER_SYNODISCHER_MONAT - mondphase(tee) / (double) (360))); |
| | | } |
| | | |
| | | public double neumondVor(double tee) { |
| | | return nterNeumond(Math.round(tee / MITTLERER_SYNODISCHER_MONAT - mondphase(tee) / (double) (360))); |
| | | } |
| | | |
| | | public double nterNeumond(long n) { |
| | | double k = n - 24724; |
| | | double c = k / 1236.85; |
| | | double approx = poly(c, nm.coeffApprox); |
| | | double capE = poly(c, nm.coeffCapE); |
| | | double solarAnomaly = poly(c, nm.coeffSolarAnomaly); |
| | | double lunarAnomaly = poly(c, nm.coeffLunarAnomaly); |
| | | double moonArgument = poly(c, nm.coeffMoonArgument); |
| | | double capOmega = poly(c, nm.coeffCapOmega); |
| | | double correction = -0.00017 * sinGrad(capOmega); |
| | | for(int ix = 0; ix < nm.sineCoeff.length; ++ix) { |
| | | correction += nm.sineCoeff[ix] * Math.pow(capE, nm.EFactor[ix]) * |
| | | sinGrad(nm.solarCoeff[ix] * solarAnomaly + |
| | | nm.lunarCoeff[ix] * lunarAnomaly + |
| | | nm.moonCoeff[ix] * moonArgument); |
| | | } |
| | | double additional = 0; |
| | | for(int ix = 0; ix < nm.addConst.length; ++ix) { |
| | | additional += nm.addFactor[ix] * |
| | | sinGrad(nm.addConst[ix] + nm.addCoeff[ix] * k); |
| | | } |
| | | double extra = 0.000325 * sinGrad(poly(c, nm.extra)); |
| | | return universalVonDynamisch(approx + correction + extra + additional); |
| | | } |
| | | private static class nm { |
| | | private static final double[] coeffApprox = new double[] {730125.59765, MITTLERER_SYNODISCHER_MONAT * 1236.85, 0.0001337, -0.000000150, 0.00000000073}; |
| | | private static final double[] coeffCapE = new double[] {1, -0.002516, -0.0000074}; |
| | | private static final double[] coeffSolarAnomaly = new double[] {2.5534, 29.10535669 * 1236.85, -0.0000218, -0.00000011}; |
| | | private static final double[] coeffLunarAnomaly = new double[] {201.5643, 385.81693528 * 1236.85, 0.0107438, 0.00001239, -0.000000058}; |
| | | private static final double[] coeffMoonArgument = new double[] {160.7108, 390.67050274 * 1236.85, -0.0016341, -0.00000227, 0.000000011}; |
| | | private static final double[] coeffCapOmega = new double[] {124.7746, -1.56375580 * 1236.85, 0.0020691, 0.00000215}; |
| | | private static final byte[] EFactor = new byte[] {0, 1, 0, 0, 1, 1, 2, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| | | private static final byte[] solarCoeff = new byte[] {0, 1, 0, 0, -1, 1, 2, 0, 0, 1, 0, 1, 1, -1, 2, 0, 3, 1, 0, 1, -1, -1, 1, 0}; |
| | | private static final byte[] lunarCoeff = new byte[] {1, 0, 2, 0, 1, 1, 0, 1, 1, 2, 3, 0, 0, 2, 1, 2, 0, 1, 2, 1, 1, 1, 3, 4}; |
| | | private static final byte[] moonCoeff = new byte[] {0, 0, 0, 2, 0, 0, 0, -2, 2, 0, 0, 2, -2, 0, 0, -2, 0, -2, 2, 2, 2, -2, 0, 0}; |
| | | private static final double[] sineCoeff = new double[] { |
| | | -0.40720, 0.17241, 0.01608, 0.01039, 0.00739, -0.00514, 0.00208, |
| | | -0.00111, -0.00057, 0.00056, -0.00042, 0.00042, 0.00038, -0.00024, |
| | | -0.00007, 0.00004, 0.00004, 0.00003, 0.00003, -0.00003, 0.00003, |
| | | -0.00002, -0.00002, 0.00002 |
| | | }; |
| | | private static final double[] addConst = new double[] { |
| | | 251.88, 251.83, 349.42, 84.66, 141.74, 207.14, 154.84, 34.52, 207.19, |
| | | 291.34, 161.72, 239.56, 331.55 |
| | | }; |
| | | private static final double[] addCoeff = new double[] { |
| | | 0.016321, 26.641886, 36.412478, 18.206239, 53.303771, 2.453732, |
| | | 7.306860, 27.261239, 0.121824, 1.844379, 24.198154, 25.513099, 3.592518 |
| | | }; |
| | | private static final double[] addFactor = new double[] { |
| | | 0.000165, 0.000164, 0.000126, 0.000110, 0.000062, 0.000060, 0.000056, |
| | | 0.000047, 0.000042, 0.000040, 0.000037, 0.000035, 0.000023 |
| | | }; |
| | | private static final double[] extra = new double[] { |
| | | 299.77, 132.8475848, -0.009173 |
| | | }; |
| | | } |
| | | |
| | | public double universalVonDynamisch(double tee) { |
| | | return tee - ephemeridenKorrektur(tee); |
| | | } |
| | | |
| | | public double universalVonStandard(double teeS, Ort locale) { |
| | | return teeS - locale.getZeitzone() / 24; |
| | | } |
| | | |
| | | /* ----------- Sonnenauf- und -untergang ----------- */ |
| | | |
| | | public double sonnenaufgang(long date, Ort ort) throws Exception { |
| | |
| | | public double zeitVonHorizont(double approx, Ort ort, double alpha) throws Exception { |
| | | double phi = ort.getBreite(); |
| | | double t = universalVonLokal(approx, ort); |
| | | double delta = arcSinGrad(sinGrad(obliquity(t)) * sinGrad(solareLaenge(t))); |
| | | double delta = arcSinGrad(sinGrad(schiefstand(t)) * sinGrad(solareLaenge(t))); |
| | | boolean morgen = modulo(approx, 1) < 0.5; |
| | | double sinusAbstand = tanGrad(phi) * tanGrad(delta) + |
| | | sinGrad(alpha) / (kosGrad(delta) * kosGrad(phi)); |
| | |
| | | double laenge = poly(c, et.koeffLaenge); |
| | | double anomalie = poly(c, et.koeffAnomalie); |
| | | double exzentrizitaet = poly(c, et.koeffExzentrizitaet); |
| | | double varepsilon = obliquity(t); |
| | | double varepsilon = schiefstand(t); |
| | | double y = quadrat(tanGrad(varepsilon / 2)); |
| | | double equation = (1d / 2d / Math.PI) * (y * sinGrad(2 * laenge) + |
| | | -2 * exzentrizitaet * sinGrad(anomalie) + |
| | | 4 * exzentrizitaet * y * sinGrad(anomalie) * kosGrad(2 * laenge) + |
| | | -0.5 * y * y * sinGrad(4 * laenge) + |
| | | -1.25 * exzentrizitaet * exzentrizitaet * sinGrad(2 * anomalie)); |
| | | return signum(equation) * Math.min(Math.abs(equation), stunde(12)); |
| | | return vorzeichen(equation) * Math.min(Math.abs(equation), stunde(12)); |
| | | } |
| | | private static class et { |
| | | private static final double[] koeffLaenge = new double[] {280.46645, 36000.76983, 0.0003032}; |
| | |
| | | private static final double[] koeffExzentrizitaet = new double[] {0.016708617, -0.000042037, -0.0000001236}; |
| | | } |
| | | |
| | | public double obliquity(double t) { |
| | | public double schiefstand(double t) { |
| | | double c = julJahrhunderte(t); |
| | | return winkel(23, 26, 21.448) + poly(c, coeffObliquity); |
| | | } |
| | | private final double[] coeffObliquity = new double[] {0, winkel(0, 0, -46.8150), winkel(0, 0, -0.00059), winkel(0, 0, 0.001813)}; |
| | | |
| | | public int signum(double x) { |
| | | public int vorzeichen(double x) { |
| | | if(x < 0) |
| | | return -1; |
| | | else if(x > 0) |
| | |
| | | |
| | | /* ---------------- Jahreszeiten ----- */ |
| | | |
| | | public static final double TROPISCHES_JAHR = 365.242189; |
| | | /** durchschnittliche Dauer eines Umlaufs der Erde um die Sonne in Tagen */ |
| | | public static final double MITTLERES_TROPISCHES_JAHR = 365.242189; |
| | | |
| | | public double standardVonUniversal(double t, Ort ort) { |
| | | return t + ort.getZeitzone() / 24; |
| | |
| | | |
| | | public double solareLaengeNach(double t, double phi) { |
| | | double varepsilon = 0.00001; |
| | | double rate = TROPISCHES_JAHR / (double) 360; |
| | | double rate = MITTLERES_TROPISCHES_JAHR / (double) 360; |
| | | double tau = t + rate * modulo(phi - solareLaenge(t), 360); |
| | | double l = Math.max(t, tau - 5); |
| | | double u = tau + 5; |
| | |
| | | return modulo(laenge + aberration(t) + nutation(t), 360); |
| | | } |
| | | |
| | | public double geschaetzteSolareLaengeVor(double tee, double phi) { |
| | | double rate = MITTLERES_TROPISCHES_JAHR / (double) (360); |
| | | double tau = tee - rate * modulo(solareLaenge(tee) - phi, 360); |
| | | double capDelta = modulo(solareLaenge(tau) - phi + (double) (180), 360) - (double) (180); |
| | | return Math.min(tee, tau - rate * capDelta); |
| | | } |
| | | |
| | | public double julJahrhunderte(double t) { |
| | | return (dynamischVonUniversal(t) - j2000()) / 36525; |
| | | } |
New file |
| | |
| | | /* |
| | | Zeitrechnung - a class library to determine calendar events |
| | | Copyright (c) 1984-2023 Ulrich Hilger, http://uhilger.de |
| | | |
| | | This program is free software: you can redistribute it and/or modify |
| | | it under the terms of the GNU Affero General Public License as published by |
| | | the Free Software Foundation, either version 3 of the License, or |
| | | (at your option) any later version. |
| | | |
| | | This program is distributed in the hope that it will be useful, |
| | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| | | GNU Affero General Public License for more details. |
| | | |
| | | You should have received a copy of the GNU Affero General Public License |
| | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
| | | */ |
| | | package de.uhilger.zeitrechnung.kalender; |
| | | |
| | | import de.uhilger.zeitrechnung.ChinesischesDatum; |
| | | import de.uhilger.zeitrechnung.Datum; |
| | | import de.uhilger.zeitrechnung.Definition; |
| | | import de.uhilger.zeitrechnung.Ort; |
| | | |
| | | /** |
| | | * Der traditionelle chinesichen Kalender ist unterteilt in Mond- und Sonnenjahr. |
| | | * Das Sonnenjahr (sui) beginnt am Tag der Wintersonnenwende ist weiter unterteilt |
| | | * in solare Abschnitte und noch weiter unterteilt in solare Haupt- und Unterabschnitte. |
| | | * |
| | | * Die Bahn, die die Sonne innerhalb eines tropischen Jahres von 365,24 Tagen scheinbar |
| | | * auf der Ekliptik durchläuft, wird in 24 Teile von je 15 Grad unterteilt. Dies sind die 24 |
| | | * Stationen oder Jahreseinteilungen (節氣 / 节气, jiéqì). Jede zweite Station ist ein |
| | | * Zhongqi (中氣 / 中气, zhōngqì – „zentrale/Haupt-Jahreseinteilung“), wobei die Sonnenwenden |
| | | * und Tagundnachtgleichen vier der zwölf Zhongqi sind. Der zeitliche Abstand von einem |
| | | * Zhongqi zum nächsten beträgt im Mittel ein Zwölftel eines tropischen Jahres oder 30,44 Tage. |
| | | * Er variiert leicht aufgrund der elliptischen Umlaufbahn der Erde um die Sonne. |
| | | * |
| | | * Jeweils sechs Jahreseinteilungen gehören zu einer Jahreszeit. Während aber im westlichen |
| | | * Kalender die Jahreszeiten mit dem Tag der Sonnenwende bzw. der Tagundnachtgleiche beginnen, |
| | | * liegen im chinesischen Kalender diese Tage in der Mitte der jeweiligen Jahreszeit. Man zählt |
| | | * die Stationen beginnend mit dem Frühlingsanfang – lichun, und mancherorts gilt der |
| | | * Frühlingsanfang (und nicht die Wintersonnenwende) als Beginn des sui-Jahres. |
| | | * |
| | | * Eine Besonderheit des Chinesischen Kalenders ist, dass ein Chinesisches Datum neben |
| | | * Tag, Monat und Jahr auch den Zyklus und die Angabe zum Schaltmonat benoetigt. Um |
| | | * die Schnittstelle Wandler vollstaendig implementieren zu koennen, werden |
| | | * die Methoden setZyklus und setSchaltmonat zusaetzlich eingebaut. |
| | | * |
| | | * Bei Nutzung der Wandler-Schnittstelle sollte im Falle des Chinesischen Kalenders |
| | | * darauf geachtet werden, dass der Methode zuTagen() entweder ein Datum der |
| | | * Klasse ChinesischesDatum uebergeben wird oder zuvor setZyklus und setSchaltmonat genutzt wird. |
| | | * Auch vor Nutzung der Methode zuTagen(jahr, monat, tag) muss zuvor setZyklus und |
| | | * setSchaltmonat genutzt oder stattdessen die Methode |
| | | * zuTagen(zyklus, jahr, monat, schaltmonat, tag) verwendet werden. |
| | | * |
| | | * @author Ulrich Hilger |
| | | */ |
| | | public class ChinesischerKalender extends BasisKalender implements Wandler { |
| | | |
| | | public static final long STARTTAG = new ISOKalender().zuTagen(-2636, Definition.FEBRUAR, 15); |
| | | |
| | | private long zyklus; |
| | | private boolean schaltmonat; |
| | | |
| | | public void setZyklus(long zyklus) { |
| | | this.zyklus = zyklus; |
| | | } |
| | | |
| | | public void setSchaltmonat(boolean schaltmonat) { |
| | | this.schaltmonat = schaltmonat; |
| | | } |
| | | |
| | | public long zuTagen(long zyklus, int jahr, int monat, boolean schaltmonat, int tag) { |
| | | this.zyklus = zyklus; |
| | | this.schaltmonat = schaltmonat; |
| | | return zuTagen(jahr, monat, tag); |
| | | } |
| | | |
| | | @Override |
| | | public long zuTagen(long jahr, int monat, int tag) { |
| | | long midYear = (long)Math.floor(STARTTAG + ((zyklus - 1) * 60 + (jahr - 1) + .5) * MITTLERES_TROPISCHES_JAHR); |
| | | long theNewYear = neujahrAmOderVor(midYear); |
| | | long p = chinesischerNeumondAmOderNach(theNewYear + 29 * (monat - 1)); |
| | | ChinesischesDatum d = vonTagen(p); |
| | | long priorNewMoon = monat == d.getMonat() && schaltmonat == d.isSchaltmonat() ? |
| | | p : |
| | | chinesischerNeumondAmOderNach(p + 1); |
| | | return priorNewMoon + tag - 1; |
| | | } |
| | | |
| | | @Override |
| | | public long zuTagen(Datum d) { |
| | | if(d instanceof ChinesischesDatum) { |
| | | ChinesischesDatum cd = (ChinesischesDatum) d; |
| | | return zuTagen(cd.getZyklus(), (int) cd.getJahr(), cd.getMonat(), cd.isSchaltmonat(), cd.getTag()); |
| | | } else { |
| | | return zuTagen(zyklus, (int) d.getJahr(), d.getMonat(), schaltmonat, d.getTag()); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public ChinesischesDatum vonTagen(long tage) { |
| | | long s1 = winterSonnenwendeAmOderVor(tage); |
| | | long s2 = winterSonnenwendeAmOderVor(s1 + 370); |
| | | long m12 = chinesischerNeumondAmOderNach(s1 + 1); |
| | | long nextM11 = chinesischerNeumondVor(s2 + 1); |
| | | long m = chinesischerNeumondVor(tage + 1); |
| | | boolean leapYear = Math.round((nextM11 - m12) / MITTLERER_SYNODISCHER_MONAT) == 12; |
| | | int month = (int)moduloAngepasst( |
| | | (long)Math.round((m - m12) / MITTLERER_SYNODISCHER_MONAT) - |
| | | (leapYear && hatSchaltmonatVorher(m12, m) ? 1 : 0), |
| | | 12); |
| | | boolean leapMonth = leapYear && |
| | | keinSolarerHauptabschnitt(m) && |
| | | !hatSchaltmonatVorher(m12, chinesischerNeumondVor(m)); |
| | | long elapsedYears = (long)Math.floor(1.5 - (month / 12d) + (tage - STARTTAG) / MITTLERES_TROPISCHES_JAHR); |
| | | long cycle = ganzzahlQuotient(elapsedYears - 1, 60) + 1; |
| | | int year = (int)moduloAngepasst(elapsedYears, 60); |
| | | int day = (int)(tage - m + 1); |
| | | return new ChinesischesDatum(cycle, year, month, leapMonth, day); |
| | | } |
| | | |
| | | /* ---- */ |
| | | |
| | | public boolean hatSchaltmonatVorher(long mPrime, long m) { |
| | | return m >= mPrime && (keinSolarerHauptabschnitt(m) || hatSchaltmonatVorher(mPrime, chinesischerNeumondVor(m))); |
| | | } |
| | | |
| | | public double mitternachtInChina(long date) { |
| | | return universalVonStandard(date, chinesischerOrt(date)); |
| | | } |
| | | |
| | | public final Ort peking(double tee) { |
| | | ISOKalender g = new ISOKalender(); |
| | | long year = g.jahrVonTagen((long)Math.floor(tee)); |
| | | return new Ort("Peking", (double) (39.55), winkel(116,25,0), |
| | | (double) (43.5), year < 1929 ? 1397d/180 : 8); |
| | | } |
| | | |
| | | public final Ort chinesischerOrt(double tee) { |
| | | return peking(tee); |
| | | } |
| | | |
| | | public long neujahrAmOderVor(long tage) { |
| | | long neujahr = neujahrInSui(tage); |
| | | return tage >= neujahr ? neujahr : neujahrInSui(tage - 180); |
| | | } |
| | | |
| | | public long neujahrInSui(long tage) { |
| | | long s1 = winterSonnenwendeAmOderVor(tage); |
| | | long s2 = winterSonnenwendeAmOderVor(s1 + 370); |
| | | long m12 = chinesischerNeumondAmOderNach(s1 + 1); |
| | | long m13 = chinesischerNeumondAmOderNach(m12 + 1); |
| | | long nextM11 = chinesischerNeumondVor(s2 + 1); |
| | | if((Math.round((nextM11 - m12) / MITTLERER_SYNODISCHER_MONAT) == 12) && (keinSolarerHauptabschnitt(m12) || keinSolarerHauptabschnitt(m13))) { |
| | | return chinesischerNeumondAmOderNach(m13 + 1); |
| | | } else { |
| | | return m13; |
| | | } |
| | | } |
| | | |
| | | public long neujahr(long gYear) { |
| | | ISOKalender g = new ISOKalender(); |
| | | return neujahrAmOderVor(g.zuTagen(gYear, Definition.JULI, 1)); |
| | | } |
| | | |
| | | public long qingMing(long gYear) { |
| | | ISOKalender g = new ISOKalender(); |
| | | return (long)Math.floor(solarerNebenabschnittAmOderNach(g.zuTagen(gYear, Definition.MAERZ, 30))); |
| | | } |
| | | |
| | | public long cDatumZuGenerisch(long isoJahr, int cMonat, int cTag) { |
| | | ISOKalender g = new ISOKalender(); |
| | | long elapsedYears = isoJahr - g.jahrVonTagen(STARTTAG) + 1; |
| | | long cycle = ganzzahlQuotient(elapsedYears - 1, 60) + 1; |
| | | int year = (int)moduloAngepasst(elapsedYears, 60); |
| | | return zuTagen(cycle, year, cMonat, false, cTag); |
| | | } |
| | | |
| | | public long winterSonnenwendeAmOderVor(long date) { |
| | | double approx = geschaetzteSolareLaengeVor(mitternachtInChina(date + 1), Definition.WINTER); |
| | | long i; |
| | | for(i = (long)(Math.floor(approx) - 1); !(Definition.WINTER <= solareLaenge(mitternachtInChina(i + 1))); ++i); |
| | | return i; |
| | | } |
| | | |
| | | public boolean keinSolarerHauptabschnitt(long date) { |
| | | return aktuellerSolarerHauptabschnitt(date) == |
| | | aktuellerSolarerHauptabschnitt(chinesischerNeumondAmOderNach(date + 1)); |
| | | } |
| | | |
| | | public int aktuellerSolarerHauptabschnitt(long date) { |
| | | double s = solareLaenge(standardVonUniversal(date, chinesischerOrt(date))); |
| | | return (int)moduloAngepasst(2 + ganzzahlQuotient(s, (double) (30)), 12); |
| | | } |
| | | |
| | | public double solarerHauptabschnittAmOderNach(long date) { |
| | | double l = modulo(30 * Math.ceil(solareLaenge(mitternachtInChina(date)) / 30), 360); |
| | | return chinesischeSolareLaengeAmOderNach(date, l); |
| | | } |
| | | |
| | | public double solarerNebenabschnittAmOderNach(long date) { |
| | | double l = modulo(30 * Math.ceil((solareLaenge(mitternachtInChina(date)) - (double) (15)) / 30) + (double) (15), 360); |
| | | return chinesischeSolareLaengeAmOderNach(date, l); |
| | | } |
| | | |
| | | public double chinesischeSolareLaengeAmOderNach(long date, double theta) { |
| | | Ort beijing = chinesischerOrt(date); |
| | | double tee = solareLaengeNach(standardVonUniversal(date, beijing), theta); |
| | | return standardVonUniversal(tee, beijing); |
| | | } |
| | | |
| | | public int aktuellerSolarerUnterabschnitt(long date) { |
| | | double s = solareLaenge(mitternachtInChina(date)); |
| | | return (int)moduloAngepasst(3 + ganzzahlQuotient(s - (double) (15), (double) (30)), 12); |
| | | } |
| | | |
| | | public long chinesischerNeumondAmOderNach(long date) { |
| | | double t = neumondNach(mitternachtInChina(date)); |
| | | return (long)Math.floor(standardVonUniversal(t, chinesischerOrt(t))); |
| | | } |
| | | |
| | | public long chinesischerNeumondVor(long date) { |
| | | double t = neumondVor((long) mitternachtInChina(date)); |
| | | return (long)Math.floor(standardVonUniversal(t, chinesischerOrt(t))); |
| | | } |
| | | } |