Skip to content

Instantly share code, notes, and snippets.

@marvi
Created August 24, 2011 15:00
Show Gist options
  • Select an option

  • Save marvi/1168255 to your computer and use it in GitHub Desktop.

Select an option

Save marvi/1168255 to your computer and use it in GitHub Desktop.

Revisions

  1. marvi created this gist Aug 24, 2011.
    313 changes: 313 additions & 0 deletions LiturgicalYear.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,313 @@
    package lectio.cal;

    import java.io.Serializable;
    import java.util.Date;
    import java.util.Map;
    import java.util.Set;
    import java.util.SortedMap;
    import java.util.TreeMap;
    import org.joda.time.DateTimeConstants;
    import org.joda.time.LocalDate;

    /**
    * Represents a Liturgical year in the Church of Sweden tradition.<br>
    *
    * Note that calendar year and liturgical year doesn't align. The liturgical
    * year starts first Sunday of Advent the previous calendar year. <br>
    * The year used in this class is the year of the Easter Sunday of this liturgical year.
    * I.e. the liturgical year 2013 starts 2012-12-02.<br>
    *
    * Inspiration from http://www.lysator.liu.se/alma/alma.cgi
    *
    * http://www.svenskakyrkan.se/tcrot/kyrkoordningen/filer/KO-kap-28.pdf
    *
    * @author marvi
    */
    public class LiturgicalYear implements Serializable {

    /**
    * The year this liturgical years Easter Sunday occurs.
    */
    private final int year;
    private LocalDate easterDay;
    private int readingCycle;
    private SortedMap<LocalDate, Day> daysOfYear = new TreeMap<LocalDate, Day>();
    private final String[] swedishCounting = new String[] {"Första", "Andra", "Tredje", "Fjärde", "Femte",
    "Sjätte", "Sjunde", "Åttonde", "Nionde", "Tionde", "Elfte", "Tolfte", "Trettonde",
    "Fjortonde", "Femtonde", "Sextonde", "Sjuttonde", "Artonde", "Nittonde", "Tjugonde",
    "Tjugoförsta", "Tjugoandra", "Tjugotredje", "Tjugofjärde", "Tjugofemte", "Tjugosjätte",
    "Tjugosjunde"};

    /**
    *
    * @param year The year to use.
    */
    public LiturgicalYear(int year) {
    this.year = year;
    this.readingCycle = this.getReadingCycle();
    easterDay = CalculateEaster.forYear(year);
    populateDaysOfYear();
    }

    /**
    * Default constructor. Uses current year.
    */
    public LiturgicalYear() {
    this.year = new LocalDate(new Date()).getYear();
    easterDay = CalculateEaster.forYear(year);
    populateDaysOfYear();
    }

    /**
    * @return the year
    */
    public int getYear() {
    return year;
    }

    /**
    *
    * @return the reading cycle for this liturgical year (1-3)
    */
    public final int getReadingCycle() {
    return getReadingCycle(year);
    }

    public final int getEasterSeries() {
    return getEasterSeries(year);
    }

    public static int getReadingCycle(int year) {
    if (year < 1986) {
    return 0;
    } else if (year < 2003) {
    return (year - 1986) % 3 + 1;
    }
    return (year - 2003) % 3 + 1;
    }

    public static int getEasterSeries(int year) {
    if (year < 2004) {
    return 0;
    }
    return (year - 2002) % 4 + 1;
    }


    /**
    * Maps the holidays to actual dates for this year.
    */
    private void populateDaysOfYear() {

    // Fixed days
    daysOfYear.put(new LocalDate(year - 1, 12, 24), new Day("Julnatten", new LocalDate(year - 1, 12, 24), readingCycle));
    daysOfYear.put(new LocalDate(year - 1, 12, 25), new Day("Juldagen", new LocalDate(year - 1, 12, 25), readingCycle));
    daysOfYear.put(new LocalDate(year - 1, 12, 26), new Day("Annandag jul", new LocalDate(year - 1, 12, 26), readingCycle));
    daysOfYear.put(new LocalDate(year - 1, 12, 31), new Day("Nyårsafton", new LocalDate(year - 1, 12, 31), readingCycle));
    daysOfYear.put(new LocalDate(year, 1, 1), new Day("Nyårsdagen", new LocalDate(year, 1, 1), readingCycle));
    daysOfYear.put(new LocalDate(year, 1, 5), new Day("Trettondagsafton", new LocalDate(year, 1, 5), readingCycle));
    daysOfYear.put(new LocalDate(year, 1, 6), new Day("Trettondedag jul", new LocalDate(year, 1, 6), readingCycle));

    // Söndagen efter jul infaller inte varje år.
    LocalDate sej = nextWeekdayOfType(DateTimeConstants.SUNDAY, new LocalDate((year - 1), 12, 26));
    if (sej.isBefore(new LocalDate(year, 1, 1))) {
    daysOfYear.put(sej, new Day("Söndagen efter jul", sej, readingCycle));
    }

    // "Söndagen efter nyår" infaller inte varje år.
    if (sundayAfterNewYear() != null) {
    daysOfYear.put(sundayAfterNewYear(), new Day("Söndagen efter nyår", sundayAfterNewYear(), readingCycle));
    }

    // Kyndelsmässodagen (flyttas tillbaka en vecka om den infaller på fastlagssöndagen
    LocalDate kynd = nextWeekdayOfType(DateTimeConstants.SUNDAY, new LocalDate(year, 2, 1));
    if (kynd.equals(easterDay.minusDays(49))) {
    kynd = kynd.minusDays(7);
    }
    daysOfYear.put(kynd, new Day("Kyndelsmässodagen", kynd, readingCycle));

    // Söndagar efter trettondedagen
    int add = 0;
    int i = 0;
    while (i < 8) {
    LocalDate d = firstAfterEpifania().plusDays(add);
    if (!d.isEqual(kynd) && d.isBefore(easterDay.minusDays(63))) {
    String prefix = swedishCounting[i];
    daysOfYear.put(d, new Day(prefix + " söndagen efter trettondedagen", d, readingCycle));
    } else if (d.isEqual(easterDay.minusDays(63))
    || d.isAfter(easterDay.minusDays(63))) {
    i = 8;
    }
    add = add + 7;
    i++;
    }

    // Jungfru Marie bebådelsedag
    LocalDate jmb = nextWeekdayOfType(DateTimeConstants.SUNDAY, new LocalDate(year, 3, 21));
    if (jmb.isAfter(easterDay.minusDays(8))) {
    jmb = easterDay.minusDays(14);
    }
    daysOfYear.put(jmb, new Day("Jungfru Marie bebådelsedag", jmb, readingCycle));
    //System.out.println(daysOfYear.get(jmb) + " " + easterDay.minusDays(42));
    if (!kynd.isEqual(easterDay.minusDays(63))) {
    daysOfYear.put(easterDay.minusDays(63), new Day("Septuagesima", easterDay.minusDays(63), readingCycle));
    }
    if (!kynd.isEqual(easterDay.minusDays(56))) {
    daysOfYear.put(easterDay.minusDays(56), new Day("Sexagesima", easterDay.minusDays(56), readingCycle));
    }
    daysOfYear.put(easterDay.minusDays(49), new Day("Fastlagssöndagen", easterDay.minusDays(49), readingCycle));
    daysOfYear.put(easterDay.minusDays(46), new Day("Askonsdagen", easterDay.minusDays(46), readingCycle));
    if (!jmb.isEqual(easterDay.minusDays(42))) {
    daysOfYear.put(easterDay.minusDays(42), new Day("Första söndagen i fastan", easterDay.minusDays(42), readingCycle));
    }
    if (!jmb.isEqual(easterDay.minusDays(35))) {
    daysOfYear.put(easterDay.minusDays(35), new Day("Andra söndagen i fastan", easterDay.minusDays(35), readingCycle));
    }
    if (!jmb.isEqual(easterDay.minusDays(28))) {
    daysOfYear.put(easterDay.minusDays(28), new Day("Tredje söndagen i fastan", easterDay.minusDays(28), readingCycle));
    }
    if (!jmb.isEqual(easterDay.minusDays(21))) {
    daysOfYear.put(easterDay.minusDays(21), new Day("Midfastosöndagen", easterDay.minusDays(21), readingCycle));
    }
    if (!jmb.isEqual(easterDay.minusDays(14))) {
    daysOfYear.put(easterDay.minusDays(14), new Day("Femte söndagen i fastan", easterDay.minusDays(14), readingCycle));
    }
    // Utgår från påsken
    daysOfYear.put(easterDay, new Day("Påskdagen", easterDay, readingCycle));
    daysOfYear.put(easterDay.minusDays(1), new Day("Påsknatten", easterDay.minusDays(1), readingCycle));
    daysOfYear.put(easterDay.minusDays(2), new Day("Långfredagen", easterDay.minusDays(2), readingCycle));
    daysOfYear.put(easterDay.minusDays(3), new Day("Skärtorsdagen", easterDay.minusDays(3), readingCycle));
    daysOfYear.put(easterDay.minusDays(4), new Day("Onsdag i Stilla veckan", easterDay.minusDays(4), readingCycle));
    daysOfYear.put(easterDay.minusDays(5), new Day("Tisdag i Stilla veckan", easterDay.minusDays(5), readingCycle));
    daysOfYear.put(easterDay.minusDays(6), new Day("Måndag i Stilla veckan", easterDay.minusDays(6), readingCycle));
    daysOfYear.put(easterDay.minusDays(7), new Day("Palmsöndagen", easterDay.minusDays(7), readingCycle));
    daysOfYear.put(easterDay.plusDays(1), new Day("Annandag påsk", easterDay.plusDays(1), readingCycle));
    daysOfYear.put(easterDay.plusDays(7), new Day("Andra söndagen i påsktiden", easterDay.plusDays(7), readingCycle));
    daysOfYear.put(easterDay.plusDays(14), new Day("Tredje söndagen i påsktiden", easterDay.plusDays(14), readingCycle));
    daysOfYear.put(easterDay.plusDays(21), new Day("Fjärde söndagen i påsktiden", easterDay.plusDays(21), readingCycle));
    daysOfYear.put(easterDay.plusDays(28), new Day("Femte söndagen i påsktiden", easterDay.plusDays(28), readingCycle));
    daysOfYear.put(easterDay.plusDays(35), new Day("Bönsöndagen", easterDay.plusDays(35), readingCycle));
    daysOfYear.put(easterDay.plusDays(39), new Day("Kristi himmelsfärds dag", easterDay.plusDays(39), readingCycle));
    daysOfYear.put(easterDay.plusDays(42), new Day("Söndagen före pingst", easterDay.plusDays(42), readingCycle));
    daysOfYear.put(easterDay.plusDays(49), new Day("Pingstdagen", easterDay.plusDays(49), readingCycle));
    daysOfYear.put(easterDay.plusDays(50), new Day("Annandag pingst", easterDay.plusDays(50), readingCycle));
    daysOfYear.put(easterDay.plusDays(56), new Day("Heliga trefaldighets dag", easterDay.plusDays(56), readingCycle));

    // Midsommardagen
    LocalDate msd = nextWeekdayOfType(DateTimeConstants.SATURDAY, new LocalDate(year, 6, 19));
    daysOfYear.put(msd, new Day("Midsommardagen", msd, readingCycle));


    // Alla helons dag (behövs för att lägga ut trefaldighetstiden)
    LocalDate ahd = nextWeekdayOfType(DateTimeConstants.SATURDAY, new LocalDate(year, 10, 30));
    daysOfYear.put(ahd, new Day("Alla helgons dag", ahd, readingCycle));
    daysOfYear.put(ahd.plusDays(1), new Day("Söndagen efter alla helgons dag", ahd.plusDays(1), readingCycle));

    // Advent och kyrkoårets slut
    LocalDate firstAdv = nextWeekdayOfType(DateTimeConstants.SUNDAY, new LocalDate((year - 1), 11, 26));
    daysOfYear.put(firstAdv, new Day("Första söndagen i advent", firstAdv, readingCycle));
    daysOfYear.put(firstAdv.plusDays(7), new Day("Andra söndagen i advent", firstAdv.plusDays(7), readingCycle));
    daysOfYear.put(firstAdv.plusDays(14), new Day("Tredje söndagen i advent", firstAdv.plusDays(14), readingCycle));
    daysOfYear.put(firstAdv.plusDays(21), new Day("Fjärde söndagen i advent", firstAdv.plusDays(21), readingCycle));

    // Kalenderårsadvent (för att räkna ut Domsöndag
    LocalDate calFirstAdv = nextWeekdayOfType(DateTimeConstants.SUNDAY, new LocalDate((year), 11, 26));
    daysOfYear.put(calFirstAdv.minusDays(7), new Day("Domsöndagen", calFirstAdv.minusDays(7), readingCycle));
    daysOfYear.put(calFirstAdv.minusDays(14), new Day("Söndagen före domsöndagen", calFirstAdv.minusDays(7), readingCycle));

    // Trefaldighetssöndagarna
    // Söndagar efter trettondedagen
    LocalDate firstAfterTre = easterDay.plusDays(63);
    add = 0;
    i = 0;
    while (i < 27) {
    LocalDate setre = firstAfterTre.plusDays(add);
    add = add + 7;
    if (setre.isEqual(ahd.plusDays(1))) {
    i++;
    continue;
    }
    if (setre.isAfter(calFirstAdv.minusDays(15))) {
    break;
    }
    String etreName = swedishCounting[i];
    switch (i) {
    case 4:
    daysOfYear.put(setre, new Day("Apostladagen", setre, readingCycle));
    break;
    case 6:
    daysOfYear.put(setre, new Day("Kristi förklarings dag", setre, readingCycle));
    break;
    default:
    daysOfYear.put(setre, new Day(etreName + " söndagen efter trefaldighet", setre, readingCycle));
    break;
    }
    i++;
    }

    // Johannes döparens dag
    daysOfYear.put(msd.plusDays(1), new Day("Den helige Johannes Döparens dag", msd.plusDays(1), readingCycle));

    // Den helige Mikaels dag
    LocalDate hmd = nextWeekdayOfType(DateTimeConstants.SUNDAY, new LocalDate(year, 9, 28));
    daysOfYear.put(hmd, new Day("Den helige Mikaels dag", hmd, readingCycle));

    // Tacksägelsedagen
    LocalDate tsd = nextWeekdayOfType(DateTimeConstants.SUNDAY, new LocalDate(year, 9, 30));
    tsd = nextWeekdayOfType(DateTimeConstants.SUNDAY, tsd);
    daysOfYear.put(tsd, new Day("Tacksägelsedagen", tsd, readingCycle));


    }

    private LocalDate sundayAfterNewYear() {
    LocalDate candidate = nextWeekdayOfType(DateTimeConstants.SUNDAY, new LocalDate(year, 1, 1));
    if (candidate.getDayOfMonth() < 6) {
    return candidate;
    }
    return null;
    }

    private LocalDate firstAfterEpifania() {
    LocalDate ep = new LocalDate(year, 1, 6);
    return nextWeekdayOfType(DateTimeConstants.SUNDAY, ep);
    }

    /**
    *
    * @param wd The day of week
    * @param date The date to start from
    * @return The next date with this weekday
    */
    protected static LocalDate nextWeekdayOfType(int wd, LocalDate date) {
    if (date.getDayOfWeek() < wd) {
    date = date.withDayOfWeek(wd);
    } else {
    date = date.plusWeeks(1).withDayOfWeek(wd);
    }
    return date;
    }

    /**
    * @return a map with a date as key and the Day as value.
    */
    public Map<LocalDate, Day> getDaysOfYear() {
    return daysOfYear;
    }

    @Override
    public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("LiturgicalYear{ year=");
    sb.append(year);
    sb.append(" ");
    Set<LocalDate> days = daysOfYear.keySet();
    for (LocalDate ld : days) {
    sb.append(daysOfYear.get(ld).toString());
    sb.append(" \n ");
    }
    sb.append(" }");
    return sb.toString();
    }

    }