Euromind
  • Javascript
    • Javascript

      Historiske administrative geografier i Google Maps

      20. april, 2022

      Javascript

      Kierkegaard injiceret med Javascript

      15. april, 2022

      Javascript

      Dansk evighedskalender

      7. december, 2020

      Javascript

      API til Statistikbanken

      21. september, 2019

      Javascript

      IntersectionObserver

      9. august, 2019

  • CSS/SCSS
    • CSS/SCSS

      Kierkegaard injiceret med Javascript

      15. april, 2022

      CSS/SCSS

      Dansk evighedskalender

      7. december, 2020

      CSS/SCSS

      Variable fonte med dansk tegnsæt i open source

      11. august, 2019

      CSS/SCSS

      Progressbar for dokumentposition

      31. juli, 2019

      CSS/SCSS

      Media Query i 2019

      18. juli, 2019

  • C#
    • C#

      Historiske administrative geografier i Google Maps

      20. april, 2022

      C#

      Authentication for IOS og Android med Firebase i…

      4. oktober, 2019

      C#

      Andersen, Grundvig, Kierkegaard og ML.NET – del 3

      5. september, 2019

      C#

      Hurtig eksport til Excel

      4. september, 2019

      C#

      Andersen, Grundtvig, Kierkegaard og ML.NET – del 2

      2. september, 2019

  • Javascript
    • Javascript

      Historiske administrative geografier i Google Maps

      20. april, 2022

      Javascript

      Kierkegaard injiceret med Javascript

      15. april, 2022

      Javascript

      Dansk evighedskalender

      7. december, 2020

      Javascript

      API til Statistikbanken

      21. september, 2019

      Javascript

      IntersectionObserver

      9. august, 2019

  • CSS/SCSS
    • CSS/SCSS

      Kierkegaard injiceret med Javascript

      15. april, 2022

      CSS/SCSS

      Dansk evighedskalender

      7. december, 2020

      CSS/SCSS

      Variable fonte med dansk tegnsæt i open source

      11. august, 2019

      CSS/SCSS

      Progressbar for dokumentposition

      31. juli, 2019

      CSS/SCSS

      Media Query i 2019

      18. juli, 2019

  • C#
    • C#

      Historiske administrative geografier i Google Maps

      20. april, 2022

      C#

      Authentication for IOS og Android med Firebase i…

      4. oktober, 2019

      C#

      Andersen, Grundvig, Kierkegaard og ML.NET – del 3

      5. september, 2019

      C#

      Hurtig eksport til Excel

      4. september, 2019

      C#

      Andersen, Grundtvig, Kierkegaard og ML.NET – del 2

      2. september, 2019

Euromind
CSS/SCSSHTMLJavascriptWebdesign

Dansk evighedskalender

af Per Lindsø Larsen 7. december, 2020
skrevet af Per Lindsø Larsen 7. december, 2020
Dansk evighedskalender

Som B.S. Ingemann skrev i 1850, så er tider noget, der kommer og henruller. Det gælder derfor om at få sig en evighedskalender, og det kan ikke gå for hurtigt. Så dagens projekt er altså som følger:

En dynamisk kalender, der strækker sig tilbage til Ruder Konges tid og kører frem til solen brænder ud. Eller sådan cirka. Den skal vise ugedag, ugenumre, forskydelige og faste helligdage og mærkedage og vise symboler for månefaser (fuldmåne, nymåne og aftagende/tiltagende halvmåne). Vi laver den responsivt så der vises 12 måneds-kolonner på større desktopskærme, 2 x 6 månedskolonner på mediumskærme og 4 x 3 månedskolonner på mobil. Det hele laves i ren javascript, css og html. Back to basic: ingen jQuery, Bootstrap, Vue el.lign frameworks.

Det færdige resultat af dagens øvelse kan ses her:

Dansk evighedskalender

eller med fuld kildekode på Codepen

Indledende om fup eller fakta

Enhver idiot kan komme og påstå, at det var fuldmåne den 25. maj 1567 eller at påskedag faldt den 29. marts 1812. Der er sjældent nogen, der står i kø for at protestere. Her er derfor nogle autoritative kilder, så vi kan kontrollere, at den dynamiske kalender, som vi laver, viser troværdige data:

R. W. Bauer: Calender for Aarene fra 601 til 2200 efter Christi Fødsel. En klassiker og kalender-bibel. Den findes i adskillige versioner på internettet og kan bruge til at kontrollere, om vi har fat i de rigtige datoer for påskedag og andre helligdage i både den julianske og gregorianske kalendertid:

Calender for Aarene fra 601 til 2200 efter Christi Fødsel

Og til kontrol af månefaser kan f.eks. anvendes:

Timeanddate.com
Astropixels.com

Hvem kontrollerer så dem, der kontrollerer? I Bauers kalender fremgår det f.eks. at Marie Bebudelsesdag altid falder den 25. marts (hvilket uheldigvis også er den viden man får, når man googler dagen). Men ved helligdagsreformen i 1770 blev Marie Bebudelsesdag i Danmark flyttet til søndagen før palmesøndag, og varierer altså fra år til år. Det lyder måske nørdet, men det har den vigtige lære, at man aldrig skal stole blindt på autoriteter. Tilsvarende kløjes timeanddate.com i fastlæggelse af månefaser før 1700, hvilket de dog også selv beskriver. Her må astropixels.com nok anses som mere troværdig.

Nå, men lad os komme i gang.

Ugedage

For at finde det rigtige ugedags-bogstav for de enkelte datoer i året kan f.eks. findes ugedagen for 1. januar i året og så blot loope gennem året og frem til 31. december. Javascripts Date-objekt er med en udsøgt logik bygget, så januar har værdien 0. Den returnerede værdi for ugedag er fra 0=søndag til 6 = lørdag. At finde ugedagen for f.eks. 1. januar 2020 vil blive

new Date(2020,0,1).getDay();   // returnerer 3 = onsdag

Før der loopes igennem året er der lige et par ting at ordne. For det første at udrede om året er skudår. Det er tilfældet, hvis årstallet kan deles med 4, dog ikke, hvis det kan deles med 100, bortset fra hvis det kan deles med 400. Det er nemmere at forstå i javascript:

let leapYear = function(year) {
    return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
}

I så tilfælde skal antallet af dage i februar jo ændres fra 28 til 29. Endelig om det valgte år er før eller efter 1700. Den 1. marts 1700 overgik vi fra den julianske til den gregorianske kalender, hvilket indebar at den 18. februar blev efterfulgt af den 1. marts. Tiden imellem de to datoer er altså en spøgelsesperiode, der aldrig har eksisteret.
Det har javascripts Date-objektet naturligvis ikke indbygget viden om:

new Date(1700,2,1).getDay();   // returnerer 1 = mandag - Rigtigt
new Date(1700,1,18).getDay(); // returnerer 4 = torsdag  - Forkert, skulle være 0=søndag 

Der findes måder at beregne sig frem til ugedag i den julianske kalender, f.eks:

let JulianWeekDay = function(y, m, d) {
    if (y < 0) ++y;
    if (m > 2) m = m + 1;
    else { --y; m = m + 13; }
    jul = Math.floor(365.25 * y) + Math.floor(30.6001 * m) + d + 1720995;
    return Math.round((jul + 1) % 7);
}

Men hvorfor gøre tingene mere komplicerede end de er? Med viden om at Date-objektet for datoer før 1. marts 1700 returnerer 4 for meget, kan vi jo blot trække dem fra igen, så vi for kalenderår til og med år 1700 ikke beder om ugedagen for 1. januar, men ugedagen for 28. december året før. Der kan nu laves en funktion, der altid vil give den korrekte ugedag for første dag i et given år:

let firstDayInYear = function(year){
    if (year > 1700) {
        return new Date(year, 0, 1).getDay(); //Find ugedag 1. januar
    }
    else {
        return new Date(year-1, 11, 28).getDay(); //Hvis 1700 eller ældre: træk fire dage fra for korrekt ugedag   
    }
}

Ugenr

Uge 1 i et år er i følge ISO-8601. der benyttes i Danmark, den første uge, der indeholder en torsdag. Der er mange måder i javascript til at finde ugenummeret på, men jeg har anvendt følgende og tilføjet det som extension til Date-objektet:

 Date.prototype.getWeek = function() {
    var date = new Date(this.getTime());
    date.setHours(0, 0, 0, 0);
    date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
    var week1 = new Date(date.getFullYear(), 0, 4);
    return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000
                          - 3 + (week1.getDay() + 6) % 7) / 7);
  }

Nu har vi ugedage og ugenumre på plads, og via css sørger vi for at gråtone søndage og delvis gråtone lørdage, så det ligner en rigtig Mayland-kalender. Lad os tage den et skridt videre og få nogle symboler ind for datoer med fuldmåne, nymåne med videre.

Månefaser

Jeg troede at denne del af opgaven var ligetil. Ifølge min indskrænkede astronomiske viden er månens cyklus fra fuldmåne til fuldmåne 29.5 dage og at jeg håbede derfor – med udgangspunkt i en kendt fuldmånedato – at kunne loope frem og tilbage i tiden, på samme måde som med ugedage. Jeg lærte hurtigt at der blot er tale om et gennemsnit og at perioden kan variere en del. I en påstået evighedskalender vil det give utroværdige udsving, der ikke tåler kontrol af mere autoritative kilder.

Lidt research ledte mig frem til Jean Meeus bog “Astronomical Algorithms“ fra 1991, der synes at være værket over dem alle, men allerede på ca. side 30, måtte jeg indse, at jeg ikke fattede en bønne. Amatørastronom bliver jeg aldrig. Heldigvis er der skarpere knive i skuffen, der har porteret Meeus formler til javascript: https://github.com/Fabiz/MeeusJs

Med dette herlige værktøj (<23 kB) kan månens fase på en given dag hentes med:

let moonPhase = function (date) {
    var jdo = new A.JulianDay(date);
    var coord = A.EclCoord.fromWgs84(55.719437, 13.197304, 50); //København
    var suntp = A.Solar.topocentricPosition(jdo, coord, true);
    var moontp = A.Moon.topocentricPosition(jdo, coord, true);
    var i = A.MoonIllum.phaseAngleEq2(moontp.eq, suntp.eq);
    return A.MoonIllum.illuminated(i);
}

Som angivelse af månefaser i kalenderen anvendes de fire Unicode-symboler: 🌕 🌗 🌑 🌓 .

Påskedag og andre helligdage

Påske er den vigtigste højtid i kristendommen, og dermed er påskedag også den vigtigste dato at få fastlagt i kalender-sammenhæng, da en lang række andre helligdage og mærkedage beregnes ud fra påskedag. Det gælder f.eks. kyndelmisse, fastelavn, palmesøndag, skærtorsdag, langfredag, pinse m.fl.

Som det er tilfældet med den islamiske ramadan, så fastlægges den kristne påske på baggrund af fuldmåne, idet påskesøndag er den første søndag efter første fuldmåne efter 21. marts. Desværre gælder det for påske som for ramadan, at det ikke nødvendigvis er den astronomiske fuldmåne, der gælder. For de, der måtte interessere sig for datoberegning af påsken, vil jeg henvise til Claus Tønderings fantastiske Calendar FAQ og til Wikipedias magelige trin-for-trin gennemgang af beregning af dato for påskedag. I javascript vil beregningen være som følger:

//Se Wikipedia: Beregning af datoen for påskedag
// https://da.wikipedia.org/wiki/P%C3%A5ske#Beregning_af_datoen_for_p%C3%A5skedag
function GregorianEaster(y) {
    Y = parseInt(y);
    var C = Math.floor(Y / 100);
    var N = Y - 19 * Math.floor(Y / 19);
    var K = Math.floor((C - 17) / 25);
    var I = C - Math.floor(C / 4) - Math.floor((C - K) / 3) + 19 * N + 15;
    I = I - 30 * Math.floor((I / 30));
    I = I - Math.floor(I / 28) * (1 - Math.floor(I / 28) * Math.floor(29 / (I + 1)) * Math.floor((21 - N) / 11));
    var J = Y + Math.floor(Y / 4) + I + 2 - C + Math.floor(C / 4);
    J = J - 7 * Math.floor(J / 7);
    var L = I - J;
    var M = 3 + Math.floor((L + 40) / 44);
    var D = Math.floor(L + 28 - 31 * Math.floor(M / 4));
    if (y == 1744) return Y + "-" + padout(3) + "-" + padout(29);
    return Y + "-" + padout(M) + "-" + padout(D); // 12-04-2020
}

Den gælder imidlertid kun for den gregorianske kalender, så for tiden før 1700 har jeg derfor tyet til udvalgt kode fra J. R. Stocktons “The Calculation of Easter Sunday” der er gyldig for den julianske kalender:

// JulianEaster baeret på J R Stocktons "The Calculation of Easter Sunday":	
// https://people.cs.nctu.edu.tw/~tsaiwn/sisc/runtime_error_200_div_by_0/www.merlyn.demon.co.uk/estr-bcp.htm

function JulianEaster(Y) {
    Y = parseInt(Y);
    function GoldenNumber(Yr) { return Mod(Yr, 19) + 1 }
    function Mod(X, Y) { return X - Math.floor(X / Y) * Y }
    function GNtoJulianPFM(GN) { // By analogy with Gregorian data
        // Golden Number to PFM as Day-of-March, valid perpetually
        var T = 21 + (GN * 19 - 4) % 30; return T
    }
    function Div(X, Y) { return Math.floor(X / Y) /* full range */ }
    function JulianSundayNumber(Y) {
        var Z = 4 + Y + Div(Y, 4)
        return 6 - Mod(Z, 7) /* 0-6 matches Sunday Letter A-G */
    }
    function JPFMtoDate2(Year, DM) {
        var SN // Ahead to Julian Sunday
        SN = JulianSundayNumber(Year)
        return YDoMtoYMonDD(Year, SundayAfterPFM(DM, SN))
    }
    function YDoMtoYMonDD(Y, DM) { return Y + DoMtoMonDD(DM) }
    function DoMtoMonDD(DM) {
        return DM > 31 ? "-04-" + LZ(DM - 31) : "-03-" + DM
    }
    function LZ(n) { return (n != null && n < 10 && n >= 0 ? "0" : "") + n }
    function SundayAfterPFM(DM, SN) { // plus 1..7 days to Sunday, J & G
        return DM + 1 + (60 + SN - DM) % 7 /* Day-of-March */
    }
    Y = parseInt(Y);
    var GN = GoldenNumber(Y)
    var PFM = GNtoJulianPFM(GN)
    return JPFMtoDate2(Y, PFM)
}

Så for at finde påskedag for et givent år, hvad enten det er julianske eller gregoriansk kalendertid, vil det altså være:

function Easter(y) {
    if (y >= 1700) {
        return GregorianEaster(y); //Gregorianske kalender indført 1.marts 1700 (altså før påskedag i året)
    } else {
        return JulianEaster(y); //Find påske efter juliansk kalender indtil 1. marts 1700 
    }
}

Nu er der ingen regel uden undtagelser, og det gælder også i dette tilfælde. I år 1744 eksperimenterede vi i Danmark med at lade beregningen af påskedag bero på den astronomiske fuldmåne. Den faldt i året på lørdag den 28. marts og påskesøndag blev derfor søndag den 29. marts. I Danmark var der altså i 1744 påske en uge tidligere end i andre nordeuropæiske lande. Det var i længden noget rod, og eksperimentet kom kun til at have effekt i dette år. Men det er nok til at vi må lave en undtagelse, der er indsat som næstsidste linje i funktionen GregorianEaster:    

if (y == 1744) return Y + "-" + padout(3) + "-" + padout(29); 

Med dateringen for påskedag på plads, kan vi fastlægge et stort antal andre helligdage og mærkedage. Jeg har valgt at indsætte dem i et JSON-array med formatet:

 { 
"danish": "Pinsedag",  // Dansk tekst for dagen
"latin": "Pentecoste",  // Latinsk tekst for dagen - for historikere og slægtsforskere 
"holliday": true,  // Skal dagen gråtones som helligdag?
"primary": true,  // Skal dagen medtages i den minimalistiske kalender
"easterDiffDays": 49  // Er dagen afhængig af påskedag, da med hvor mange dage?
}

Herefter er der kun at køre gennem kalenderårets dage og indsætte de nødvendige tekster og symboler.

Plads til forbedring

Den dynamiske evighedskalender viste sig at være lidt mere kompliceret, end jeg havde forudset, og da min personlige arbejdskalender desværre ikke er så responsiv som denne kalender , så måtte jeg adskillige steder springe over, hvor gærdet er lavest:

Jeg nåede ikke at få meeus.js til at fungere for den gregorianske kalender og tyede derfor til loon.js (https://github.com/ryanseys) for månefaser efter 1700. Der må helt klart være plads til forbedringer her.

At udrede helligdage på en historisk korrekt måde er et større studie, der falder uden for rammerne for denne blogpost. F.eks. kom Store Bededag først til verden med helligdagsreformen i 1770 og det giver derfor ikke mening at anføre den i kalenderår tidligere end dette. Omvendt afløste den en række andre helligdage, som ikke er medtaget her.

Endelig er det ikke mig, der er Kalendervender Tage, og sikkert er der noget i kalenderhistorikken, som jeg har overset eller misforstået.

Så der er alt i alt rigeligt at arbejde videre med. God fornøjelse.

4 kommentarer
3
FacebookTwitterPinterestEmail
forrige post
Automatisk køreafstand i Excel
næste post
Kierkegaard injiceret med Javascript

Relaterede indlæg

Historiske administrative geografier i Google Maps

20. april, 2022

Kierkegaard injiceret med Javascript

15. april, 2022

Headless browser på 10 minutter

25. juli, 2020

API til Statistikbanken

21. september, 2019

Variable fonte med dansk tegnsæt i open source

11. august, 2019

IntersectionObserver

9. august, 2019

Progressbar for dokumentposition

31. juli, 2019

Navngivne RegEx-grupper i ECMAScript 2018

29. juli, 2019

RegEx: Unicode og Look Backward i ECMAScript 2018

24. juli, 2019

Media Query i 2019

18. juli, 2019

4 kommentarer

Bongobib 25. september, 2021 - 4:06 am

Visse år, hvor mange eller hvilke ukendt men feks 1930 eller 1931, indeholder tilsyneladende ingen december

Svar
Per Lindsø Larsen 25. september, 2021 - 2:10 pm

Hej.
Tak for opmærksomheden. Fejlen skulle være rettet nu. Ellers giv lyd igen.

Svar
Bongobib 9. januar, 2022 - 11:55 pm

Systematikken mht benævnelserne ‘1 Søndag før/efter xxxx’ synes inkonsekvent. Hvis feks 6. Jan er Hellig trekongers aften må næstfølgende søndag jo være 1. s e Hellig trekongers aften, også i de tilfælde hvor Hellig trekongers aften falder på en søndag

Svar
Per Lindsø Larsen 1. februar, 2022 - 1:41 am

Så skulle inkonsekvensen være rettet konsekvent.

Svar

Efterlad en kommentar Afbryd svar

Gem mit navn, email, og website i denne browser til senere kommentarer.

Seneste indlæg

  • Historiske administrative geografier i Google Maps

    20. april, 2022
  • Kierkegaard injiceret med Javascript

    15. april, 2022
  • Dansk evighedskalender

    7. december, 2020

Kategorier

  • C#
  • CSS/SCSS
  • Excel
  • HTML
  • Javascript
  • Mobile
  • Webdesign
  • Xamarin

Om mig

Om mig

Per Lindsø Larsen

Freelance fullstack developer bosat i Aarhus.

Du kan hyre mig til korterevarende projekter eller konkrete opgaveløsninger.

Pæn rabat til non-profit organisationer og foreninger.

Når jeg ikke koder, deltager jeg løbende i diverse spændende forskningsprojekter om alt andet end kodning.

Keep in touch

Facebook Twitter Email Github

Tags

Adresser AMP AMP Story Android API Billedformater Billedoptimering Brand C# Codepen Cordova CPR Crome DevTools CSS Debug Ecmascript Excel Fonte Gmail Gulp HTML Ikoner IOS Javascript JsFiddle Machine Learning Mail Mediaquery ML.NET Mobile RegEx SCSS SMTP Stylometri Visual Studio Webdesign Xamarin

Nyhedsbrev

Timeld nyhedsbrev for info om nye blog-indlæg, tips m.v.

  • Facebook
  • Twitter
  • Email
  • Github

@2019 - Euromind.com - Code-To-Go. All Right Reserved.
lindsoe@gmail.com - mobil: 42797273


Tilbage til top
Euromind
  • Javascript
    • Javascript

      Historiske administrative geografier i Google Maps

      20. april, 2022

      Javascript

      Kierkegaard injiceret med Javascript

      15. april, 2022

      Javascript

      Dansk evighedskalender

      7. december, 2020

      Javascript

      API til Statistikbanken

      21. september, 2019

      Javascript

      IntersectionObserver

      9. august, 2019

  • CSS/SCSS
    • CSS/SCSS

      Kierkegaard injiceret med Javascript

      15. april, 2022

      CSS/SCSS

      Dansk evighedskalender

      7. december, 2020

      CSS/SCSS

      Variable fonte med dansk tegnsæt i open source

      11. august, 2019

      CSS/SCSS

      Progressbar for dokumentposition

      31. juli, 2019

      CSS/SCSS

      Media Query i 2019

      18. juli, 2019

  • C#
    • C#

      Historiske administrative geografier i Google Maps

      20. april, 2022

      C#

      Authentication for IOS og Android med Firebase i…

      4. oktober, 2019

      C#

      Andersen, Grundvig, Kierkegaard og ML.NET – del 3

      5. september, 2019

      C#

      Hurtig eksport til Excel

      4. september, 2019

      C#

      Andersen, Grundtvig, Kierkegaard og ML.NET – del 2

      2. september, 2019

Populære indlæg

  • 1

    Stylometri i C# – del 2

    7. juni, 2019
  • 2

    Andersen, Grundvig, Kierkegaard og ML.NET – del 1

    11. august, 2019
  • 3

    Send email fra Javascript med Gmail API

    21. juni, 2019
  • 4

    Gmail, Yahoo og Outlook som SMTP-server

    18. april, 2019
  • 5

    Registrer Gmail API til brug i javascript

    27. juni, 2019
@2019 - Euromind.com - Code-To-Go. All Right Reserved.
lindsoe@gmail.com - mobil: 42797273

Læs ogsåx

API til Statistikbanken

21. september, 2019

Forbind Visual Studio til IOS devices på 10 minutter

12. juli, 2019

Historiske administrative geografier i Google Maps

20. april, 2022