From ca1105f139308ad265137cd221fd6b574319f8c5 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 15 Oct 2018 16:21:29 +0200 Subject: [PATCH] parseDateTime: parse timezone offset if given :boom: --- parse/arrival-or-departure.js | 9 +++++---- parse/date-time.js | 24 ++++++++++++++++-------- parse/journey-leg.js | 22 ++++++++++++---------- parse/stopover.js | 20 ++++++++++++-------- parse/warning.js | 7 ++++--- 5 files changed, 49 insertions(+), 33 deletions(-) diff --git a/parse/arrival-or-departure.js b/parse/arrival-or-departure.js index 1c3b043e..4936792c 100644 --- a/parse/arrival-or-departure.js +++ b/parse/arrival-or-departure.js @@ -14,11 +14,12 @@ const createParseArrOrDep = (profile, opt, data, prefix) => { const parseArrOrDep = (d) => { const t = d.stbStop[prefix + 'TimeR'] || d.stbStop[prefix + 'TimeS'] + const tz = d.stbStop[prefix + 'TZOffset'] || null const res = { tripId: d.jid, stop: locations[parseInt(d.stbStop.locX)] || null, - when: profile.parseDateTime(profile, d.date, t), + when: profile.parseDateTime(profile, d.date, t, tz), // todo: for arrivals, this is the *origin*, not the *direction* direction: prefix === DEPARTURE && profile.parseStationName(d.dirTxt) || null, line: lines[parseInt(d.prodX)] || null, @@ -32,8 +33,8 @@ const createParseArrOrDep = (profile, opt, data, prefix) => { const tR = d.stbStop[prefix + 'TimeR'] const tP = d.stbStop[prefix + 'TimeS'] if (tR && tP) { - const realtime = profile.parseDateTime(profile, d.date, tR, true) - const planned = profile.parseDateTime(profile, d.date, tP, true) + const realtime = profile.parseDateTime(profile, d.date, tR, tz, true) + const planned = profile.parseDateTime(profile, d.date, tP, tz, true) res.delay = Math.round((realtime - planned) / 1000) } else res.delay = null @@ -50,7 +51,7 @@ const createParseArrOrDep = (profile, opt, data, prefix) => { res.cancelled = true Object.defineProperty(res, 'canceled', {value: true}) res.when = res.delay = null - res.formerScheduledWhen = profile.parseDateTime(profile, d.date, tP) + res.formerScheduledWhen = profile.parseDateTime(profile, d.date, tP, tz) } if (opt.remarks) { diff --git a/parse/date-time.js b/parse/date-time.js index e551ea94..7d945a95 100644 --- a/parse/date-time.js +++ b/parse/date-time.js @@ -5,7 +5,7 @@ const {DateTime, IANAZone} = require('luxon') const timezones = new WeakMap() // todo: change to `(profile) => (date, time) => {}` -const parseDateTime = (profile, date, time, timestamp = false) => { +const parseDateTime = (profile, date, time, tzOffset = null, timestamp = false) => { const pDate = [date.substr(-8, 4), date.substr(-4, 2), date.substr(-2, 2)] if (!pDate[0] || !pDate[1] || !pDate[2]) { throw new Error('invalid date format: ' + date) @@ -16,7 +16,7 @@ const parseDateTime = (profile, date, time, timestamp = false) => { throw new Error('invalid time format: ' + time) } - const offset = time.length > 6 ? parseInt(time.slice(0, -6)) : 0 + const daysOffset = time.length > 6 ? parseInt(time.slice(0, -6)) : 0 let timezone if (timezones.has(profile)) timezone = timezones.get(profile) @@ -25,12 +25,20 @@ const parseDateTime = (profile, date, time, timestamp = false) => { timezones.set(profile, timezone) } - let dt = DateTime.fromISO(pDate.join('-') + 'T' + pTime.join(':'), { - locale: profile.locale, - zone: timezone - }) - if (offset > 0) dt = dt.plus({days: offset}) - return timestamp ? dt.toMillis() : dt.toISO() + if (tzOffset !== null) { + // We don't know the timezone, but only the *timezone offset*, which is why we + // can't use Luxon to process the offset. + const isoOffset = ('0' + (tzOffset / 60 | 0)).slice(-2) + ('0' + (tzOffset % 60)).slice(-2) + const isoStr = pDate.join('-') + 'T' + pTime.join(':') + '+' + isoOffset + return timestamp ? +new Date(isoStr) : isoStr + } else { + let dt = DateTime.fromISO(pDate.join('-') + 'T' + pTime.join(':'), { + locale: profile.locale, + zone: timezone + }) + if (daysOffset > 0) dt = dt.plus({days: daysOffset}) + return timestamp ? dt.toMillis() : dt.toISO() + } } module.exports = parseDateTime diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 0150295f..a574b26a 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -49,8 +49,8 @@ const createParseJourneyLeg = (profile, opt, data) => { // j = journey, pt = part // todo: pt.planrtTS const parseJourneyLeg = (j, pt, parseStopovers = true) => { - const dep = profile.parseDateTime(profile, j.date, pt.dep.dTimeR || pt.dep.dTimeS) - const arr = profile.parseDateTime(profile, j.date, pt.arr.aTimeR || pt.arr.aTimeS) + const dep = profile.parseDateTime(profile, j.date, pt.dep.dTimeR || pt.dep.dTimeS, pt.dep.dTZOffset) + const arr = profile.parseDateTime(profile, j.date, pt.arr.aTimeR || pt.arr.aTimeS, pt.arr.aTZOffset) const res = { origin: clone(locations[parseInt(pt.dep.locX)]) || null, destination: clone(locations[parseInt(pt.arr.locX)]), @@ -61,13 +61,13 @@ const createParseJourneyLeg = (profile, opt, data) => { // todo: DRY with parseDeparture // todo: DRY with parseStopover if (pt.dep.dTimeR && pt.dep.dTimeS) { - const realtime = profile.parseDateTime(profile, j.date, pt.dep.dTimeR, true) - const planned = profile.parseDateTime(profile, j.date, pt.dep.dTimeS, true) + const realtime = profile.parseDateTime(profile, j.date, pt.dep.dTimeR, pt.dep.dTZOffset, true) + const planned = profile.parseDateTime(profile, j.date, pt.dep.dTimeS, pt.dep.dTZOffset, true) res.departureDelay = Math.round((realtime - planned) / 1000) } if (pt.arr.aTimeR && pt.arr.aTimeS) { - const realtime = profile.parseDateTime(profile, j.date, pt.arr.aTimeR, true) - const planned = profile.parseDateTime(profile, j.date, pt.arr.aTimeS, true) + const realtime = profile.parseDateTime(profile, j.date, pt.arr.aTimeR, pt.dep.aTZOffset, true) + const planned = profile.parseDateTime(profile, j.date, pt.arr.aTimeS, pt.dep.aTZOffset, true) res.arrivalDelay = Math.round((realtime - planned) / 1000) } @@ -136,9 +136,9 @@ const createParseJourneyLeg = (profile, opt, data) => { let when = null, delay = null if (st0) { - const planned = st0.dTimeS && profile.parseDateTime(profile, j.date, st0.dTimeS) + const planned = st0.dTimeS && profile.parseDateTime(profile, j.date, st0.dTimeS, st0.dTZOffset) if (st0.dTimeR && planned) { - const realtime = profile.parseDateTime(profile, j.date, st0.dTimeR) + const realtime = profile.parseDateTime(profile, j.date, st0.dTimeR, st0.dTZOffset) when = realtime delay = Math.round((new Date(realtime) - new Date(planned)) / 1000) } else if (planned) when = planned @@ -161,11 +161,13 @@ const createParseJourneyLeg = (profile, opt, data) => { Object.defineProperty(res, 'canceled', {value: true}) if (pt.arr.aCncl) { res.arrival = res.arrivalPlatform = res.arrivalDelay = null - res.formerScheduledArrival = profile.parseDateTime(profile, j.date, pt.arr.aTimeS) + const arr = profile.parseDateTime(profile, j.date, pt.arr.aTimeS, pt.arr.aTZOffset) + res.formerScheduledArrival = arr } if (pt.dep.dCncl) { res.departure = res.departurePlatform = res.departureDelay = null - res.formerScheduledDeparture = profile.parseDateTime(profile, j.date, pt.dep.dTimeS) + const dep = profile.parseDateTime(profile, j.date, pt.dep.dTimeS, pt.dep.dTZOffset) + res.formerScheduledDeparture = dep } } diff --git a/parse/stopover.js b/parse/stopover.js index 1a3e7a7e..c1e8a7c2 100644 --- a/parse/stopover.js +++ b/parse/stopover.js @@ -19,20 +19,22 @@ const createParseStopover = (profile, opt, data, date) => { // todo: DRY with parseDeparture // todo: DRY with parseJourneyLeg if (st.aTimeR || st.aTimeS) { - res.arrival = profile.parseDateTime(profile, date, st.aTimeR || st.aTimeS) + const arr = profile.parseDateTime(profile, date, st.aTimeR || st.aTimeS, st.aTZOffset) + res.arrival = arr } if (st.aTimeR && st.aTimeS) { - const realtime = profile.parseDateTime(profile, date, st.aTimeR, true) - const planned = profile.parseDateTime(profile, date, st.aTimeS, true) + const realtime = profile.parseDateTime(profile, date, st.aTimeR, st.aTZOffset, true) + const planned = profile.parseDateTime(profile, date, st.aTimeS, st.aTZOffset, true) res.arrivalDelay = Math.round((realtime - planned) / 1000) } if (st.dTimeR || st.dTimeS) { - res.departure = profile.parseDateTime(profile, date, st.dTimeR || st.dTimeS) + const dep = profile.parseDateTime(profile, date, st.dTimeR || st.dTimeS, st.dTZOffset) + res.departure = dep } if (st.dTimeR && st.dTimeS) { - const realtime = profile.parseDateTime(profile, date, st.dTimeR, true) - const planned = profile.parseDateTime(profile, date, st.dTimeS, true) + const realtime = profile.parseDateTime(profile, date, st.dTimeR, st.dTZOffset, true) + const planned = profile.parseDateTime(profile, date, st.dTimeS, st.dTZOffset, true) res.departureDelay = Math.round((realtime - planned) / 1000) } @@ -55,14 +57,16 @@ const createParseStopover = (profile, opt, data, date) => { res.formerArrivalDelay = res.arrivalDelay res.arrival = res.arrivalDelay = null if (st.aTimeS) { - res.formerScheduledArrival = profile.parseDateTime(profile, date, st.aTimeS) + const arr = profile.parseDateTime(profile, date, st.aTimeS, st.aTZOffset) + res.formerScheduledArrival = arr } } if (st.dCncl) { res.formerDepartureDelay = res.departureDelay res.departure = res.departureDelay = null if (st.dTimeS) { - res.formerScheduledDeparture = profile.parseDateTime(profile, date, st.dTimeS) + const arr = profile.parseDateTime(profile, date, st.dTimeS, st.dTZOffset) + res.formerScheduledDeparture = arr } } } diff --git a/parse/warning.js b/parse/warning.js index 234a71d2..ab24e092 100644 --- a/parse/warning.js +++ b/parse/warning.js @@ -35,9 +35,10 @@ const parseWarning = (profile, w, icons) => { category: w.cat || null // todo: parse to sth meaningful } - if (w.sDate && w.sTime) res.validFrom = parseDateTime(profile, w.sDate, w.sTime) - if (w.eDate && w.eTime) res.validUntil = parseDateTime(profile, w.eDate, w.eTime) - if (w.lModDate && w.lModTime) res.modified = parseDateTime(profile, w.lModDate, w.lModTime) + // todo: pass tzOffset to `parseDateTime` + if (w.sDate && w.sTime) res.validFrom = parseDateTime(profile, w.sDate, w.sTime, null) + if (w.eDate && w.eTime) res.validUntil = parseDateTime(profile, w.eDate, w.eTime, null) + if (w.lModDate && w.lModTime) res.modified = parseDateTime(profile, w.lModDate, w.lModTime, null) return res }