Bestimmung der Zeitpunkte von Ereignissen
ulrich
2023-03-19 8cf8493e4b918cece529fef978d50c8b9835d230
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*
  Zeitrechnung - a class library to determine calendar events
  Copyright (c) 1984-2022 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.Datum;
import de.uhilger.zeitrechnung.Definition;
 
/**
 * Die Klasse ISOKalender implementiert einen Wandler fuer das gregorianische 
 * Kalendersystem gem&auml;&szlig; ISO 8601 
 * 
 * @author Ulrich Hilger
 * @version 2, 1.10.2022
 */
public class ISOKalender extends BasisKalender implements Wandler {
 
  public static final long STARTTAG = 1;
  
  /**
   * Das Datum im gregorianischen Kalendersystem fuer ein generisches Datum 
   * ermitteln.
   * 
   * @param generischesDatum Anzahl der Tage zwischen 1. Januar 1 im gregorianischen 
   * Kalender und dem Tag, dessen Datum im gregorianischen Kalendersystem 
   * ermittelt werden soll
   * @return das Datum im gregorianischen Kalendersystem
   */
  @Override
  public Datum vonTagen(long generischesDatum) {
    long jahr = jahrVonTagen(generischesDatum);
    long tageVorJan1 = generischesDatum - zuTagen(jahr, Definition.JANUAR, 1);
    int schalttag = generischesDatum < zuTagen(jahr, Definition.MAERZ, 1) ? 0 :
            (schaltjahr(jahr) ? 1 : 2);
    int monat = (int) ganzzahlQuotient(12 * (tageVorJan1 + schalttag) + 373, 367);
    int tag = (int) (generischesDatum - zuTagen(jahr, monat, 1) + 1);
    return new Datum(jahr, monat, tag);
  }
  
  public long jahrVonTagen(long generischesDatum) {
    long l0 = generischesDatum - STARTTAG;
    long n400 = ganzzahlQuotient(l0, 146097);
    long d1 = modulo(l0, 146097);
    long n100 = ganzzahlQuotient(d1, 36524);
    long d2 = modulo(d1, 36524);
    long n4 = ganzzahlQuotient(d2, 1461);
    long d3 = modulo(d2, 1461);
    long n1 = ganzzahlQuotient(d3, 365);
    long year = 400 * n400 + 100 * n100 + 4 * n4 + n1;
    return n100 == 4 || n1 == 4 ? year : year + 1;
  }
 
  /**
   * Die Anzahl der Tage ermitteln, die zwischen einem gegebenen Datum 
   * des gregorianischen Kalendersystems und dem Tag liegen, der im
   * gregorianischen Kalender mit dem Datum 1. Januar 1 bezeichnet ist.
   * 
   * @param isoDatum das Datum im gregorianischen Kalendersystem
   * 
   * @return Anzahl Tage, die zwischen dem gegebenen Datum 
   * eines bestimmten Kalendersystems 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 isoDatum) {
    return zuTagen(isoDatum.getJahr(), isoDatum.getMonat(), isoDatum.getTag());
  }
 
  /**
   * Die Anzahl der Tage ermitteln, die zwischen einem gegebenen Datum 
   * des gregorianischen Kalendersystems und dem Tag liegen, der im
   * gregorianischen Kalender mit dem Datum 1. Januar 1 bezeichnet ist.
   * 
   * @param isoJahr das Jahr im gregorianischen Kalendersystem
   * @param isoMonat der Monat im gregorianischen Kalendersystem
   * @param tag der Tag im Monat des gregorianischen Kalendersysem
   * 
   * @return Anzahl Tage, die zwischen dem gegebenen Datum 
   * eines bestimmten Kalendersystems 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 isoJahr, int isoMonat, int tag) {
    return STARTTAG - 1
            + 365 * (isoJahr - 1)
            + ganzzahlQuotient(isoJahr - 1, 4)
            - ganzzahlQuotient(isoJahr - 1, 100)
            + ganzzahlQuotient(isoJahr - 1, 400)
            + ganzzahlQuotient(367 * isoMonat - 362, 12)
            + (isoMonat <= 2 ? 0 : (schaltjahr(isoJahr) ? -1 : -2))
            + tag;
  }
 
  private boolean schaltjahr(long isoJahr) {
    boolean ergebnis = false;
    if (modulo(isoJahr, 4) == 0) {
      long n = modulo(isoJahr, 400);
      if (n != 100 && n != 200 && n != 300) {
        ergebnis = true;
      }
    }
    return ergebnis;
  }
  
  public int letzterDesMonats(long isoJahr, int isoMonat) {
    switch (isoMonat) {
      case Definition.FEBRUAR:
        if (schaltjahr(isoJahr)) {
          return 29;
        } else {
          return 28;
        }
      case Definition.APRIL:
      case Definition.JUNI:
      case Definition.SEPTEMBER:
      case Definition.NOVEMBER:
        return 30;
      default:
        return 31;
    }
  }  
}