Skip to content

Commit

Permalink
old hindu calendar validation
Browse files Browse the repository at this point in the history
see issue #851
  • Loading branch information
MenoData committed Jan 27, 2020
1 parent d9abc7a commit a3d0153
Show file tree
Hide file tree
Showing 7 changed files with 287 additions and 113 deletions.
79 changes: 59 additions & 20 deletions base/src/main/java/net/time4j/calendar/hindu/AryaSiddhanta.java
Expand Up @@ -22,7 +22,6 @@
package net.time4j.calendar.hindu;

import net.time4j.engine.CalendarEra;
import net.time4j.engine.CalendarSystem;
import net.time4j.engine.EpochDays;
import net.time4j.engine.VariantSource;

Expand Down Expand Up @@ -61,7 +60,7 @@ public enum AryaSiddhanta
*/
SOLAR {
@Override
public CalendarSystem<HinduCalendar> getCalendarSystem() {
public HinduCS getCalendarSystem() {
return new OldCS(true);
}
},
Expand All @@ -79,7 +78,7 @@ public CalendarSystem<HinduCalendar> getCalendarSystem() {
*/
LUNAR {
@Override
public CalendarSystem<HinduCalendar> getCalendarSystem() {
public HinduCS getCalendarSystem() {
return new OldCS(false);
}
};
Expand All @@ -103,15 +102,17 @@ public String getVariant() {
*
* @return CalendarSystem for the old Hindu calendar
*/
public abstract CalendarSystem<HinduCalendar> getCalendarSystem();
abstract HinduCS getCalendarSystem();

//~ Innere Klassen ----------------------------------------------------

private static class OldCS
extends HinduVariant.BaseCS {
extends HinduCS {

//~ Statische Felder/Initialisierungen ----------------------------

private static final long KALI_YUGA_EPOCH = -1132959L; // julian-BCE-3102-02-18 (as rata die)

private static final double ARYA_SOLAR_YEAR = 15779175.0 / 43200.0;
private static final double ARYA_SOLAR_MONTH = ARYA_SOLAR_YEAR / 12.0;
private static final double ARYA_LUNAR_MONTH = 1577917500.0 / 53433336.0;
Expand All @@ -130,7 +131,7 @@ public List<CalendarEra> getEras() {
}

@Override
public HinduCalendar transform(long utcDays) {
HinduCalendar create(long utcDays) {
double sun = EpochDays.RATA_DIE.transform(utcDays, EpochDays.UTC) - KALI_YUGA_EPOCH + 0.25;

if (this.isSolar()) {
Expand All @@ -142,7 +143,9 @@ public HinduCalendar transform(long utcDays) {
super.variant,
y,
HinduMonth.ofSolar(m),
HinduDay.valueOf(dom));
HinduDay.valueOf(dom),
utcDays
);
} else { // lunisolar
double newMoon = sun - modulo(sun, ARYA_LUNAR_MONTH);
double modNMAS = modulo(newMoon, ARYA_SOLAR_MONTH);
Expand All @@ -156,38 +159,74 @@ public HinduCalendar transform(long utcDays) {
super.variant,
y,
leap ? month.withLeap() : month,
HinduDay.valueOf(dom)
HinduDay.valueOf(dom),
utcDays
);
}
}

@Override
public long transform(HinduCalendar date) {
HinduCalendar create(int kyYear, HinduMonth month, HinduDay dom) {
double d;

if (this.isSolar()) {
d = date.getExpiredYearOfKaliYuga() * ARYA_SOLAR_YEAR
+ (date.getMonth().getRasi() - 1) * ARYA_SOLAR_MONTH
+ date.getDayOfMonth().getValue()
d = kyYear * ARYA_SOLAR_YEAR
+ (month.getRasi() - 1) * ARYA_SOLAR_MONTH
+ dom.getValue()
- 1.25;
} else { // lunisolar
double mina = (12 * date.getExpiredYearOfKaliYuga() - 1) * ARYA_SOLAR_MONTH;
double mina = (12 * kyYear - 1) * ARYA_SOLAR_MONTH;
double lunarNewYear = ARYA_LUNAR_MONTH * (Math.floor(mina / ARYA_LUNAR_MONTH) + 1);
int month = date.getMonth().getValue().getValue();
int m = month.getValue().getValue();

if (
date.getMonth().isLeap()
|| (Math.ceil((lunarNewYear - mina) / (ARYA_SOLAR_MONTH - ARYA_LUNAR_MONTH)) > month)
month.isLeap()
|| (Math.ceil((lunarNewYear - mina) / (ARYA_SOLAR_MONTH - ARYA_LUNAR_MONTH)) > m)
) {
month--;
m--;
}

d = lunarNewYear
+ ARYA_LUNAR_MONTH * month
+ (date.getDayOfMonth().getValue() - 1) * (ARYA_LUNAR_MONTH / 30) - 0.25;
+ ARYA_LUNAR_MONTH * m
+ (dom.getValue() - 1) * (ARYA_LUNAR_MONTH / 30) - 0.25;
}

return new HinduCalendar(
super.variant,
kyYear,
month,
dom,
EpochDays.UTC.transform((long) Math.ceil(KALI_YUGA_EPOCH + d), EpochDays.RATA_DIE)
);
}

@Override
boolean isValid(int kyYear, HinduMonth month, HinduDay dom) {
if ((kyYear < 0) || (kyYear > 5999) || (month == null) || (dom == null)) {
return false;
}

// solar: 30/31, lunar: 29/30
if (dom.getValue() > (this.isSolar() ? 31 : 30)) {
return false;
}

return EpochDays.UTC.transform((long) Math.ceil(KALI_YUGA_EPOCH + d), EpochDays.RATA_DIE);
HinduCalendar cal = this.create(kyYear, month, dom);
return cal.equals(this.create(cal.getDaysSinceEpochUTC()));

}

@Override
public long getMinimumSinceUTC() {
// solar: 0, vaishakha, 1 | lunar: 0, *chaitra, 1
long min = this.isSolar() ? KALI_YUGA_EPOCH : KALI_YUGA_EPOCH - 29;
return EpochDays.UTC.transform(min, EpochDays.RATA_DIE);
}

@Override
public long getMaximumSinceUTC() {
// solar: 5999, chaitra, 30 | lunar: 5999, phalguna, 30
return this.isSolar() ? 338699L : 338671L;
}

private boolean isSolar() {
Expand Down
105 changes: 105 additions & 0 deletions base/src/main/java/net/time4j/calendar/hindu/HinduCS.java
@@ -0,0 +1,105 @@
/*
* -----------------------------------------------------------------------
* Copyright © 2013-2020 Meno Hochschild, <http://www.menodata.de/>
* -----------------------------------------------------------------------
* This file (HinduCS.java) is part of project Time4J.
*
* Time4J is free software: You can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* Time4J 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Time4J. If not, see <http://www.gnu.org/licenses/>.
* -----------------------------------------------------------------------
*/

package net.time4j.calendar.hindu;

import net.time4j.engine.CalendarEra;
import net.time4j.engine.CalendarSystem;

import java.util.Arrays;
import java.util.List;


/**
* <p>Abstract calendar system for the Hindu calendar. </p>
*
* @author Meno Hochschild
* @since 5.6
*/
/*[deutsch]
* <p>Abstraktes Kalendersystem f&uuml;r den Hindu-Kalender. </p>
*
* @author Meno Hochschild
* @since 5.6
*/
abstract class HinduCS
implements CalendarSystem<HinduCalendar> {

//~ Instanzvariablen --------------------------------------------------

final HinduVariant variant;

//~ Konstruktoren -----------------------------------------------------

HinduCS(HinduVariant variant) {
super();

if (variant == null) {
throw new NullPointerException();
}

this.variant = variant;
}

//~ Methoden ----------------------------------------------------------

@Override
public final HinduCalendar transform(long utcDays) {
long min = this.getMinimumSinceUTC();
long max = this.getMaximumSinceUTC();

if ((utcDays < min) || (utcDays > max)) {
throw new IllegalArgumentException("Out of range: " + min + " <= " + utcDays + " <= " + max);
}

return this.create(utcDays);
}

@Override
public final long transform(HinduCalendar date) {
return date.getDaysSinceEpochUTC();
}

@Override
public List<CalendarEra> getEras() {
return Arrays.asList(HinduEra.values());
}

abstract HinduCalendar create(long utcDays);

abstract HinduCalendar create(
int kyYear,
HinduMonth month,
HinduDay dom
);

abstract boolean isValid(
int kyYear,
HinduMonth month,
HinduDay dom
);

// used in subclasses
static double modulo(double x, double y) {
return x - y * Math.floor(x / y);
}

}
51 changes: 31 additions & 20 deletions base/src/main/java/net/time4j/calendar/hindu/HinduCalendar.java
Expand Up @@ -55,7 +55,7 @@ public final class HinduCalendar

//~ Statische Felder/Initialisierungen --------------------------------

private static final Map<String, CalendarSystem<HinduCalendar>> CALSYS;
private static final Map<String, HinduCS> CALSYS;
private static final CalendarFamily<HinduCalendar> ENGINE;

static {
Expand Down Expand Up @@ -116,14 +116,16 @@ public final class HinduCalendar
private transient final int kyYear; // year of Kali Yuga (elapsed / expired)
private transient final HinduMonth month;
private transient final HinduDay dayOfMonth;
private transient final long utcDays;

//~ Konstruktoren -----------------------------------------------------

HinduCalendar(
HinduVariant variant,
int kyYear,
HinduMonth month,
HinduDay dayOfMonth
HinduDay dayOfMonth,
long utcDays
) {
super();

Expand All @@ -141,6 +143,7 @@ public final class HinduCalendar
this.kyYear = kyYear;
this.month = month;
this.dayOfMonth = dayOfMonth;
this.utcDays = utcDays;

}

Expand Down Expand Up @@ -181,15 +184,10 @@ public static HinduCalendar of(
int month,
int dayOfMonth
) {
// TODO: validation of date components

if (aryaSiddhanta == AryaSiddhanta.SOLAR) {
return new HinduCalendar(
HinduVariant.VAR_OLD_SOLAR, year, HinduMonth.ofSolar(month), HinduDay.valueOf(dayOfMonth));
} else { // lunisolar
return new HinduCalendar(
HinduVariant.VAR_OLD_LUNAR, year, HinduMonth.ofLunisolar(month), HinduDay.valueOf(dayOfMonth));
}

HinduMonth m = (
(aryaSiddhanta == AryaSiddhanta.SOLAR) ? HinduMonth.ofSolar(month) : HinduMonth.ofLunisolar(month));
return HinduCalendar.of(aryaSiddhanta, year, m, dayOfMonth);

}

Expand Down Expand Up @@ -222,11 +220,16 @@ public static HinduCalendar of(
HinduMonth month,
int dayOfMonth
) {
// TODO: validation of date components

HinduVariant variant =
(aryaSiddhanta == AryaSiddhanta.SOLAR) ? HinduVariant.VAR_OLD_SOLAR : HinduVariant.VAR_OLD_LUNAR;
return new HinduCalendar(variant, year, month, HinduDay.valueOf(dayOfMonth));
HinduDay dom = HinduDay.valueOf(dayOfMonth);
HinduCS calsys = aryaSiddhanta.getCalendarSystem();

if (calsys.isValid(year, month, dom)) {
return calsys.create(year, month, dom);
} else {
throw new IllegalArgumentException(
"Invalid values: " + aryaSiddhanta.getVariant() + "/" + year + "/" + month + "/" + dayOfMonth);
}

}

Expand Down Expand Up @@ -371,15 +374,22 @@ public String toString() {
return sb.toString();
}

@Override
public long getDaysSinceEpochUTC() {
return this.utcDays;
}

/**
* <p>Obtains the standard week model of this calendar. </p>
*
* @return Weekmodel
* @see IndianCalendar#getDefaultWeekmodel()
*/
/*[deutsch]
* <p>Ermittelt das Standardwochenmodell dieses Kalenders. </p>
*
* @return Weekmodel
* @see IndianCalendar#getDefaultWeekmodel()
*/
public static Weekmodel getDefaultWeekmodel() {

Expand Down Expand Up @@ -423,7 +433,8 @@ int getExpiredYearOfKaliYuga() {
/**
* @serialData Uses <a href="../../../../serialized-form.html#net.time4j.calendar.hindu/SPX">
* a dedicated serialization form</a> as proxy. The first byte contains
* the type-ID {@code 20}. Then the variant is written as UTF-String.
* the type-ID {@code 20}. Then the variant is written as UTF-String and finally
* the days since UTC-epoch as long-primitive.
*
* @return replacement object in serialization graph
*/
Expand All @@ -448,19 +459,19 @@ private void readObject(ObjectInputStream in)
//~ Innere Klassen ----------------------------------------------------

private static class VariantMap
extends ConcurrentHashMap<String, CalendarSystem<HinduCalendar>> {
extends ConcurrentHashMap<String, HinduCS> {

//~ Methoden ------------------------------------------------------

@Override
public CalendarSystem<HinduCalendar> get(Object key) {
public HinduCS get(Object key) {

CalendarSystem<HinduCalendar> calsys = super.get(key);
HinduCS calsys = super.get(key);

if (calsys == null) {
String variant = key.toString();
calsys = HinduVariant.from(variant).getCalendarSystem();
CalendarSystem<HinduCalendar> old = this.putIfAbsent(variant, calsys);
HinduCS old = this.putIfAbsent(variant, calsys);

if (old != null) {
calsys = old;
Expand Down

0 comments on commit a3d0153

Please sign in to comment.