Bestimmung der Zeitpunkte von Ereignissen
ulrich
2023-03-24 a2b5909d2b689f4f609218d80087a332ce55f2be
src/de/uhilger/zeitrechnung/kalender/ChinesischerKalender.java
@@ -57,25 +57,86 @@
 */
public class ChinesischerKalender extends BasisKalender implements Wandler {
  /**
   * Der Start der chinesischen Zeitrechung laesst sich aus dem heutigen
   * chinesischen Datum errechnen. Das erste Jahr des ersten Zyklus faellt
   * so auf den 15. Februar -2636 im gregorianischen Kalender.
   */
   public static final long STARTTAG = new ISOKalender().zuTagen(-2636, Definition.FEBRUAR, 15);
  
  private long zyklus;
  private boolean schaltmonat;
  /**
   * Den Zyklus angeben, in dem ein Jahr des chinesischen Kalenders liegt, das
   * mit der Methode zuTagen(jahr, monat, tag) in ein generisches Datum
   * umgerechnet werden soll.
   *
   * Diese Methode ist eine Hilfsfunktion, wenn nicht die Methode
   * zuTagen(zyklus, jahr, monat, schaltmonat, tag) genutzt werden kann.
   *
   * @param zyklus der Zyklus im chinesischen á¸°alender
   */
  public void setZyklus(long zyklus) {
    this.zyklus = zyklus;
  }
  /**
   * Angeben, ob ein Monat eines Datums im chinesischen Kalender ein
   * Schaltmonat ist, das mit der Methode
   * zuTagen(jahr, monat, tag) in ein generisches Datum
   * umgerechnet werden soll.
   *
   * Diese Methode ist eine Hilfsfunktion, wenn nicht die Methode
   * zuTagen(zyklus, jahr, monat, schaltmonat, tag) genutzt werden kann.
   *
   * @param schaltmonat
   */
  public void setSchaltmonat(boolean schaltmonat) {
    this.schaltmonat = schaltmonat;
  }
  
  /**
   * Die Anzahl der Tage ermitteln, die zwischen einem gegebenen Datum
   * des chinesischen Kalenders und dem Tag liegen, der im
   * Gregorianischen Kalender mit dem Datum 1. Januar 1 bezeichnet ist.
   *
   * @param zyklus der Zyklus des chinesischen Kalenders
   * @param jahr  das Jahr des chinesischen Kalenders
   * @param monat  der Monat des chinesischen Kalenders
   * @param schaltmonat true, wenn der Monat ein Schaltmonat ist, sonst false
   * @param tag Tag im Monat des chinesischen Kalenders
   *
   * @return Anzahl Tage, die zwischen dem gegebenen Datum
   * des chinesischen Kalenders und dem Tag liegen, der im
   * Gregorianischen Kalender mit dem Datum 1. Januar 1 bezeichnet ist.
   * Liegt das gegebene Datum vor dem 1. Januar 1 (gregorianisch), wird
   * eine negative Zahl zurueckgegeben.
   */
   public long zuTagen(long zyklus, int jahr, int monat, boolean schaltmonat, int tag) {
    this.zyklus = zyklus;
    this.schaltmonat = schaltmonat;
    return zuTagen(jahr, monat, tag);
   }
  /**
   * Die Anzahl der Tage ermitteln, die zwischen einem gegebenen Datum
   * des chinesischen Kalenders und dem Tag liegen, der im
   * Gregorianischen Kalender mit dem Datum 1. Januar 1 bezeichnet ist.
   *
   * Bei Verwendung dieser Methode muessen zuvor die Methode setZyklus
   * und setSchaltmonat genutzt werden.
   *
   * @param jahr das Jahr im betreffenden Kalendersystem
   * @param monat der Monat im betreffenden Kalendersystem
   * @param tag der Tag im betreffenden Kalendersysem
   *
   * @return Anzahl Tage, die zwischen dem gegebenen Datum
   * des chinesischen Kalenders  und dem Tag liegen, der im
   * Gregorianischen Kalender mit dem Datum 1. Januar 1 bezeichnet ist.
   * Liegt das gegebene Datum vor dem 1. Januar 1 (gregorianisch), wird
   * eine negative Zahl zurueckgegeben.
   */
  @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);
@@ -88,6 +149,19 @@
      return priorNewMoon + tag - 1;
  }
  /**
   * Die Anzahl der Tage ermitteln, die zwischen einem gegebenen Datum
   * des chinesischen Kalenders und dem Tag liegen, der im
   * Gregorianischen Kalender mit dem Datum 1. Januar 1 bezeichnet ist.
   *
   * @param d das Datum im betreffenden Kalendersystem
   *
   * @return Anzahl Tage, die zwischen dem gegebenen Datum
   * des chinesischen Kalenders und dem Tag liegen, der im
   * Gregorianischen Kalender mit dem Datum 1. Januar 1 bezeichnet ist.
   * Liegt das gegebene Datum vor dem 1. Januar 1 (gregorianisch), wird
   * eine negative Zahl zurueckgegeben.
   */
  @Override
  public long zuTagen(Datum d) {
    if(d instanceof ChinesischesDatum) {
@@ -98,6 +172,15 @@
    }
  }
  /**
   * Das Datum des chinesischen Kalenders fuer ein generisches Datum
   * ermitteln.
   *
   * @param tage Anzahl der Tage zwischen 1. Januar 1 im gregorianischen
   * Kalender und dem Tag, dessen Datum des chinesischen Kalenders
   * ermittelt werden soll
   * @return das Datum im chinesischen Kalender
   */
  @Override
  public ChinesischesDatum vonTagen(long tage) {
      long s1 = winterSonnenwendeAmOderVor(tage);
@@ -120,25 +203,53 @@
    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) {
   public long neujahr(long isoJahr) {
    ISOKalender g = new ISOKalender();
      long year = g.jahrVonTagen((long)Math.floor(tee));
      return neujahrAmOderVor(g.zuTagen(isoJahr, Definition.JULI, 1));
   }
   public long qingMing(long isoJahr) {
    ISOKalender g = new ISOKalender();
      return (long)Math.floor(solarerNebenabschnittAmOderNach(g.zuTagen(isoJahr, Definition.MAERZ, 30)));
   }
  /**
   * Das generische Datum des Tages mit einem gegebenen festen Datum
   * im chinesischen Kalender fuer ein gegebenes gregorianisches Jahr
   * ermitteln
   *
   * @param isoJahr Jahr im gregorischen Kalender, in das ein gesuchtes Ereignis faellt
   * @param cMonat Monat im chinesischen Kalender, an dem das Ereignis stattfindet
   * @param cTag  Tag im Monat des chinesischen Kalenders, an dem das Ereignis stattfindet
   * @return generisches Datum des Ereignisses
   */
   public long cDatumZuTagen(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);
   }
  /* ---- Hilfsfunktionen --------- */
   public boolean hatSchaltmonatVorher(long mHaupt, long m) {
      return m >= mHaupt && (keinSolarerHauptabschnitt(m) || hatSchaltmonatVorher(mHaupt, chinesischerNeumondVor(m)));
   }
   public double mitternachtInChina(long tage) {
      return universalVonStandard(tage, chinesischerOrt(tage));
   }
   public final Ort peking(double t) {
    ISOKalender g = new ISOKalender();
      long year = g.jahrVonTagen((long)Math.floor(t));
      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 final Ort chinesischerOrt(double t) {
      return peking(t);
   }
   
   public long neujahrAmOderVor(long tage) {
@@ -151,77 +262,59 @@
      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))) {
      long naechsterM11 = chinesischerNeumondVor(s2 + 1);
      if((Math.round((naechsterM11 - 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);
   public long winterSonnenwendeAmOderVor(long tage) {
      double approx = geschaetzteSolareLaengeVor(mitternachtInChina(tage + 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 boolean keinSolarerHauptabschnitt(long tage) {
      return aktuellerSolarerHauptabschnitt(tage) ==
         aktuellerSolarerHauptabschnitt(chinesischerNeumondAmOderNach(tage + 1));
   }
   public int aktuellerSolarerHauptabschnitt(long date) {
      double s = solareLaenge(standardVonUniversal(date, chinesischerOrt(date)));
   public int aktuellerSolarerHauptabschnitt(long tage) {
      double s = solareLaenge(standardVonUniversal(tage, chinesischerOrt(tage)));
      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 solarerHauptabschnittAmOderNach(long tage) {
      double l = modulo(30 * Math.ceil(solareLaenge(mitternachtInChina(tage)) / 30), 360);
      return chinesischeSolareLaengeAmOderNach(tage, 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 solarerNebenabschnittAmOderNach(long tage) {
      double l = modulo(30 * Math.ceil((solareLaenge(mitternachtInChina(tage)) - (double) (15)) / 30) + (double) (15), 360);
      return chinesischeSolareLaengeAmOderNach(tage, l);
   }
   
   public double chinesischeSolareLaengeAmOderNach(long date, double theta) {
      Ort beijing = chinesischerOrt(date);
      double tee = solareLaengeNach(standardVonUniversal(date, beijing), theta);
      return standardVonUniversal(tee, beijing);
   public double chinesischeSolareLaengeAmOderNach(long tage, double theta) {
      Ort peking = chinesischerOrt(tage);
      double t = solareLaengeNach(standardVonUniversal(tage, peking), theta);
      return standardVonUniversal(t, peking);
   }
   
   public int aktuellerSolarerUnterabschnitt(long date) {
      double s = solareLaenge(mitternachtInChina(date));
   public int aktuellerSolarerUnterabschnitt(long tage) {
      double s = solareLaenge(mitternachtInChina(tage));
      return (int)moduloAngepasst(3 + ganzzahlQuotient(s - (double) (15), (double) (30)), 12);
   }
   public long chinesischerNeumondAmOderNach(long date) {
      double t = neumondNach(mitternachtInChina(date));
   public long chinesischerNeumondAmOderNach(long tage) {
      double t = neumondNach(mitternachtInChina(tage));
      return (long)Math.floor(standardVonUniversal(t, chinesischerOrt(t)));
   }
   public long chinesischerNeumondVor(long date) {
    double t = neumondVor((long) mitternachtInChina(date));
   public long chinesischerNeumondVor(long tage) {
    double t = neumondVor((long) mitternachtInChina(tage));
      return (long)Math.floor(standardVonUniversal(t, chinesischerOrt(t)));
   }
}