Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fix scheduler related issues resulting from the UTC changes.

By definition kTimeslot and kWeekslot recording rules work on local
time.  Change the scheduler to convert UTC times back to local time on
the fly as needed.

IMPORTANT NOTE: This means MythTV now needs working time zone support
in MySQL.  Because time zone support is not typically configured in
MySQL by most distributions at this time, users will need to do that
themselves.  See the following URL for more information.

  http://dev.mysql.com/doc/refman/5.5/en/mysql-tzinfo-to-sql.html

In mythtv-setup and mythbackend, check the MySQL time zone support and
exit immediately if it is not working.

It's simpler to keep record.findday and findtime in local time at this
point.  Undo part of schema upgrade 1305 and update RecordingRule to
accomplish that.

Change the other, recent time zone related schema upgrades to use
CONVERT_TZ() instead of simply adding the utc_offset in minutes.  This
is so future upgraders will not have the "off by 1 hour" problem on
some historical times.
  • Loading branch information...
commit 181641abbdd76e3e65f4d8bd0d9979b566a0b9f2 1 parent b0a0e56
@gigem gigem authored
View
2  mythtv/bindings/perl/MythTV.pm
@@ -115,7 +115,7 @@ package MythTV;
# schema version supported in the main code. We need to check that the schema
# version in the database is as expected by the bindings, which are expected
# to be kept in sync with the main code.
- our $SCHEMA_VERSION = "1305";
+ our $SCHEMA_VERSION = "1306";
# NUMPROGRAMLINES is defined in mythtv/libs/libmythtv/programinfo.h and is
# the number of items in a ProgramInfo QStringList group used by
View
2  mythtv/bindings/python/MythTV/static.py
@@ -5,7 +5,7 @@
"""
OWN_VERSION = (0,26,-1,1)
-SCHEMA_VERSION = 1305
+SCHEMA_VERSION = 1306
NVSCHEMA_VERSION = 1007
MUSICSCHEMA_VERSION = 1018
PROTO_VERSION = '75'
View
16 mythtv/libs/libmythbase/dbutil.cpp
@@ -867,4 +867,20 @@ void DBUtil::UnlockSchema(MSqlQuery &query)
query.exec();
}
+/** \fn CheckTimeZoneSupport(void)
+ * \brief Check if MySQL has working timz zone support.
+ */
+bool DBUtil::CheckTimeZoneSupport(void)
+{
+ MSqlQuery query(MSqlQuery::InitCon());
+ query.prepare("SELECT CONVERT_TZ(NOW(), 'SYSTEM', 'UTC')");
+ if (!query.exec() || !query.next())
+ {
+ LOG(VB_GENERAL, LOG_ERR, "MySQL time zone support check failed");
+ return false;
+ }
+
+ return !query.value(0).isNull();
+}
+
/* vim: set expandtab tabstop=4 shiftwidth=4: */
View
2  mythtv/libs/libmythbase/dbutil.h
@@ -49,6 +49,8 @@ class MBASE_PUBLIC DBUtil
static bool TryLockSchema(MSqlQuery &, uint timeout_secs);
static void UnlockSchema(MSqlQuery &);
+ static bool CheckTimeZoneSupport(void);
+
static const int kUnknownVersionNumber;
protected:
View
1  mythtv/libs/libmythbase/exitcodes.h
@@ -33,5 +33,6 @@
#define GENERIC_EXIT_DEADLOCK 150 ///< Transcode deadlock detected
#define GENERIC_EXIT_IN_USE 151 ///< Recording in use, can't flag
#define GENERIC_EXIT_START 152 ///< MythSystem process starting
+#define GENERIC_EXIT_DB_NOTIMEZONE 153 ///< Missing DB time zone support
#endif // __MYTH_EXIT_CODES__
View
2  mythtv/libs/libmythbase/mythversion.h
@@ -57,7 +57,7 @@
* mythtv/bindings/php/MythBackend.php
#endif
-#define MYTH_DATABASE_VERSION "1305"
+#define MYTH_DATABASE_VERSION "1306"
MBASE_PUBLIC const char *GetMythSourceVersion();
View
115 mythtv/libs/libmythtv/dbcheck.cpp
@@ -2061,36 +2061,42 @@ NULL
for (uint i = 0; i < sizeof(with_endtime)/sizeof(char*); i++)
{
updates_ba.push_back(
- QString("UPDATE %1 "
- "SET starttime = starttime + interval %2 minute, "
- " endtime = endtime + interval %3 minute "
- "ORDER BY %4")
- .arg(with_endtime[i]).arg(utc_offset).arg(utc_offset)
- .arg(order).toLocal8Bit());
+ QString("UPDATE %1 "
+ "SET starttime = "
+ " CONVERT_TZ(starttime, 'SYSTEM', 'UTC'), "
+ " endtime = "
+ " CONVERT_TZ(endtime, 'SYSTEM', 'UTC') "
+ "ORDER BY %4")
+ .arg(with_endtime[i])
+ .arg(order).toLocal8Bit());
}
for (uint i = 0; i < sizeof(without_endtime)/sizeof(char*); i++)
{
updates_ba.push_back(
- QString("UPDATE %1 "
- "SET starttime = starttime + interval %2 minute "
- "ORDER BY %3")
- .arg(without_endtime[i]).arg(utc_offset).arg(order)
- .toLocal8Bit());
+ QString("UPDATE %1 "
+ "SET starttime = "
+ " CONVERT_TZ(starttime, 'SYSTEM', 'UTC') "
+ "ORDER BY %3")
+ .arg(without_endtime[i]).arg(order)
+ .toLocal8Bit());
}
updates_ba.push_back(
- QString("UPDATE oldprogram "
- "SET airdate = airdate + interval %2 minute "
- "ORDER BY %3")
- .arg(utc_offset).arg((utc_offset > 0) ? "-airdate" : "airdate")
- .toLocal8Bit());
+ QString("UPDATE oldprogram "
+ "SET airdate = "
+ " CONVERT_TZ(airdate, 'SYSTEM', 'UTC') "
+ "ORDER BY %3")
+ .arg((utc_offset > 0) ? "-airdate" :
+ "airdate").toLocal8Bit());
updates_ba.push_back(
- QString("UPDATE recorded "
- "set progstart = progstart + interval %1 minute, "
- " progend = progend + interval %2 minute ")
- .arg(utc_offset).arg(utc_offset).toLocal8Bit());
+ QString("UPDATE recorded "
+ "set progstart = "
+ " CONVERT_TZ(progstart, 'SYSTEM', 'UTC'), "
+ " progend = "
+ " CONVERT_TZ(progend, 'SYSTEM', 'UTC') ")
+ .toLocal8Bit());
}
// Convert DATETIME back to seperate DATE and TIME in record table
@@ -2143,22 +2149,20 @@ NULL
for (uint i = 0; i < sizeof(with_endtime)/sizeof(char*); i++)
{
updates_ba.push_back(
- QString("UPDATE %1 "
- "SET starttime = starttime + interval %2 minute, "
- " endtime = endtime + interval %3 minute "
- "ORDER BY %4")
- .arg(with_endtime[i]).arg(utc_offset).arg(utc_offset)
- .arg(order).toLocal8Bit());
+ QString("UPDATE %1 "
+ "SET starttime = CONVERT_TZ(starttime, 'SYSTEM', 'UTC'), "
+ " endtime = CONVERT_TZ(endtime, 'SYSTEM', 'UTC') "
+ "ORDER BY %4")
+ .arg(with_endtime[i]).arg(order).toLocal8Bit());
}
for (uint i = 0; i < sizeof(without_endtime)/sizeof(char*); i++)
{
updates_ba.push_back(
- QString("UPDATE %1 "
- "SET starttime = starttime + interval %2 minute "
- "ORDER BY %3")
- .arg(without_endtime[i]).arg(utc_offset).arg(order)
- .toLocal8Bit());
+ QString("UPDATE %1 "
+ "SET starttime = CONVERT_TZ(starttime, 'SYSTEM', 'UTC') "
+ "ORDER BY %3")
+ .arg(without_endtime[i]).arg(order).toLocal8Bit());
}
}
@@ -2176,11 +2180,6 @@ NULL
if (dbver == "1304")
{
- QDateTime loc = QDateTime::currentDateTime();
- QDateTime utc = loc.toUTC();
- loc = QDateTime(loc.date(), loc.time(), Qt::UTC);
- int utc_offset = loc.secsTo(utc) / 60;
-
QList<QByteArray> updates_ba;
updates_ba.push_back(
@@ -2190,15 +2189,16 @@ NULL
"WHERE filterid=3");
updates_ba.push_back(QString(
-"UPDATE record SET "
-"findday = DAYOFWEEK(ADDTIME('2012-06-02 00:00:00', findtime) "
-" + INTERVAL %1 MINUTE + INTERVAL findday DAY) "
-"WHERE findday > 0").arg(utc_offset).toLocal8Bit());
+"UPDATE record SET findday = "
+" DAYOFWEEK(CONVERT_TZ(ADDTIME('2012-06-02 00:00:00', findtime), "
+" 'SYSTEM', 'UTC') + INTERVAL findday DAY) "
+"WHERE findday > 0").toLocal8Bit());
updates_ba.push_back(QString(
-"UPDATE record SET "
-"findtime = TIME(ADDTIME('2012-06-02 00:00:00', findtime) "
-" + INTERVAL %1 MINUTE)").arg(utc_offset).toLocal8Bit());
+"UPDATE record SET findtime = "
+" TIME(CONVERT_TZ(ADDTIME('2012-06-02 00:00:00', findtime), "
+" 'SYSTEM', 'UTC')) ")
+ .toLocal8Bit());
// Convert update ByteArrays to NULL terminated char**
QList<QByteArray>::const_iterator it = updates_ba.begin();
@@ -2211,6 +2211,35 @@ NULL
return false;
}
+ if (dbver == "1305")
+ {
+ // Reverse the findday/findtime changes from above since those
+ // values need to be kept in local time.
+
+ QList<QByteArray> updates_ba;
+
+ updates_ba.push_back(QString(
+"UPDATE record SET findday = "
+" DAYOFWEEK(CONVERT_TZ(ADDTIME('2012-06-02 00:00:00', findtime), "
+" 'UTC', 'SYSTEM') + INTERVAL findday DAY) "
+"WHERE findday > 0").toLocal8Bit());
+
+ updates_ba.push_back(QString(
+"UPDATE record SET findtime = "
+" TIME(CONVERT_TZ(ADDTIME('2012-06-02 00:00:00', findtime), "
+" 'UTC', 'SYSTEM')) ").toLocal8Bit());
+
+ // Convert update ByteArrays to NULL terminated char**
+ QList<QByteArray>::const_iterator it = updates_ba.begin();
+ vector<const char*> updates;
+ for (; it != updates_ba.end(); ++it)
+ updates.push_back((*it).constData());
+ updates.push_back(NULL);
+
+ if (!performActualUpdate(&updates[0], "1306", dbver))
+ return false;
+ }
+
return true;
}
View
25 mythtv/libs/libmythtv/recordingrule.cpp
@@ -36,7 +36,8 @@ RecordingRule::RecordingRule()
m_channelid(0),
m_findday(-1),
m_findtime(QTime::fromString("00:00:00", Qt::ISODate)),
- m_findid(QDate(1970, 1, 1).daysTo(MythDate::current().date()) + 719528),
+ m_findid(QDate(1970, 1, 1).daysTo(MythDate::current().toLocalTime().date())
+ + 719528),
m_type(kNotRecording),
m_searchType(kNoSearch),
m_recPriority(0),
@@ -262,9 +263,10 @@ bool RecordingRule::LoadBySearch(RecSearchType lsearch, QString textname,
m_title = ltitle;
m_subtitle = from;
m_description = forwhat;
- m_findday = (m_startdate.dayOfWeek() + 1) % 7;
+ QDate ldate = MythDate::current().toLocalTime().date();
+ m_findday = (ldate.dayOfWeek() + 1) % 7;
QDate epoch(1970, 1, 1);
- m_findid = epoch.daysTo(m_startdate) + 719528;
+ m_findid = epoch.daysTo(ldate) + 719528;
}
m_loaded = true;
@@ -579,9 +581,10 @@ void RecordingRule::ToMap(InfoMap &infoMap) const
if (m_type == kFindDailyRecord || m_type == kFindWeeklyRecord)
{
- QDateTime dt =
- QDateTime(MythDate::current().date(), m_findtime, Qt::UTC);
- QString findfrom = MythDate::toString(dt, MythDate::kTime);
+ QDateTime ldt =
+ QDateTime(MythDate::current().toLocalTime().date(), m_findtime,
+ Qt::LocalTime);
+ QString findfrom = MythDate::toString(ldt, MythDate::kTime);
if (m_type == kFindWeeklyRecord)
{
int daynum = (m_findday + 5) % 7 + 1;
@@ -703,12 +706,13 @@ void RecordingRule::AssignProgramInfo()
if (m_findday < 0)
{
m_findday =
- (m_progInfo->GetScheduledStartTime().date().dayOfWeek() + 1) % 7;
- m_findtime = m_progInfo->GetScheduledStartTime().time();
+ (m_progInfo->GetScheduledStartTime().toLocalTime().date()
+ .dayOfWeek() + 1) % 7;
+ m_findtime = m_progInfo->GetScheduledStartTime().toLocalTime().time();
QDate epoch(1970, 1, 1);
m_findid = epoch.daysTo(
- m_progInfo->GetScheduledStartTime().date()) + 719528;
+ m_progInfo->GetScheduledStartTime().toLocalTime().date()) + 719528;
}
else
{
@@ -718,7 +722,8 @@ void RecordingRule::AssignProgramInfo()
{
QDate epoch(1970, 1, 1);
m_findid = epoch.daysTo(
- m_progInfo->GetScheduledStartTime().date()) + 719528;
+ m_progInfo->GetScheduledStartTime().toLocalTime().date())
+ + 719528;
}
}
m_category = m_progInfo->GetCategory();
View
8 mythtv/programs/mythbackend/main_helpers.cpp
@@ -36,6 +36,7 @@
#include "mythcontext.h"
#include "mythversion.h"
#include "mythdb.h"
+#include "dbutil.h"
#include "exitcodes.h"
#include "compat.h"
#include "storagegroup.h"
@@ -528,6 +529,13 @@ void print_warnings(const MythBackendCommandLineParser &cmdline)
int run_backend(MythBackendCommandLineParser &cmdline)
{
+ if (!DBUtil::CheckTimeZoneSupport())
+ {
+ LOG(VB_GENERAL, LOG_ERR, "MySQL time zone support is not missing. "
+ "Please install it and try again.");
+ return GENERIC_EXIT_DB_NOTIMEZONE;
+ }
+
bool ismaster = gCoreContext->IsMasterHost();
if (!UpgradeTVDatabaseSchema(ismaster, ismaster))
View
46 mythtv/programs/mythbackend/scheduler.cpp
@@ -3205,11 +3205,11 @@ void Scheduler::UpdateManuals(uint recordid)
RecordingType rectype = RecordingType(query.value(0).toInt());
QString title = query.value(1).toString();
QString station = query.value(2).toString() ;
- QDateTime startdt = QDateTime(query.value(3).toDate(),
- query.value(4).toTime(), Qt::UTC);
- int duration = startdt.secsTo(
+ QDateTime lstartdt = QDateTime(query.value(3).toDate(),
+ query.value(4).toTime(), Qt::UTC).toLocalTime();
+ int duration = lstartdt.secsTo(
QDateTime(query.value(5).toDate(),
- query.value(6).toTime(), Qt::UTC)) / 60;
+ query.value(6).toTime(), Qt::UTC).toLocalTime());
query.prepare("SELECT chanid from channel "
"WHERE callsign = :STATION");
@@ -3241,17 +3241,17 @@ void Scheduler::UpdateManuals(uint recordid)
case kTimeslotRecord:
progcount = 13;
skipdays = 1;
- weekday = (startdt.toLocalTime().date().dayOfWeek() < 6);
- startdt = QDateTime(
- MythDate::current().date(), startdt.time(), Qt::UTC);
+ weekday = (lstartdt.date().dayOfWeek() < 6);
+ lstartdt = QDateTime(MythDate::current().toLocalTime().date(),
+ lstartdt.time(), Qt::LocalTime);
break;
case kWeekslotRecord:
progcount = 2;
skipdays = 7;
weekday = false;
- weeksoff = (startdt.date().daysTo(
- MythDate::current().date()) + 6) / 7;
- startdt = startdt.addDays(weeksoff * 7);
+ weeksoff = (lstartdt.date()
+ .daysTo(MythDate::current().toLocalTime().date()) + 6) / 7;
+ lstartdt = lstartdt.addDays(weeksoff * 7);
break;
default:
LOG(VB_GENERAL, LOG_ERR,
@@ -3263,7 +3263,7 @@ void Scheduler::UpdateManuals(uint recordid)
{
for (int i = 0; i < (int)chanidlist.size(); i++)
{
- if (weekday && startdt.toLocalTime().date().dayOfWeek() >= 6)
+ if (weekday && lstartdt.toLocalTime().date().dayOfWeek() >= 6)
continue;
query.prepare("REPLACE INTO program (chanid, starttime, endtime,"
@@ -3271,10 +3271,10 @@ void Scheduler::UpdateManuals(uint recordid)
"VALUES (:CHANID, :STARTTIME, :ENDTIME, :TITLE,"
" :SUBTITLE, :RECORDID, 1)");
query.bindValue(":CHANID", chanidlist[i]);
- query.bindValue(":STARTTIME", startdt);
- query.bindValue(":ENDTIME", startdt.addSecs(duration * 60));
+ query.bindValue(":STARTTIME", lstartdt.toUTC());
+ query.bindValue(":ENDTIME", lstartdt.toUTC().addSecs(duration));
query.bindValue(":TITLE", title);
- query.bindValue(":SUBTITLE", startdt);
+ query.bindValue(":SUBTITLE", lstartdt);
query.bindValue(":RECORDID", recordid);
if (!query.exec())
{
@@ -3282,7 +3282,7 @@ void Scheduler::UpdateManuals(uint recordid)
return;
}
}
- startdt = startdt.addDays(skipdays);
+ lstartdt = lstartdt.addDays(skipdays);
}
}
@@ -3433,12 +3433,12 @@ static QString progfindid = QString(
" WHEN %1 "
" THEN RECTABLE.findid "
" WHEN %2 "
-" THEN to_days(date_sub(program.starttime, interval "
-" time_format(RECTABLE.findtime, '%H:%i') hour_minute)) "
+" THEN to_days(date_sub(convert_tz(program.starttime, 'UTC', 'SYSTEM'), "
+" interval time_format(RECTABLE.findtime, '%H:%i') hour_minute)) "
" WHEN %3 "
-" THEN floor((to_days(date_sub(program.starttime, interval "
-" time_format(RECTABLE.findtime, '%H:%i') hour_minute)) - "
-" RECTABLE.findday)/7) * 7 + RECTABLE.findday "
+" THEN floor((to_days(date_sub(convert_tz(program.starttime, 'UTC', "
+" 'SYSTEM'), interval time_format(RECTABLE.findtime, '%H:%i') "
+" hour_minute)) - RECTABLE.findday)/7) * 7 + RECTABLE.findday "
" WHEN %4 "
" THEN RECTABLE.findid "
" ELSE 0 "
@@ -3572,15 +3572,15 @@ void Scheduler::UpdateMatches(uint recordid, uint sourceid, uint mplexid,
" AND "
" ((RECTABLE.type = %5) " // channelrecord
" OR"
-" ((TIME_TO_SEC(RECTABLE.starttime) = TIME_TO_SEC(program.starttime)) " // timeslot matches
+" (( TIME(CONVERT_TZ(ADDTIME(RECTABLE.startdate, RECTABLE.starttime), 'UTC', 'SYSTEM')) = TIME(CONVERT_TZ(program.starttime, 'UTC', 'SYSTEM'))) " // timeslot matches
" AND "
" ((RECTABLE.type = %6) " // timeslotrecord
" OR"
-" ((DAYOFWEEK(RECTABLE.startdate) = DAYOFWEEK(program.starttime) "
+" ((DAYOFWEEK(CONVERT_TZ(ADDTIME(RECTABLE.startdate, RECTABLE.starttime), 'UTC', 'SYSTEM')) = DAYOFWEEK(CONVERT_TZ(program.starttime, 'UTC', 'SYSTEM')) "
" AND "
" ((RECTABLE.type = %7) " // weekslotrecord
" OR"
-" ((TO_DAYS(RECTABLE.startdate) = TO_DAYS(program.starttime)) " // date matches
+" ((ADDTIME(RECTABLE.startdate, RECTABLE.starttime) = program.starttime) " // date/time matches
" AND (RECTABLE.type <> %8)" // single,override,don't,etc.
" )"
" )"
View
8 mythtv/programs/mythtv-setup/main.cpp
@@ -13,6 +13,7 @@
#include "mythconfig.h"
#include "mythcontext.h"
#include "mythdbcon.h"
+#include "dbutil.h"
#include "mythlogging.h"
#include "mythversion.h"
#include "langsettings.h"
@@ -515,6 +516,13 @@ int main(int argc, char *argv[])
return GENERIC_EXIT_NO_THEME;
}
+ if (!DBUtil::CheckTimeZoneSupport())
+ {
+ LOG(VB_GENERAL, LOG_ERR, "MySQL time zone support is not missing. "
+ "Please install it and try again.");
+ return GENERIC_EXIT_DB_NOTIMEZONE;
+ }
+
if (!UpgradeTVDatabaseSchema(true))
{
LOG(VB_GENERAL, LOG_ERR, "Couldn't upgrade database to new schema.");

0 comments on commit 181641a

Please sign in to comment.
Something went wrong with that request. Please try again.