Skip to content

Commit

Permalink
Remove XMLTV TimeOffset setting and improve xmltv date parsing
Browse files Browse the repository at this point in the history
The XMLTV spec requires that the grabber do one of two things:
1) Supply dates and times in GMT
2) Include a timezone/time offset in ISO8601 format with datetime
strings

Since we require grabbers and xmltv files to be XMLTV compliant the
TimeOffset setting makes no sense and is only likely to cause
mis-configurations.

The date string parsing has been improved to handling more legitimate
strings and leverages QDateTimes parsing instead of duplicating it.
  • Loading branch information
stuartm committed Apr 4, 2013
1 parent 7683a3e commit ff5ab27
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 189 deletions.
162 changes: 60 additions & 102 deletions mythtv/programs/mythfilldatabase/xmltvparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,44 +116,12 @@ ChannelInfo *XMLTVParser::parseChannel(QDomElement &element, QUrl &baseUrl)
return chaninfo;
}

static int TimezoneToInt (QString timezone)
static void fromXMLTVDate(QString &timestr, QDateTime &dt)
{
// we signal an error by setting it invalid (> 840min = 14hr)
int result = 841;
// The XMLTV spec requires dates to either be in UTC/GMT or to specify a
// valid timezone. We are sticking to the spec and require all grabbers
// to comply.

if (timezone.toUpper() == "UTC" || timezone.toUpper() == "GMT")
return 0;

if (timezone.length() == 5)
{
bool ok;

result = timezone.mid(1,2).toInt(&ok, 10);

if (!ok)
result = 841;
else
{
result *= 60;

int min = timezone.right(2).toInt(&ok, 10);

if (!ok)
result = 841;
else
{
result += min;
if (timezone.left(1) == "-")
result *= -1;
}
}
}
return result;
}

// localTimezoneOffset: 841 == "None", -841 == "Auto", other == fixed offset
static void fromXMLTVDate(QString &timestr, QDateTime &dt, int localTimezoneOffset = 841)
{
if (timestr.isEmpty())
{
LOG(VB_XMLTV, LOG_ERR, "Found empty Date/Time in XMLTV data, ignoring");
Expand All @@ -162,62 +130,76 @@ static void fromXMLTVDate(QString &timestr, QDateTime &dt, int localTimezoneOffs

QStringList split = timestr.split(" ");
QString ts = split[0];
bool ok;
int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0;
QDateTime tmpDT;
tmpDT.setTimeSpec(Qt::LocalTime);

// UTC/GMT, just strip
if (ts.endsWith('Z'))
ts.truncate(ts.length()-1);

if (ts.length() == 14)
{
year = ts.left(4).toInt(&ok, 10);
month = ts.mid(4,2).toInt(&ok, 10);
day = ts.mid(6,2).toInt(&ok, 10);
hour = ts.mid(8,2).toInt(&ok, 10);
min = ts.mid(10,2).toInt(&ok, 10);
sec = ts.mid(12,2).toInt(&ok, 10);
tmpDT = QDateTime::fromString(ts, "yyyyMMddHHmmss");
}
else if (ts.length() == 12)
{
year = ts.left(4).toInt(&ok, 10);
month = ts.mid(4,2).toInt(&ok, 10);
day = ts.mid(6,2).toInt(&ok, 10);
hour = ts.mid(8,2).toInt(&ok, 10);
min = ts.mid(10,2).toInt(&ok, 10);
sec = 0;
tmpDT = QDateTime::fromString(ts, "yyyyMMddHHmm");
}
else
else if (ts.length() == 8)
{
tmpDT = QDateTime::fromString(ts, "yyyyMMdd");
}
else if (ts.length() == 6)
{
tmpDT = QDateTime::fromString(ts, "yyyyMM");
}
else if (ts.length() == 4)
{
tmpDT = QDateTime::fromString(ts, "yyyy");
}

if (!tmpDT.isValid())
{
LOG(VB_GENERAL, LOG_ERR,
QString("Ignoring unknown timestamp format: %1")
.arg(ts));
return;
}

dt = QDateTime(QDate(year, month, day),QTime(hour, min, sec),
Qt::LocalTime);

if ((split.size() > 1) && (localTimezoneOffset <= 840))

if (split.size() > 1)
{
QString tmp = split[1].trimmed();

int ts_offset = TimezoneToInt(tmp);
if (abs(ts_offset) > 840)
{
ts_offset = 0;
localTimezoneOffset = 841;
}
dt = dt.addSecs(-ts_offset * 60);

// These shouldn't be required and they aren't ISO 8601 but the
// xmltv spec mentions these and just these so handle them just in
// case
if (tmp == "GMT" || tmp == "UTC")
tmp = "+0000";
else if (tmp == "BST")
tmp = "+0100";

// While this seems like a hack, it's better than what was done before
QString isoDateString = QString("%1 %2").arg(tmpDT.toString(Qt::ISODate))
.arg(tmp);
dt = QDateTime::fromString(isoDateString, Qt::ISODate).toUTC();
}

if (localTimezoneOffset < -840)
if (!dt.isValid())
{
dt.setTimeSpec(Qt::UTC);
}
else if (abs(localTimezoneOffset) <= 840)
{
dt = dt.addSecs(localTimezoneOffset * 60 );
static bool warned_once_on_implicit_utc = false;
if (!warned_once_on_implicit_utc)
{
LOG(VB_XMLTV, LOG_ERR, "No explicit time zone found, "
"guessing implicit UTC! Please consider enhancing "
"the guide source to provice explicit UTC or local "
"time instead.");
warned_once_on_implicit_utc = true;
}
dt = tmpDT;
}

dt = dt.toUTC();

dt.setTimeSpec(Qt::UTC);
timestr = MythDate::toString(dt, MythDate::kFilename);
}

Expand Down Expand Up @@ -286,19 +268,18 @@ static void parseAudio(QDomElement &element, ProgInfo *pginfo)
}
}

ProgInfo *XMLTVParser::parseProgram(
QDomElement &element, int localTimezoneOffset)
ProgInfo *XMLTVParser::parseProgram(QDomElement &element)
{
QString uniqueid, season, episode;
int dd_progid_done = 0;
ProgInfo *pginfo = new ProgInfo();

QString text = element.attribute("start", "");
fromXMLTVDate(text, pginfo->starttime, localTimezoneOffset);
fromXMLTVDate(text, pginfo->starttime);
pginfo->startts = text;

text = element.attribute("stop", "");
fromXMLTVDate(text, pginfo->endtime, localTimezoneOffset);
fromXMLTVDate(text, pginfo->endtime);
pginfo->endts = text;

text = element.attribute("channel", "");
Expand Down Expand Up @@ -422,8 +403,7 @@ ProgInfo *XMLTVParser::parseProgram(
if (!prevdate.isEmpty())
{
QDateTime date;
fromXMLTVDate(prevdate, date,
localTimezoneOffset);
fromXMLTVDate(prevdate, date);
pginfo->originalairdate = date.date();
}
}
Expand Down Expand Up @@ -610,28 +590,6 @@ bool XMLTVParser::parseFile(

f.close();

// now we calculate the localTimezoneOffset, so that we can fix
// the programdata if needed
QString config_offset = gCoreContext->GetSetting("TimeOffset", "None");
// we disable this feature by setting it invalid (> 840min = 14hr)
int localTimezoneOffset = 841;

if (config_offset == "Auto")
{
// we mark auto with the -ve of the disable magic number
localTimezoneOffset = -841;
}
else if (config_offset != "None")
{
localTimezoneOffset = TimezoneToInt(config_offset);
if (abs(localTimezoneOffset) > 840)
{
LOG(VB_XMLTV, LOG_ERR, QString("Ignoring invalid TimeOffset %1")
.arg(config_offset));
localTimezoneOffset = 841;
}
}

QDomElement docElem = doc.documentElement();

QUrl baseUrl(docElem.attribute("source-data-url", ""));
Expand Down Expand Up @@ -664,7 +622,7 @@ bool XMLTVParser::parseFile(
}
else if (e.tagName() == "programme")
{
ProgInfo *pginfo = parseProgram(e, localTimezoneOffset);
ProgInfo *pginfo = parseProgram(e);

if (pginfo->startts == pginfo->endts)
{
Expand Down
2 changes: 1 addition & 1 deletion mythtv/programs/mythfilldatabase/xmltvparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class XMLTVParser
XMLTVParser();

ChannelInfo *parseChannel(QDomElement &element, QUrl &baseUrl);
ProgInfo *parseProgram(QDomElement &element, int localTimezoneOffset);
ProgInfo *parseProgram(QDomElement &element);
bool parseFile(QString filename, ChannelInfoList *chanlist,
QMap<QString, QList<ProgInfo> > *proglist);

Expand Down
86 changes: 0 additions & 86 deletions mythtv/programs/mythtv-setup/backendsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,90 +273,6 @@ static HostLineEdit *MiscStatusScript()
return he;
}

static void init_time_offsets(GlobalComboBox *gc)
{
gc->addSelection("None");
gc->addSelection("Auto");
gc->addSelection("+0030");
gc->addSelection("+0100");
gc->addSelection("+0130");
gc->addSelection("+0200");
gc->addSelection("+0230");
gc->addSelection("+0300");
gc->addSelection("+0330");
gc->addSelection("+0400");
gc->addSelection("+0430");
gc->addSelection("+0500");
gc->addSelection("+0530");
gc->addSelection("+0600");
gc->addSelection("+0630");
gc->addSelection("+0700");
gc->addSelection("+0730");
gc->addSelection("+0800");
gc->addSelection("+0830");
gc->addSelection("+0900");
gc->addSelection("+0930");
gc->addSelection("+1000");
gc->addSelection("+1030");
gc->addSelection("+1100");
gc->addSelection("+1130");
gc->addSelection("+1200");
gc->addSelection("-1100");
gc->addSelection("-1030");
gc->addSelection("-1000");
gc->addSelection("-0930");
gc->addSelection("-0900");
gc->addSelection("-0830");
gc->addSelection("-0800");
gc->addSelection("-0730");
gc->addSelection("-0700");
gc->addSelection("-0630");
gc->addSelection("-0600");
gc->addSelection("-0530");
gc->addSelection("-0500");
gc->addSelection("-0430");
gc->addSelection("-0400");
gc->addSelection("-0330");
gc->addSelection("-0300");
gc->addSelection("-0230");
gc->addSelection("-0200");
gc->addSelection("-0130");
gc->addSelection("-0100");
gc->addSelection("-0030");
}

static GlobalComboBox *TimeOffset()
{
GlobalComboBox *gc = new GlobalComboBox("TimeOffset");
gc->setLabel(QObject::tr("Your local time zone (for XMLTV)"));
init_time_offsets(gc);
QString helptext = QObject::tr(
"Used if the XMLTV data comes from a different time zone than your "
"own and modifies the date and time before insertion into the "
"database. 'Auto' converts the XMLTV time to local time using your "
"computer's time zone. "
"'None' ignores the XMLTV time zone, interpreting times as local.");
gc->setHelpText(helptext);
return gc;
};

#if 0
static GlobalComboBox *EITTimeOffset()
{
GlobalComboBox *gc = new GlobalComboBox("EITTimeOffset");
gc->setLabel(QObject::tr("Time offset for EIT listings"));
init_time_offsets(gc);
gc->setValue(1);
QString helptext = QObject::tr(
"Adjust the relative time zone of the EIT EPG data. "
"'Auto' converts the EIT time to local time using your "
"computer's time zone. "
"'None' ignores the EIT time zone, interpreting times as local.");
gc->setHelpText(helptext);
return gc;
};
#endif

static GlobalSpinBox *EITTransportTimeout()
{
GlobalSpinBox *gc = new GlobalSpinBox("EITTransportTimeout", 1, 15, 1);
Expand Down Expand Up @@ -918,7 +834,6 @@ BackendSettings::BackendSettings() {
locale->addChild(TVFormat());
locale->addChild(VbiFormat());
locale->addChild(FreqTable());
locale->addChild(TimeOffset());
addChild(locale);

VerticalConfigurationGroup* group2 = new VerticalConfigurationGroup(false);
Expand Down Expand Up @@ -947,7 +862,6 @@ BackendSettings::BackendSettings() {

VerticalConfigurationGroup* group2a1 = new VerticalConfigurationGroup(false);
group2a1->setLabel(QObject::tr("EIT Scanner Options"));
//group2a1->addChild(EITTimeOffset());
group2a1->addChild(EITTransportTimeout());
group2a1->addChild(EITCrawIdleStart());
addChild(group2a1);
Expand Down

0 comments on commit ff5ab27

Please sign in to comment.