summaryrefslogtreecommitdiffstats
path: root/kstars/kstars/timezonerule.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kstars/kstars/timezonerule.cpp')
-rw-r--r--kstars/kstars/timezonerule.cpp430
1 files changed, 430 insertions, 0 deletions
diff --git a/kstars/kstars/timezonerule.cpp b/kstars/kstars/timezonerule.cpp
new file mode 100644
index 00000000..20ac8c45
--- /dev/null
+++ b/kstars/kstars/timezonerule.cpp
@@ -0,0 +1,430 @@
+/***************************************************************************
+ timezonerule.cpp - description
+ -------------------
+ begin : Tue Apr 2 2002
+ copyright : (C) 2002 by Jason Harris
+ email : kstars@30doradus.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include "timezonerule.h"
+#include "kstarsdatetime.h"
+
+TimeZoneRule::TimeZoneRule() {
+ //Build the empty TimeZoneRule.
+ StartMonth = 0;
+ RevertMonth = 0;
+ StartDay = 0;
+ RevertDay = 0;
+ StartWeek = -1;
+ RevertWeek = -1;
+ StartTime = QTime();
+ RevertTime = QTime();
+ HourOffset = 0.0;
+ dTZ = 0.0;
+}
+
+TimeZoneRule::TimeZoneRule( const QString &smonth, const QString &sday, const QTime &stime,
+ const QString &rmonth, const QString &rday, const QTime &rtime, const double &dh ) {
+ dTZ = 0.0;
+ if ( smonth != "0" ) {
+ StartMonth = initMonth( smonth );
+ RevertMonth = initMonth( rmonth );
+
+ if ( StartMonth && RevertMonth && initDay( sday, StartDay, StartWeek ) &&
+ initDay( rday, RevertDay, RevertWeek ) && stime.isValid() && rtime.isValid() ) {
+ StartTime = stime;
+ RevertTime = rtime;
+ HourOffset = dh;
+ } else {
+ kdWarning() << i18n( "Error parsing TimeZoneRule, setting to empty rule." ) << endl;
+ StartMonth = 0;
+ RevertMonth = 0;
+ StartDay = 0;
+ RevertDay = 0;
+ StartWeek = -1;
+ RevertWeek = -1;
+ StartTime = QTime();
+ RevertTime = QTime();
+ HourOffset = 0.0;
+ }
+ } else { //Empty rule
+ StartMonth = 0;
+ RevertMonth = 0;
+ StartDay = 0;
+ RevertDay = 0;
+ StartWeek = -1;
+ RevertWeek = -1;
+ StartTime = QTime();
+ RevertTime = QTime();
+ HourOffset = 0.0;
+ }
+}
+
+TimeZoneRule::~TimeZoneRule() {
+}
+
+void TimeZoneRule::setDST( bool activate ) {
+ if ( activate ) {
+ kdDebug() << i18n( "Daylight Saving Time active" ) << endl;
+ dTZ = HourOffset;
+ } else {
+ kdDebug() << i18n( "Daylight Saving Time inactive" ) << endl;
+ dTZ = 0.0;
+ }
+}
+
+int TimeZoneRule::initMonth( const QString &mn ) {
+//Check whether the argument is a three-letter English month code.
+ QString ml = mn.lower();
+ if ( ml == "jan" ) return 1;
+ else if ( ml == "feb" ) return 2;
+ else if ( ml == "mar" ) return 3;
+ else if ( ml == "apr" ) return 4;
+ else if ( ml == "may" ) return 5;
+ else if ( ml == "jun" ) return 6;
+ else if ( ml == "jul" ) return 7;
+ else if ( ml == "aug" ) return 8;
+ else if ( ml == "sep" ) return 9;
+ else if ( ml == "oct" ) return 10;
+ else if ( ml == "nov" ) return 11;
+ else if ( ml == "dec" ) return 12;
+
+ kdWarning() << i18n( "Could not parse " ) << mn << i18n( " as a valid month code." ) << endl;
+ return false;
+}
+
+bool TimeZoneRule::initDay( const QString &dy, int &Day, int &Week ) {
+//Three possible ways to express a day.
+//1. simple integer; the calendar date...set Week=0 to indicate that Date is not the day of the week
+ bool ok;
+ int day = dy.toInt( &ok );
+ if ( ok ) {
+ Day = day;
+ Week = 0;
+ return true;
+ }
+
+ QString dl = dy.lower();
+//2. 3-letter day of week string, indicating the last of that day of the month
+// ...set Week to 5 to indicate the last weekday of the month
+ if ( dl == "mon" ) { Day = 1; Week = 5; return true; }
+ else if ( dl == "tue" ) { Day = 2; Week = 5; return true; }
+ else if ( dl == "wed" ) { Day = 3; Week = 5; return true; }
+ else if ( dl == "thu" ) { Day = 4; Week = 5; return true; }
+ else if ( dl == "fri" ) { Day = 5; Week = 5; return true; }
+ else if ( dl == "sat" ) { Day = 6; Week = 5; return true; }
+ else if ( dl == "sun" ) { Day = 7; Week = 5; return true; }
+
+//3. 1,2 or 3 followed by 3-letter day of week string; this indicates
+// the (1st/2nd/3rd) weekday of the month.
+ int wn = dl.left(1).toInt();
+ if ( wn >0 && wn <4 ) {
+ QString dm = dl.mid( 1, dl.length() ).lower();
+ if ( dm == "mon" ) { Day = 1; Week = wn; return true; }
+ else if ( dm == "tue" ) { Day = 2; Week = wn; return true; }
+ else if ( dm == "wed" ) { Day = 3; Week = wn; return true; }
+ else if ( dm == "thu" ) { Day = 4; Week = wn; return true; }
+ else if ( dm == "fri" ) { Day = 5; Week = wn; return true; }
+ else if ( dm == "sat" ) { Day = 6; Week = wn; return true; }
+ else if ( dm == "sun" ) { Day = 7; Week = wn; return true; }
+ }
+
+ kdWarning() << i18n( "Could not parse " ) << dy << i18n( " as a valid day code." ) << endl;
+ return false;
+}
+
+int TimeZoneRule::findStartDay( const KStarsDateTime &d ) {
+ // Determine the calendar date of StartDay for the month and year of the given date.
+ ExtDate test;
+
+ // TimeZoneRule is empty, return -1
+ if ( isEmptyRule() ) return -1;
+
+ // If StartWeek=0, just return the integer.
+ if ( StartWeek==0 ) return StartDay;
+
+ // Since StartWeek was not zero, StartDay is the day of the week, not the calendar date
+ else if ( StartWeek==5 ) { // count back from end of month until StartDay is found.
+ for ( test = ExtDate( d.date().year(), d.date().month(), d.date().daysInMonth() );
+ test.day() > 21; test = test.addDays( -1 ) )
+ if ( test.dayOfWeek() == StartDay ) break;
+ } else { // Count forward from day 1, 8 or 15 (depending on StartWeek) until correct day of week is found
+ for ( test = ExtDate( d.date().year(), d.date().month(), (StartWeek-1)*7 + 1 );
+ test.day() < 7*StartWeek; test = test.addDays( 1 ) )
+ if ( test.dayOfWeek() == StartDay ) break;
+ }
+ return test.day();
+}
+
+int TimeZoneRule::findRevertDay( const KStarsDateTime &d ) {
+ // Determine the calendar date of RevertDay for the month and year of the given date.
+ ExtDate test;
+
+ // TimeZoneRule is empty, return -1
+ if ( isEmptyRule() ) return -1;
+
+ // If RevertWeek=0, just return the integer.
+ if ( RevertWeek==0 ) return RevertDay;
+
+ // Since RevertWeek was not zero, RevertDay is the day of the week, not the calendar date
+ else if ( RevertWeek==5 ) { //count back from end of month until RevertDay is found.
+ for ( test = ExtDate( d.date().year(), d.date().month(), d.date().daysInMonth() );
+ test.day() > 21; test = test.addDays( -1 ) )
+ if ( test.dayOfWeek() == RevertDay ) break;
+ } else { //Count forward from day 1, 8 or 15 (depending on RevertWeek) until correct day of week is found
+ for ( test = ExtDate( d.date().year(), d.date().month(), (RevertWeek-1)*7 + 1 );
+ test.day() < 7*RevertWeek; test = test.addDays( 1 ) )
+ if ( test.dayOfWeek() == StartDay ) break;
+ }
+ return test.day();
+}
+
+bool TimeZoneRule::isDSTActive( const KStarsDateTime &date ) {
+ // The empty rule always returns false
+ if ( isEmptyRule() ) return false;
+
+ // First, check whether the month is outside the DST interval. Note that
+ // the interval check is different if StartMonth > RevertMonth (indicating that
+ // the DST interval includes the end of the year).
+ int month = date.date().month();
+
+ if ( StartMonth < RevertMonth ) {
+ if ( month < StartMonth || month > RevertMonth ) return false;
+ } else {
+ if ( month < StartMonth && month > RevertMonth ) return false;
+ }
+
+ // OK, if the month is equal to StartMonth or Revert Month, we have more
+ // detailed checking to do...
+ int day = date.date().day();
+
+ if ( month == StartMonth ) {
+ int sday = findStartDay( date );
+ if ( day < sday ) return false;
+ if ( day==sday && date.time() < StartTime ) return false;
+ } else if ( month == RevertMonth ) {
+ int rday = findRevertDay( date );
+ if ( day > rday ) return false;
+ if ( day==rday && date.time() > RevertTime ) return false;
+ }
+
+ // passed all tests, so we must be in DST.
+ return true;
+}
+
+void TimeZoneRule::nextDSTChange_LTime( const KStarsDateTime &date ) {
+ KStarsDateTime result;
+
+ // return a very remote date if the rule is the empty rule.
+ if ( isEmptyRule() ) result = KStarsDateTime( INVALID_DAY );
+
+ else if ( deltaTZ() ) {
+ // Next change is reverting back to standard time.
+
+ //y is the year for the next DST Revert date. It's either the current year, or
+ //the next year if the current month is already past RevertMonth
+ int y = date.date().year();
+ if ( RevertMonth < date.date().month() ) ++y;
+
+ result = KStarsDateTime( ExtDate( y, RevertMonth, 1 ), RevertTime );
+ result = KStarsDateTime( ExtDate( y, RevertMonth, findRevertDay( result ) ), RevertTime );
+
+ } else {
+ // Next change is starting DST.
+
+ //y is the year for the next DST Start date. It's either the current year, or
+ //the next year if the current month is already past StartMonth
+ int y = date.date().year();
+ if ( StartMonth < date.date().month() ) ++y;
+
+ result = KStarsDateTime( ExtDate( y, StartMonth, 1 ), StartTime );
+ result = KStarsDateTime( ExtDate( y, StartMonth, findStartDay( result ) ), StartTime );
+ }
+
+ kdDebug() << i18n( "Next Daylight Savings Time change (Local Time): " ) << result.toString() << endl;
+ next_change_ltime = result;
+}
+
+
+void TimeZoneRule::previousDSTChange_LTime( const KStarsDateTime &date ) {
+ KStarsDateTime result;
+
+ // return a very remote date if the rule is the empty rule
+ if ( isEmptyRule() ) next_change_ltime = KStarsDateTime( INVALID_DAY );
+
+ if ( deltaTZ() ) {
+ // Last change was starting DST.
+
+ //y is the year for the previous DST Start date. It's either the current year, or
+ //the previous year if the current month is earlier than StartMonth
+ int y = date.date().year();
+ if ( StartMonth > date.date().month() ) --y;
+
+ result = KStarsDateTime( ExtDate( y, StartMonth, 1 ), StartTime );
+ result = KStarsDateTime( ExtDate( y, StartMonth, findStartDay( result ) ), StartTime );
+
+ } else if ( StartMonth ) {
+ //Last change was reverting to standard time.
+
+ //y is the year for the previous DST Start date. It's either the current year, or
+ //the previous year if the current month is earlier than StartMonth
+ int y = date.date().year();
+ if ( RevertMonth > date.date().month() ) --y;
+
+ result = KStarsDateTime( ExtDate( y, RevertMonth, 1 ), RevertTime );
+ result = KStarsDateTime( ExtDate( y, RevertMonth, findRevertDay( result ) ), RevertTime );
+ }
+
+ kdDebug() << i18n( "Previous Daylight Savings Time change (Local Time): " ) << result.toString() << endl;
+ next_change_ltime = result;
+}
+
+/**Convert current local DST change time in universal time */
+void TimeZoneRule::nextDSTChange( const KStarsDateTime &local_date, const double TZoffset ) {
+ // just decrement timezone offset and hour offset
+ KStarsDateTime result = local_date.addSecs( int( (TZoffset + deltaTZ()) * -3600) );
+
+ kdDebug() << i18n( "Next Daylight Savings Time change (UTC): " ) << result.toString() << endl;
+ next_change_utc = result;
+}
+
+/**Convert current local DST change time in universal time */
+void TimeZoneRule::previousDSTChange( const KStarsDateTime &local_date, const double TZoffset ) {
+ // just decrement timezone offset
+ KStarsDateTime result = local_date.addSecs( int( TZoffset * -3600) );
+
+ // if prev DST change is a revert change, so the revert time is in daylight saving time
+ if ( result.date().month() == RevertMonth )
+ result = result.addSecs( int(HourOffset * -3600) );
+
+ kdDebug() << i18n( "Previous Daylight Savings Time change (UTC): " ) << result.toString() << endl;
+ next_change_utc = result;
+}
+
+void TimeZoneRule::reset_with_ltime( KStarsDateTime &ltime, const double TZoffset, const bool time_runs_forward,
+ const bool automaticDSTchange ) {
+
+/**There are some problems using local time for getting next daylight saving change time.
+ 1. The local time is the start time of DST change. So the local time doesn't exists and must
+ corrected.
+ 2. The local time is the revert time. So the local time exists twice.
+ 3. Neither start time nor revert time. There is no problem.
+
+ Problem #1 is more complicated and we have to change the local time by reference.
+ Problem #2 we just have to reset status of DST.
+
+ automaticDSTchange should only set to true if DST status changed due to running automatically over
+ a DST change time. If local time will changed manually the automaticDSTchange should always set to
+ false, to hold current DST status if possible (just on start and revert time possible).
+ */
+
+ //don't need to do anything for empty rule
+ if ( isEmptyRule() ) return;
+
+ // check if DST is active before resetting with new time
+ bool wasDSTactive(false);
+
+ if ( deltaTZ() != 0.0 ) {
+ wasDSTactive = true;
+ }
+
+ // check if current time is start time, this means if a DST change happend in last hour(s)
+ bool active_with_houroffset = isDSTActive(ltime.addSecs( int(HourOffset * -3600) ) );
+ bool active_normal = isDSTActive( ltime );
+
+ // store a valid local time
+ KStarsDateTime ValidLTime = ltime;
+
+ if ( active_with_houroffset != active_normal && ValidLTime.date().month() == StartMonth ) {
+ // current time is the start time
+ kdDebug() << "Current time = Starttime: invalid local time due to daylight saving time" << endl;
+
+ // set a correct local time because the current time doesn't exists
+ // if automatic DST change happend, new DST setting is the opposite of current setting
+ if ( automaticDSTchange ) {
+ // revert DST status
+ setDST( !wasDSTactive );
+ // new setting DST is inactive, so subtract hour offset to new time
+ if ( wasDSTactive ) {
+ // DST inactive
+ ValidLTime = ltime.addSecs( int( HourOffset * - 3600) );
+ } else {
+ // DST active
+ // add hour offset to new time
+ ValidLTime = ltime.addSecs( int( HourOffset * 3600) );
+ }
+ } else { // if ( automaticDSTchange )
+ // no automatic DST change happend, so stay in current DST mode
+ setDST( wasDSTactive );
+ if ( wasDSTactive ) {
+ // DST active
+ // add hour offset to current time, because time doesn't exists
+ ValidLTime = ltime.addSecs( int( HourOffset * 3600) );
+ } else {
+ // DST inactive
+ // subtrace hour offset to current time, because time doesn't exists
+ ValidLTime = ltime.addSecs( int( HourOffset * -3600) );
+ }
+ } // else { // if ( automaticDSTchange )
+
+ } else { // if ( active_with_houroffset != active_normal && ValidLTime.date().month() == StartMonth )
+ // If current time was not start time, so check if current time is revert time
+ // this means if a DST change happend in next hour(s)
+ active_with_houroffset = isDSTActive(ltime.addSecs( int(HourOffset * 3600) ) );
+ if ( active_with_houroffset != active_normal && RevertMonth == ValidLTime.date().month() ) {
+ // current time is the revert time
+ kdDebug() << "Current time = Reverttime" << endl;
+
+ // we don't kneed to change the local time, because local time always exists, but
+ // some times exists twice, so we have to reset DST status
+ if ( automaticDSTchange ) {
+ // revert DST status
+ setDST( !wasDSTactive );
+ } else {
+ // no automatic DST change so stay in current DST mode
+ setDST( wasDSTactive );
+ }
+
+ } else {
+ //Current time was neither starttime nor reverttime, so use normal calculated DST status
+ setDST( active_normal );
+ }
+ } // if ( active_with_houroffset != active_normal && ValidLTime.date().month() == StartMonth )
+
+// kdDebug() << "Using Valid Local Time = " << ValidLTime.toString() << endl;
+
+ if (time_runs_forward) {
+ // get next DST change time in local time
+ nextDSTChange_LTime( ValidLTime );
+ nextDSTChange( next_change_ltime, TZoffset );
+ } else {
+ // get previous DST change time in local time
+ previousDSTChange_LTime( ValidLTime );
+ previousDSTChange( next_change_ltime, TZoffset );
+ }
+ ltime = ValidLTime;
+}
+
+
+bool TimeZoneRule::equals( TimeZoneRule *r ) {
+ if ( StartDay == r->StartDay && RevertDay == r->RevertDay &&
+ StartWeek == r->StartWeek && RevertWeek == r->RevertWeek &&
+ StartMonth == r->StartMonth && RevertMonth == r->RevertMonth &&
+ StartTime == r->StartTime && RevertTime == r->RevertTime &&
+ isEmptyRule() == r->isEmptyRule() )
+ return true;
+ else
+ return false;
+}