*.o
*.obj
moc_*
+*.moc
elam/tests/eval/evaltest*
elam/tests/parser/parsertest*
.qmake.stash
doc/chester/
doc/elam/source/
doc/zip-src/
-
+caltest
+caltest.exe
+target_wrapper.sh
--- /dev/null
+#include this into your qmake project file to
+#use the Calendar Tools library
+
+INCLUDEPATH += $$PWD/include/caltools
+LIBS += -lm -L$$PWD/lib -lcaltools
+CONFIG += link_prl
--- /dev/null
+TEMPLATE=subdirs
+SUBDIRS += src tests/easter tests/gregorian tests/lunar tests/solar
--- /dev/null
+// Calculating Easter Dates
+//
+// (c) Konrad Rosenbaum, 2017
+// protected under the GNU LGPL v3 or at your option any newer
+
+#include <easterdate.h>
+
+// Debugger function, make if "#if 1" to enable debugging output
+#if 0
+#include <QDebug>
+#define DD(x) qDebug()<<""#x "="<<x;
+#else
+#define DD(x)
+#endif
+
+// formulas were found at http://www.dateofeaster.com/
+// some more explanations (in German): https://de.wikipedia.org/wiki/Osterzyklus#Die_Mondgleichung
+// and: https://de.wikipedia.org/wiki/Gau%C3%9Fsche_Osterformel
+
+namespace CalTools {
+
+///calculates the date of easter for a specific year
+QDate easterSunday(quint16 year)
+{
+ DD(year);
+ const int c=year/100; // century
+ DD(c);
+ const int q=(c-15)*3/4+10; // gregorian correction
+ DD(q);
+ const int l=7-((year/4+year+4-q)%7); // dominical number
+ DD(l);
+ const int g=year%19+1; // golden number
+ DD(g);
+ const int j=(g*11-10)%30; // julian epact
+ DD(j);
+ const int s=q-10; // solar equation
+ DD(s);
+ const int m=(c-14)*8/25; //lunar equation
+ DD(m);
+ int e=j-s+m;
+ while(e<=0)e+=30;
+ e%=30; // epact
+ DD(e);
+ if(g>11 && e==25)e=26; // second epact 25 rule
+ DD(e);
+ if(e==24)e=25; // epact 24 equals epact 25
+ DD(e);
+ int d;
+ if(e<24)d=44-e; // paschal full moon
+ else d=74-e;
+ DD(d);
+
+ d+= 7 - ((d+10-l)%7); // find sunday following this
+ DD(d);
+ int mo=3; //
+ if(d>61){mo=5;d-=61;} // may
+ else if(d>31){mo=4;d-=31;} // april
+ return QDate(year,mo,d);
+}
+
+///calculates the eastern orthodox date for Easter sunday
+QDate orthodoxEasterSunday(quint16 year)
+{
+ DD(year);
+ const int l=7-(year/4+year+4)%7; //dominical
+ DD(l);
+ const int g=year%19+1; //gregorian
+ DD(g);
+ const int e=(g*11-11)%30; //epact
+ DD(e);
+ const int d=(e>16)?(66-e):(36-e); // paschal
+ DD(d);
+ const int w=(d+3-l)%7; // weekday of paschal
+ DD(w);
+ int d2=d+7-w; // sunday after paschal in julian calendar
+ DD(d2);
+ const int q=(year/100-15)*3/4+10; // gregorian correction
+ DD(q);
+ d2+=q;
+ DD(d2);
+
+ int mo=3;
+ if(d2>61){mo=5;d2-=61;}
+ else if(d2>31){mo=4;d2-=31;}
+ return QDate(year,mo,d2);
+}
+
+//TODO: date of jewish pessach
+
+
+//end of namespace
+}
+
+#undef DD
--- /dev/null
+// Calculating Easter Dates
+//
+// (c) Konrad Rosenbaum, 2017
+// protected under the GNU LGPL v3 or at your option any newer
+
+#include <lunar.h>
+#include <math.h>
+
+namespace CalTools {
+
+/// amount of phases used for internal calculations
+const int LunarPhases_CycleLength=8;
+
+/// average length of a lunar cycle in solar days
+const double LunarCycleLengthInDays=29.530588853l;
+
+/// Julian day of a known new moon
+const int JulianDayJan1st1900=2415021;
+
+///returns the phase of the moon for a specific date
+/// note: this function is quite
+LunarPhase lunarPhaseForDate(QDate d)
+{
+ //calculate where in the cycle we are (in days)
+ double phase=fmod(d.toJulianDay()-JulianDayJan1st1900+1.0, LunarCycleLengthInDays);
+ if(phase<0.0)phase+=LunarCycleLengthInDays;
+ //convert to enum base
+ int p=phase*double(LunarPhases_CycleLength)/LunarCycleLengthInDays;
+ //this should not be necessary, but you never know how screwed the FPU is...
+ if(p<0)p=0;
+ if(p>=LunarPhases_CycleLength)p=LunarPhases_CycleLength-1;
+ return LunarPhase(p);
+}
+
+
+}
--- /dev/null
+// Calculating Sun phases
+//
+// (c) Konrad Rosenbaum, 2017
+// protected under the GNU LGPL v3 or at your option any newer
+
+#include <solar.h>
+
+#include <math.h>
+
+
+void CalTools::GeoCoord::moveWest(double w)
+{
+ mwest+=w;
+ while(mwest>180)mwest-=360;
+ while(mwest<-180)mwest+=360;
+}
+
+void CalTools::GeoCoord::moveEast(double e){moveWest(-e);}
+
+void CalTools::GeoCoord::moveNorth(double n)
+{
+ mnorth+=n;
+ while(mnorth>180)mnorth-=360;
+ while(mnorth<-180)mnorth+=360;
+ if(mnorth>90){
+ moveWest(180);
+ mnorth=180-mnorth;
+ }
+ if(mnorth<-90){
+ moveWest(180);
+ mnorth=-180-mnorth;
+ }
+}
+
+void CalTools::GeoCoord::moveSouth(double s){moveNorth(-s);}
+
+static inline double deg2rad(double d){return d*M_PI/180.;}
+static inline double rad2deg(double r){return r*180./M_PI;}
+
+//argument of perihelion
+static inline double aperi(double n){return 102.9372 + 0.3179526*n/36525.;}
+
+CalTools::SolarInfo::SolarInfo(QDate date,GeoCoord geo)
+{
+ if(!date.isValid() || !geo.isValid())return;
+ mcoord=geo;
+
+ // /////
+ // section: calculate midday UTC
+
+ // day since 2000-1-1 plus slight correction
+ double n=date.toJulianDay() - 2451545.0 + 0.0008;
+ // mean solar noon
+ double J = n - mcoord.degWest()/360.;
+ // solar mean anomaly
+ double M = fmod((357.5291 + .98560028 * J), 360.);
+ // equation of the center
+ double C = 1.9148*sin(deg2rad(M)) + 0.02*sin(2*deg2rad(M)) + 0.0003*sin(3*deg2rad(M));
+ // ecliptic longitude
+ double lambda = fmod((M + C + 180. + aperi(n)), 360.);
+ // solar transit
+ double Jtrans=2451545.5 + J + 0.0053*sin(deg2rad(M)) - 0.0069*sin(deg2rad(2*lambda));
+ // convert to date and time
+ mdate=QDate::fromJulianDay(floor(Jtrans));
+ double t=Jtrans - floor(Jtrans);
+ mmidday=QTime::fromMSecsSinceStartOfDay(t*24*3600*1000);
+
+ // /////
+ // section: calculate twilight diffs
+ auto calcdiff=[&](double angle)->double{
+ //declination of the sun
+ double sindelta=sin(deg2rad(lambda))*sin(deg2rad(23.44));
+ double delta=rad2deg(asin(sindelta));
+ //hour angle
+ double cosomega=(sin(deg2rad(angle))-sin(deg2rad(mcoord.degNorth()))*sindelta) / (cos(deg2rad(mcoord.degNorth()))*cos(deg2rad(delta)));
+ double omega=acos(cosomega);
+ //calculate difference
+ return omega/2./M_PI;
+ };
+ mbegin=calcdiff(0.83);
+ mcivil=calcdiff(6.0);
+ mnautic=calcdiff(12.0);
+ mastro=calcdiff(18.0);
+}
+
+QDateTime CalTools::SolarInfo::sunrise(TwilightType type)const
+{
+ switch(type){
+ case TwilightType::Begin:return gentime(-mbegin);
+ case TwilightType::Civil:return gentime(-mcivil);
+ case TwilightType::Nautical:return gentime(-mnautic);
+ case TwilightType::Astronomical:return gentime(-mastro);
+ //should not happen:
+ default:return QDateTime();
+ }
+}
+
+QDateTime CalTools::SolarInfo::sunset(TwilightType type)const
+{
+ switch(type){
+ case TwilightType::Begin:return gentime(mbegin);
+ case TwilightType::Civil:return gentime(mcivil);
+ case TwilightType::Nautical:return gentime(mnautic);
+ case TwilightType::Astronomical:return gentime(mastro);
+ //should not happen:
+ default:return QDateTime();
+ }
+}
+
+QDateTime CalTools::SolarInfo::gentime(double diff)const
+{
+ if(!mdate.isValid() || isnan(diff) || isinf(diff))
+ return QDateTime();
+
+ return highNoon().addSecs(diff*24*3600);
+}
--- /dev/null
+TEMPLATE = lib
+TARGET = caltools
+VERSION =
+QT -= gui
+
+DESTDIR = $$PWD/../../lib
+
+INCLUDEPATH += ../../include/caltools
+
+DEFINES += CALTOOLS_EXPORT=Q_DECL_EXPORT
+
+SOURCES += lunar.cpp easterdate.cpp solar.cpp
--- /dev/null
+TEMPLATE=app
+TARGET=caltest
+
+QT+=testlib
+QT-=gui
+CONFIG += testcase
+
+include(../../../caltools.pri)
+
+SOURCES+=test2.cpp
--- /dev/null
+#include "easterdate.h"
+#include <QDebug>
+#include <QTest>
+
+
+class ED{
+public:
+ ED(quint16 y,quint8 m1,quint8 d1,quint8 m2=0,quint8 d2=0){year=y;wmonth=m1;wday=d1;emonth=m2?m2:m1;eday=d2?d2:d1;}
+ ED(){year=0;wmonth=wday=emonth=eday=0;}
+ ED(const ED&)=default;
+
+ inline bool isWest(QDate d)const{return d.year()==year && d.month()==wmonth && d.day()==wday;}
+ inline bool isEast(QDate d)const{return d.year()==year && d.month()==emonth && d.day()==eday;}
+
+ inline QDate west()const{return QDate(year,wmonth,wday);}
+ inline QDate east()const{return QDate(year,emonth,eday);}
+
+ quint16 year;
+ quint8 wmonth,wday,emonth,eday;
+};
+
+//https://en.wikipedia.org/wiki/List_of_dates_for_Easter
+QList<ED> initTV()
+{
+ QList<ED> r;
+ r<<ED(1997,3, 30, 4, 27)
+ <<ED(1998,4, 12, 4, 19)
+ <<ED(1999,4, 4, 4, 11)
+ <<ED(2000,4, 23, 4, 30)
+ <<ED(2001,4, 15)
+ <<ED(2002,3, 31, 5, 5)
+ <<ED(2003,4, 20, 4, 27)
+ <<ED(2004,4, 11)
+ <<ED(2005,3, 27, 5, 1)
+ <<ED(2006,4, 16, 4, 23)
+ <<ED(2007,4, 8)
+ <<ED(2008,3, 23, 4, 27)
+ <<ED(2009,4, 12, 4, 19)
+ <<ED(2010,4, 4)
+ <<ED(2011,4, 24)
+ <<ED(2012,4, 8, 4, 15)
+ <<ED(2013,3, 31, 5, 5)
+ <<ED(2014,4, 20)
+ <<ED(2015,4, 5, 4, 12)
+ <<ED(2016,3, 27, 5, 1)
+ <<ED(2017,4, 16)
+ <<ED(2018,4, 1, 4, 8)
+ <<ED(2019,4, 21, 4, 28)
+ <<ED(2020,4, 12, 4, 19)
+ <<ED(2021,4, 4, 5, 2)
+ <<ED(2022,4, 17, 4, 24)
+ <<ED(2023,4, 9, 4, 16)
+ <<ED(2024,3, 31, 5, 5)
+ <<ED(2025,4, 20)
+ <<ED(2026,4, 5, 4, 12)
+ <<ED(2027,3, 28, 5, 2)
+ <<ED(2028,4, 16)
+ <<ED(2029,4, 1, 4, 8)
+ <<ED(2030,4, 21, 4, 28)
+ <<ED(2031,4, 13)
+ <<ED(2032,3, 28, 5, 2)
+ <<ED(2033,4, 17, 4, 24)
+ <<ED(2034,4, 9)
+ <<ED(2035,3, 25, 4, 29)
+ <<ED(2036,4, 13, 4, 20)
+ <<ED(2037,4, 5);
+ return r;
+}
+
+class TClass:public QObject
+{
+ Q_OBJECT
+private slots:
+ void western_easter();
+ void eastern_easter();
+};
+
+#include "test2.moc"
+
+void TClass::western_easter()
+{
+ for(ED ed:initTV()){
+ QDate d=CalTools::easterSunday(ed.year);
+ QVERIFY(ed.isWest(d));
+ }
+}
+
+void TClass::eastern_easter()
+{
+ for(ED ed:initTV()){
+ QDate d=CalTools::orthodoxEasterSunday(ed.year);
+ QVERIFY(ed.isEast(d));
+ }
+}
+
+QTEST_MAIN(TClass)
\ No newline at end of file
--- /dev/null
+TEMPLATE = app
+TARGET = caltest
+
+QT+=testlib
+QT-=gui
+CONFIG += testcase
+
+include(../../../caltools.pri)
+
+SOURCES += geotest.cpp
--- /dev/null
+#include <solar.h>
+
+#include <QTest>
+
+
+using namespace CalTools;
+
+class GT:public QObject{
+ Q_OBJECT
+private slots:
+ void validity();
+ void moveLatitude();
+ void moveLongitude();
+};
+
+void GT::validity()
+{
+ GeoCoord c1;
+ QVERIFY(!c1.isValid());
+ GeoCoord c2(10,30);
+ QVERIFY(c2.isValid());
+ c2.moveWest(200);
+ QVERIFY(c2.isValid());
+ c2.moveNorth(100);
+ QVERIFY(c2.isValid());
+}
+
+void GT::moveLatitude()
+{
+ GeoCoord c1(10,30);
+ c1.moveNorth(10);
+ QCOMPARE(c1.degNorth(),20);
+}
+void GT::moveLongitude(){}
+
+QTEST_MAIN(GT)
\ No newline at end of file
--- /dev/null
+TEMPLATE=app
+TARGET=caltest
+
+QT+=testlib
+QT-=gui
+CONFIG += testcase
+
+INCLUDEPATH+=../../../include/caltools
+
+SOURCES+=gregtest.cpp
--- /dev/null
+#include "gregorian.h"
+#include <QTest>
+
+using namespace CalTools;
+
+class TClass:public QObject
+{
+ Q_OBJECT
+private slots:
+ void leapyear();
+ void daysInMonth();
+ void weekDaysInMonth();
+ void firstDay();
+ void lastDay();
+ void dayAfter();
+ void dayBefore();
+};
+
+#include "gregtest.moc"
+
+void TClass::leapyear()
+{
+ QCOMPARE(isLeapYear(1904),true);
+ QCOMPARE(isLeapYear(2004),true);
+ QCOMPARE(isLeapYear(2000),true);
+ QCOMPARE(isLeapYear(1901),false);
+ QCOMPARE(isLeapYear(1902),false);
+ QCOMPARE(isLeapYear(1903),false);
+ QCOMPARE(isLeapYear(1900),false);
+ QCOMPARE(isLeapYear(1800),false);
+}
+
+void TClass::daysInMonth()
+{
+ QCOMPARE(numDaysInMonth(2000,1),(quint8)31);
+ QCOMPARE(numDaysInMonth(2000,2),(quint8)29);
+ QCOMPARE(numDaysInMonth(2001,2),(quint8)28);
+ QCOMPARE(numDaysInMonth(2000,3),(quint8)31);
+ QCOMPARE(numDaysInMonth(2000,4),(quint8)30);
+ QCOMPARE(numDaysInMonth(2000,5),(quint8)31);
+ QCOMPARE(numDaysInMonth(2000,6),(quint8)30);
+ QCOMPARE(numDaysInMonth(2000,7),(quint8)31);
+ QCOMPARE(numDaysInMonth(2000,8),(quint8)31);
+ QCOMPARE(numDaysInMonth(2000,9),(quint8)30);
+ QCOMPARE(numDaysInMonth(2000,10),(quint8)31);
+ QCOMPARE(numDaysInMonth(2000,11),(quint8)30);
+ QCOMPARE(numDaysInMonth(2000,12),(quint8)31);
+}
+
+void TClass::weekDaysInMonth()
+{
+ QCOMPARE(numWeekDaysInMonth(2017,5),4);
+ QCOMPARE(numWeekDaysInMonth(2017,5,0),4);
+ QCOMPARE(numWeekDaysInMonth(2017,5,7),4);
+ QCOMPARE(numWeekDaysInMonth(2017,5,1),5);
+ QCOMPARE(numWeekDaysInMonth(2017,5,2),5);
+ QCOMPARE(numWeekDaysInMonth(2017,5,3),5);
+ QCOMPARE(numWeekDaysInMonth(2017,5,4),4);
+ QCOMPARE(numWeekDaysInMonth(2017,5,5),4);
+ QCOMPARE(numWeekDaysInMonth(2017,5,6),4);
+}
+
+void TClass::firstDay()
+{
+ QCOMPARE(firstWeekDayInMonth(2017,5,1),QDate(2017,5,1));
+ QCOMPARE(firstWeekDayInMonth(2017,5,2),QDate(2017,5,2));
+ QCOMPARE(firstWeekDayInMonth(2017,5,3),QDate(2017,5,3));
+ QCOMPARE(firstWeekDayInMonth(2017,5,4),QDate(2017,5,4));
+ QCOMPARE(firstWeekDayInMonth(2017,5,5),QDate(2017,5,5));
+ QCOMPARE(firstWeekDayInMonth(2017,5,6),QDate(2017,5,6));
+ QCOMPARE(firstWeekDayInMonth(2017,5,7),QDate(2017,5,7));
+ QCOMPARE(firstWeekDayInMonth(2017,5,0),QDate(2017,5,7));
+
+ QCOMPARE(firstWeekDayInMonth(2017,6,4),QDate(2017,6,1));
+ QCOMPARE(firstWeekDayInMonth(2017,6,5),QDate(2017,6,2));
+ QCOMPARE(firstWeekDayInMonth(2017,6,6),QDate(2017,6,3));
+ QCOMPARE(firstWeekDayInMonth(2017,6,7),QDate(2017,6,4));
+ QCOMPARE(firstWeekDayInMonth(2017,6,0),QDate(2017,6,4));
+ QCOMPARE(firstWeekDayInMonth(2017,6,1),QDate(2017,6,5));
+ QCOMPARE(firstWeekDayInMonth(2017,6,2),QDate(2017,6,6));
+ QCOMPARE(firstWeekDayInMonth(2017,6,3),QDate(2017,6,7));
+
+ QCOMPARE(firstWeekDayInMonth(2017,6,33),QDate());
+}
+
+void TClass::lastDay()
+{
+ QCOMPARE(lastWeekDayInMonth(2017,5,1),QDate(2017,5,29));
+ QCOMPARE(lastWeekDayInMonth(2017,5,2),QDate(2017,5,30));
+ QCOMPARE(lastWeekDayInMonth(2017,5,3),QDate(2017,5,31));//
+ QCOMPARE(lastWeekDayInMonth(2017,5,4),QDate(2017,5,25));
+ QCOMPARE(lastWeekDayInMonth(2017,5,5),QDate(2017,5,26));
+ QCOMPARE(lastWeekDayInMonth(2017,5,6),QDate(2017,5,27));
+ QCOMPARE(lastWeekDayInMonth(2017,5,7),QDate(2017,5,28));
+ QCOMPARE(lastWeekDayInMonth(2017,5,0),QDate(2017,5,28));
+
+ QCOMPARE(lastWeekDayInMonth(2017,5,10),QDate());
+}
+
+void TClass::dayAfter()
+{
+ const QDate d(2017,5,17);
+ QCOMPARE(findDayAfter(d),QDate(2017,5,21));
+ QCOMPARE(findDayAfter(d,0),QDate(2017,5,21));
+ QCOMPARE(findDayAfter(d,7),QDate(2017,5,21));
+ QCOMPARE(findDayAfter(d,0,false),QDate(2017,5,21));
+ QCOMPARE(findDayAfter(d,0,false),QDate(2017,5,21));
+
+ QCOMPARE(findDayAfter(d,1,false),QDate(2017,5,22));
+ QCOMPARE(findDayAfter(d,2,false),QDate(2017,5,23));
+ QCOMPARE(findDayAfter(d,3,false),QDate(2017,5,24));
+ QCOMPARE(findDayAfter(d,4,false),QDate(2017,5,18));
+ QCOMPARE(findDayAfter(d,5,false),QDate(2017,5,19));
+ QCOMPARE(findDayAfter(d,6,false),QDate(2017,5,20));
+ QCOMPARE(findDayAfter(d,7,false),QDate(2017,5,21));
+
+ QCOMPARE(findDayAfter(d,1,true),QDate(2017,5,22));
+ QCOMPARE(findDayAfter(d,2,true),QDate(2017,5,23));
+ QCOMPARE(findDayAfter(d,3,true),QDate(2017,5,17));
+ QCOMPARE(findDayAfter(d,4,true),QDate(2017,5,18));
+ QCOMPARE(findDayAfter(d,5,true),QDate(2017,5,19));
+ QCOMPARE(findDayAfter(d,6,true),QDate(2017,5,20));
+ QCOMPARE(findDayAfter(d,7,true),QDate(2017,5,21));
+
+ QCOMPARE(findDayAfter(d,9,false),QDate());
+}
+
+void TClass::dayBefore()
+{
+ const QDate d(2017,5,17);
+ QCOMPARE(findDayBefore(d),QDate(2017,5,14));
+ QCOMPARE(findDayBefore(d,0),QDate(2017,5,14));
+ QCOMPARE(findDayBefore(d,7),QDate(2017,5,14));
+ QCOMPARE(findDayBefore(d,0,false),QDate(2017,5,14));
+ QCOMPARE(findDayBefore(d,0,false),QDate(2017,5,14));
+
+ QCOMPARE(findDayBefore(d,1,false),QDate(2017,5,15));
+ QCOMPARE(findDayBefore(d,2,false),QDate(2017,5,16));
+ QCOMPARE(findDayBefore(d,3,false),QDate(2017,5,10));
+ QCOMPARE(findDayBefore(d,4,false),QDate(2017,5,11));
+ QCOMPARE(findDayBefore(d,5,false),QDate(2017,5,12));
+ QCOMPARE(findDayBefore(d,6,false),QDate(2017,5,13));
+ QCOMPARE(findDayBefore(d,7,false),QDate(2017,5,14));
+
+ QCOMPARE(findDayBefore(d,1,true),QDate(2017,5,15));
+ QCOMPARE(findDayBefore(d,2,true),QDate(2017,5,16));
+ QCOMPARE(findDayBefore(d,3,true),QDate(2017,5,17));
+ QCOMPARE(findDayBefore(d,4,true),QDate(2017,5,11));
+ QCOMPARE(findDayBefore(d,5,true),QDate(2017,5,12));
+ QCOMPARE(findDayBefore(d,6,true),QDate(2017,5,13));
+ QCOMPARE(findDayBefore(d,7,true),QDate(2017,5,14));
+
+ QCOMPARE(findDayBefore(d,9,false),QDate());
+}
+
+QTEST_MAIN(TClass)
\ No newline at end of file
--- /dev/null
+#include "lunar.h"
+#include <QTest>
+
+using namespace CalTools;
+
+class TClass:public QObject
+{
+ Q_OBJECT
+private slots:
+ void lunarPhases();
+};
+
+#include "lunar.moc"
+
+void TClass::lunarPhases()
+{
+ QCOMPARE(lunarPhaseForDate(QDate(2017,4,26)),LunarPhase::NewMoon);
+ QCOMPARE(lunarPhaseForDate(QDate(2017,5,1)),LunarPhase::Waxing25);
+ QCOMPARE(lunarPhaseForDate(QDate(2017,5,3)),LunarPhase::Waxing50);
+ QCOMPARE(lunarPhaseForDate(QDate(2017,5,6)),LunarPhase::Waxing75);
+ QCOMPARE(lunarPhaseForDate(QDate(2017,5,10)),LunarPhase::FullMoon);
+ QCOMPARE(lunarPhaseForDate(QDate(2017,5,16)),LunarPhase::Waning75);
+ QCOMPARE(lunarPhaseForDate(QDate(2017,5,19)),LunarPhase::Waning50);
+ QCOMPARE(lunarPhaseForDate(QDate(2017,5,21)),LunarPhase::Waning25);
+ QCOMPARE(lunarPhaseForDate(QDate(2017,5,25)),LunarPhase::NewMoon);
+}
+
+QTEST_MAIN(TClass)
\ No newline at end of file
--- /dev/null
+TEMPLATE=app
+TARGET=caltest
+
+QT+=testlib
+QT-=gui
+CONFIG += testcase
+
+include(../../../caltools.pri)
+
+SOURCES+=lunar.cpp
--- /dev/null
+TEMPLATE = app
+TARGET = caltest
+
+QT-=gui
+
+include(../../../caltools.pri)
+
+SOURCES += solartest.cpp
--- /dev/null
+#include <solar.h>
+
+int main(){}
\ No newline at end of file
--- /dev/null
+#include "easterdate.h"
+#include "gregorian.h"
+#include "lunar.h"
+#include "solar.h"
--- /dev/null
+// Calculating Easter Dates
+//
+// (c) Konrad Rosenbaum, 2017
+// protected under the GNU LGPL v3 or at your option any newer
+
+#ifndef CALTOOLS_EASTER_H
+#define CALTOOLS_EASTER_H
+
+#include <QDate>
+
+#if 0
+#include <QDebug>
+#define DD(x) qDebug()<<""#x "="<<x;
+#else
+#define DD(x)
+#endif
+
+#ifndef CALTOOLS_EXPORT
+#define CALTOOLS_EXPORT Q_DECL_IMPORT
+#endif
+
+
+// formulas were found at http://www.dateofeaster.com/
+// some more explanations (in German): https://de.wikipedia.org/wiki/Osterzyklus#Die_Mondgleichung
+// and: https://de.wikipedia.org/wiki/Gau%C3%9Fsche_Osterformel
+
+namespace CalTools {
+
+///calculates the date of easter for a specific year
+QDate CALTOOLS_EXPORT easterSunday(quint16 year);
+
+///calculates the eastern orthodox date for Easter sunday
+QDate CALTOOLS_EXPORT orthodoxEasterSunday(quint16 year);
+
+//TODO: date of jewish pessach
+
+
+//end of namespace
+}
+
+#undef DD
+
+#endif
--- /dev/null
+// Calculating various aspects of the Gregorian calendar
+//
+// (c) Konrad Rosenbaum, 2017
+// protected under the GNU LGPL v3 or at your option any newer
+
+#ifndef CALTOOLS_GREGORIAN_H
+#define CALTOOLS_GREGORIAN_H
+
+#include <QDate>
+
+namespace CalTools{
+
+// https://en.wikipedia.org/wiki/Leap_year
+/// checks for leap years (works only within the Gregorian Calendar!)
+static inline
+bool isLeapYear(quint16 year)
+{
+ if(year%4 == 0){
+ if(year%100 == 0){
+ if(year%400 == 0)return true;
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+///returns how many days there are in a specific month
+static inline
+quint8 numDaysInMonth(quint16 year,quint8 month)
+{
+ switch(month){
+ case 1:case 3:case 5:case 7:
+ case 8:case 10:case 12:
+ return 31;
+ case 4:case 6:case 9:case 11:
+ return 30;
+ case 2:
+ return 28+(isLeapYear(year)?1:0);
+ default:
+ return 0;
+ }
+}
+
+///finds the first instance of a week day in a month
+/// \param year the Year in which to search
+/// \param month the Month in which to search
+/// \param dayOfWeek the week day to look for: 0/7 is sunday (\see QDate)
+static inline
+QDate firstWeekDayInMonth(quint16 year,quint8 month,quint8 dayOfWeek=0)
+{
+ if(dayOfWeek==0)dayOfWeek=7;
+ if(dayOfWeek>7)return QDate();
+ //check first day
+ QDate d(year,month,1);
+ int dw=d.dayOfWeek();
+ if(dw==dayOfWeek)return d;
+ //move on
+ if(dayOfWeek<dw)dayOfWeek+=7;
+ return d.addDays(dayOfWeek-dw);
+}
+
+///finds how many instances of a specific weekday there are in a month
+/// \param year the Year in which to search
+/// \param month the Month in which to search
+/// \param dayOfWeek the week day to look for: 0/7 is sunday (\see QDate)
+static inline
+int numWeekDaysInMonth(quint16 year,quint8 month,quint8 dayOfWeek=0)
+{
+ if(dayOfWeek==0)dayOfWeek=7;
+ if(dayOfWeek>7)return 0;
+ //get first day of that type, then calculate
+ int d=firstWeekDayInMonth(year,month,dayOfWeek).day();
+ return (numDaysInMonth(year,month)-d)/7 + 1;
+}
+
+///finds the last instance of a week day in a month
+/// \param year the Year in which to search
+/// \param month the Month in which to search
+/// \param dayOfWeek the week day to look for: 0/7 is sunday (\see QDate)
+static inline
+QDate lastWeekDayInMonth(quint16 year,quint8 month,quint8 dayOfWeek=0)
+{
+ if(dayOfWeek==0)dayOfWeek=7;
+ if(dayOfWeek>7)return QDate();
+ //check last day
+ QDate d(year,month,numDaysInMonth(year,month));
+ int dw=d.dayOfWeek();
+ if(dw==dayOfWeek)return d;
+ if(dw<dayOfWeek)dw+=7;
+ return d.addDays(dayOfWeek-dw);
+}
+
+///find the first instance of a specific week day after a given date
+/// \param date the date from which to search
+/// \param dayOfWeek the week day to look for (default is sunday)
+/// \param inclusive if true date is returned if it is the week day in question, otherwise a date 7 days later
+static inline
+QDate findDayAfter(const QDate &date,quint8 dayOfWeek=0,bool inclusive=false)
+{
+ if(dayOfWeek==0)dayOfWeek=7;
+ if(dayOfWeek>7)return QDate();
+ //same one?
+ int dw=date.dayOfWeek();
+ if(inclusive && dw==dayOfWeek)return date;
+ if(dayOfWeek<=dw)dayOfWeek+=7;
+ return date.addDays(dayOfWeek-dw);
+}
+
+///find the first instance of a specific week day before a given date
+/// \param date the date from which to search
+/// \param dayOfWeek the week day to look for (default is sunday)
+/// \param inclusive if true date is returned if it is the week day in question, otherwise a date 7 days earlier
+static inline
+QDate findDayBefore(QDate date,quint8 dayOfWeek=0,bool inclusive=false)
+{
+ if(dayOfWeek==0)dayOfWeek=7;
+ if(dayOfWeek>7)return QDate();
+ //same one?
+ int dw=date.dayOfWeek();
+ if(inclusive && dw==dayOfWeek)return date;
+ if(dayOfWeek>=dw)dw+=7;
+ return date.addDays(dayOfWeek-dw);
+}
+
+//end of namespace
+}
+
+#endif
--- /dev/null
+// Calculating Easter Dates
+//
+// (c) Konrad Rosenbaum, 2017
+// protected under the GNU LGPL v3 or at your option any newer
+
+#ifndef CALTOOLS_LUNAR_H
+#define CALTOOLS_LUNAR_H
+
+#include <QDate>
+
+namespace CalTools{
+
+///Phases of the Moon (note: this only applies to earth's moon, not to Tatooine, Europa, Io or any other moon)
+enum class LunarPhase {
+ ///New Moon, not visible with the naked eye
+ NewMoon=0,
+ ///getting bigger, it's a crescent
+ Waxing25=1,
+ ///getting bigger, it's a half moon
+ Waxing50=2,
+ ///getting bigger, it's almost full
+ Waxing75=3,
+ ///getting bigger, it's a crescent
+ WaxingCrescent=1,
+ ///getting bigger, it's a half moon
+ WaxingHalf=2,
+ ///getting bigger, it's almost full
+ WaxingGibbous=3,
+ ///getting bigger, it's a half moon, first quarter of the full cycle
+ FirstQuarter=2,
+ ///full moon
+ FullMoon=4,
+ ///getting smaller, it's almost full
+ Waning75=5,
+ ///getting smaller, it's a half moon
+ Waning50=6,
+ ///getting smaller, it's a crescent
+ Waning25=7,
+ ///getting smaller, it's almost full
+ WaningGibbous=5,
+ ///getting smaller, it's a half moon
+ WaningHalf=6,
+ ///getting smaller, it's a crescent
+ WaningCrescent=7,
+ ///getting smaller, it's a half moon, third quarter of the full cycle
+ ThirdQuarter=6,
+};
+
+#ifndef CALTOOLS_EXPORT
+#define CALTOOLS_EXPORT Q_DECL_IMPORT
+#endif
+
+///returns the phase of the moon for a specific date
+/// note: this function is quite
+LunarPhase lunarPhaseForDate(QDate d);
+
+//end of namespace
+}
+
+#endif
--- /dev/null
+// Calculating Sun phases
+//
+// (c) Konrad Rosenbaum, 2017
+// protected under the GNU LGPL v3 or at your option any newer
+
+#ifndef CALTOOLS_SOLAR_H
+#define CALTOOLS_SOLAR_H
+
+#include <QDateTime>
+
+namespace CalTools {
+
+//TODO: calculate sunrise/sundown
+// https://en.wikipedia.org/wiki/Sunrise_equation
+
+#ifndef CALTOOLS_EXPORT
+#define CALTOOLS_EXPORT Q_DECL_IMPORT
+#endif
+
+///Represents geographical coordinates.
+class CALTOOLS_EXPORT GeoCoord
+{
+ double mwest=1000,mnorth=0;
+public:
+ ///Instantiates an invalid coordinate instance.
+ GeoCoord(){}
+ ///Instantiates a coordinate instance.
+ ///\param north - the latitude in degrees north, negative values are south (-90 .. 0 .. 90)
+ ///\param west - the longitude in degrees west, negative values are east (-180 .. 0 .. 180)
+ GeoCoord(double north,double west):mwest(west),mnorth(north){/*for the corrections*/ moveWest(0);moveNorth(0);}
+ ///Copies the instance.
+ GeoCoord(const GeoCoord&)=default;
+ ///Moves the instance.
+ GeoCoord(GeoCoord&&)=default;
+
+ ///Copies the instance.
+ GeoCoord& operator=(const GeoCoord&)=default;
+ ///Moves the instance.
+ GeoCoord& operator=(GeoCoord&&)=default;
+
+ ///returns true if the coordinates are valid
+ bool isValid()const{return mwest<=180 && mwest>=-180 && mnorth<=90 && mnorth>=-90;}
+ ///returns the longitude in degrees west (negative is east)
+ double degWest()const{return mwest;}
+ ///returns the longitude in degrees east (negative is west)
+ double degEast()const{return -mwest;}
+ ///returns the latitude in degrees north (negative is south)
+ double degNorth()const{return mnorth;}
+ ///returns the latitude in degrees south (negative is north)
+ double degSouth()const{return -mnorth;}
+
+ ///moves the coordinates further west
+ void moveWest(double);
+ ///moves the coordinates further east
+ void moveEast(double);
+ ///moves the coordinates further north (can wrap around beyond the north pole)
+ void moveNorth(double);
+ ///moves the coordinates further south (can wrap around beyond the south pole)
+ void moveSouth(double);
+};
+
+///Calculator for sunset and sunrise
+class CALTOOLS_EXPORT SolarInfo
+{
+public:
+ ///Instantiates an invalid instance that will return invalid times for all values.
+ SolarInfo(){}
+ ///Instantiates an instance for the given date and coordinates on earth.
+ SolarInfo(QDate date,GeoCoord geo);
+
+ ///copies the instance
+ SolarInfo(const SolarInfo&)=default;
+ ///moves the instance
+ SolarInfo(SolarInfo&&)=default;
+
+ ///copies the instance
+ SolarInfo& operator=(const SolarInfo&)=default;
+ ///moves the instance
+ SolarInfo& operator=(SolarInfo&&)=default;
+
+ ///returns true if the solar calculator has valid date and coordinates
+ bool isValid()const{return mdate.isValid();}
+
+ ///What exact part of twilight we are calculating.
+ enum class TwilightType {
+ ///Begin of sunset or sunrise - when the sun touches the horizon.
+ Begin,
+ ///End of civil twilight - the sun is 6 degrees below the horizon.
+ Civil,
+ ///End of nautic twilight - the sun is 12 degrees below the horizon.
+ Nautical,
+ ///End of astronomical twilight - the sun is 18 degrees below the horizon.
+ Astronomical
+ };
+
+ ///Calculate sunrise.
+ ///\param type the phase of sunrise that will be calculated
+ ///\returns the time of the sunrise phase in UTC
+ QDateTime sunrise(TwilightType type=TwilightType::Begin)const;
+
+ ///Calculate sunset.
+ ///\param type the phase of sunset that will be calculated
+ ///\returns the time of the sunset phase in UTC
+ QDateTime sunset(TwilightType type=TwilightType::Begin)const;
+
+ ///\returns The time of highest sun (zenith).
+ QDateTime highNoon()const{return QDateTime(mdate,mmidday,Qt::UTC);}
+
+private:
+ ///the date for which we calculate times
+ QDate mdate;
+ ///the time at which the sun is at the zenith in UTC
+ QTime mmidday;
+ ///time differences in fractions of a day
+ double mbegin=0,mcivil=0,mnautic=0,mastro=0;
+ ///geographical coordinates
+ GeoCoord mcoord;
+
+ ///calculate a time relative to the calculated zenith
+ QDateTime gentime(double diff)const;
+};
+
+//end of namespace
+}
+
+#endif
TEMPLATE = subdirs
-SUBDIRS = zip elam aurora
+SUBDIRS = zip elam aurora caltools