Skip to content

Commit

Permalink
Fix scheduler related issues resulting from the UTC changes.
Browse files Browse the repository at this point in the history
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
gigem committed Jun 16, 2012
1 parent b0a0e56 commit 181641a
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 79 deletions.
2 changes: 1 addition & 1 deletion mythtv/bindings/perl/MythTV.pm
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion mythtv/bindings/python/MythTV/static.py
Expand Up @@ -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'
Expand Down
16 changes: 16 additions & 0 deletions mythtv/libs/libmythbase/dbutil.cpp
Expand Up @@ -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: */
2 changes: 2 additions & 0 deletions mythtv/libs/libmythbase/dbutil.h
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions mythtv/libs/libmythbase/exitcodes.h
Expand Up @@ -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__
2 changes: 1 addition & 1 deletion mythtv/libs/libmythbase/mythversion.h
Expand Up @@ -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();
Expand Down
115 changes: 72 additions & 43 deletions mythtv/libs/libmythtv/dbcheck.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -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());
}
}

Expand All @@ -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(
Expand All @@ -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();
Expand All @@ -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;
}

Expand Down
25 changes: 15 additions & 10 deletions mythtv/libs/libmythtv/recordingrule.cpp
Expand Up @@ -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),
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
{
Expand All @@ -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();
Expand Down
8 changes: 8 additions & 0 deletions mythtv/programs/mythbackend/main_helpers.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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))
Expand Down

0 comments on commit 181641a

Please sign in to comment.