parseDateTime: parse timezone offset if given 💥

This commit is contained in:
Jannis R 2018-10-15 16:21:29 +02:00
parent a9fd9ff814
commit ca1105f139
No known key found for this signature in database
GPG key ID: 0FE83946296A88A5
5 changed files with 49 additions and 33 deletions

View file

@ -14,11 +14,12 @@ const createParseArrOrDep = (profile, opt, data, prefix) => {
const parseArrOrDep = (d) => { const parseArrOrDep = (d) => {
const t = d.stbStop[prefix + 'TimeR'] || d.stbStop[prefix + 'TimeS'] const t = d.stbStop[prefix + 'TimeR'] || d.stbStop[prefix + 'TimeS']
const tz = d.stbStop[prefix + 'TZOffset'] || null
const res = { const res = {
tripId: d.jid, tripId: d.jid,
stop: locations[parseInt(d.stbStop.locX)] || null, 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* // todo: for arrivals, this is the *origin*, not the *direction*
direction: prefix === DEPARTURE && profile.parseStationName(d.dirTxt) || null, direction: prefix === DEPARTURE && profile.parseStationName(d.dirTxt) || null,
line: lines[parseInt(d.prodX)] || null, line: lines[parseInt(d.prodX)] || null,
@ -32,8 +33,8 @@ const createParseArrOrDep = (profile, opt, data, prefix) => {
const tR = d.stbStop[prefix + 'TimeR'] const tR = d.stbStop[prefix + 'TimeR']
const tP = d.stbStop[prefix + 'TimeS'] const tP = d.stbStop[prefix + 'TimeS']
if (tR && tP) { if (tR && tP) {
const realtime = profile.parseDateTime(profile, d.date, tR, true) const realtime = profile.parseDateTime(profile, d.date, tR, tz, true)
const planned = profile.parseDateTime(profile, d.date, tP, true) const planned = profile.parseDateTime(profile, d.date, tP, tz, true)
res.delay = Math.round((realtime - planned) / 1000) res.delay = Math.round((realtime - planned) / 1000)
} else res.delay = null } else res.delay = null
@ -50,7 +51,7 @@ const createParseArrOrDep = (profile, opt, data, prefix) => {
res.cancelled = true res.cancelled = true
Object.defineProperty(res, 'canceled', {value: true}) Object.defineProperty(res, 'canceled', {value: true})
res.when = res.delay = null 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) { if (opt.remarks) {

View file

@ -5,7 +5,7 @@ const {DateTime, IANAZone} = require('luxon')
const timezones = new WeakMap() const timezones = new WeakMap()
// todo: change to `(profile) => (date, time) => {}` // 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)] const pDate = [date.substr(-8, 4), date.substr(-4, 2), date.substr(-2, 2)]
if (!pDate[0] || !pDate[1] || !pDate[2]) { if (!pDate[0] || !pDate[1] || !pDate[2]) {
throw new Error('invalid date format: ' + date) 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) 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 let timezone
if (timezones.has(profile)) timezone = timezones.get(profile) if (timezones.has(profile)) timezone = timezones.get(profile)
@ -25,12 +25,20 @@ const parseDateTime = (profile, date, time, timestamp = false) => {
timezones.set(profile, timezone) timezones.set(profile, timezone)
} }
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(':'), { let dt = DateTime.fromISO(pDate.join('-') + 'T' + pTime.join(':'), {
locale: profile.locale, locale: profile.locale,
zone: timezone zone: timezone
}) })
if (offset > 0) dt = dt.plus({days: offset}) if (daysOffset > 0) dt = dt.plus({days: daysOffset})
return timestamp ? dt.toMillis() : dt.toISO() return timestamp ? dt.toMillis() : dt.toISO()
}
} }
module.exports = parseDateTime module.exports = parseDateTime

View file

@ -49,8 +49,8 @@ const createParseJourneyLeg = (profile, opt, data) => {
// j = journey, pt = part // j = journey, pt = part
// todo: pt.planrtTS // todo: pt.planrtTS
const parseJourneyLeg = (j, pt, parseStopovers = true) => { const parseJourneyLeg = (j, pt, parseStopovers = true) => {
const dep = profile.parseDateTime(profile, j.date, pt.dep.dTimeR || pt.dep.dTimeS) 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) const arr = profile.parseDateTime(profile, j.date, pt.arr.aTimeR || pt.arr.aTimeS, pt.arr.aTZOffset)
const res = { const res = {
origin: clone(locations[parseInt(pt.dep.locX)]) || null, origin: clone(locations[parseInt(pt.dep.locX)]) || null,
destination: clone(locations[parseInt(pt.arr.locX)]), destination: clone(locations[parseInt(pt.arr.locX)]),
@ -61,13 +61,13 @@ const createParseJourneyLeg = (profile, opt, data) => {
// todo: DRY with parseDeparture // todo: DRY with parseDeparture
// todo: DRY with parseStopover // todo: DRY with parseStopover
if (pt.dep.dTimeR && pt.dep.dTimeS) { if (pt.dep.dTimeR && pt.dep.dTimeS) {
const realtime = profile.parseDateTime(profile, j.date, pt.dep.dTimeR, 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, true) const planned = profile.parseDateTime(profile, j.date, pt.dep.dTimeS, pt.dep.dTZOffset, true)
res.departureDelay = Math.round((realtime - planned) / 1000) res.departureDelay = Math.round((realtime - planned) / 1000)
} }
if (pt.arr.aTimeR && pt.arr.aTimeS) { if (pt.arr.aTimeR && pt.arr.aTimeS) {
const realtime = profile.parseDateTime(profile, j.date, pt.arr.aTimeR, 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, true) const planned = profile.parseDateTime(profile, j.date, pt.arr.aTimeS, pt.dep.aTZOffset, true)
res.arrivalDelay = Math.round((realtime - planned) / 1000) res.arrivalDelay = Math.round((realtime - planned) / 1000)
} }
@ -136,9 +136,9 @@ const createParseJourneyLeg = (profile, opt, data) => {
let when = null, delay = null let when = null, delay = null
if (st0) { 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) { 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 when = realtime
delay = Math.round((new Date(realtime) - new Date(planned)) / 1000) delay = Math.round((new Date(realtime) - new Date(planned)) / 1000)
} else if (planned) when = planned } else if (planned) when = planned
@ -161,11 +161,13 @@ const createParseJourneyLeg = (profile, opt, data) => {
Object.defineProperty(res, 'canceled', {value: true}) Object.defineProperty(res, 'canceled', {value: true})
if (pt.arr.aCncl) { if (pt.arr.aCncl) {
res.arrival = res.arrivalPlatform = res.arrivalDelay = null 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) { if (pt.dep.dCncl) {
res.departure = res.departurePlatform = res.departureDelay = null 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
} }
} }

View file

@ -19,20 +19,22 @@ const createParseStopover = (profile, opt, data, date) => {
// todo: DRY with parseDeparture // todo: DRY with parseDeparture
// todo: DRY with parseJourneyLeg // todo: DRY with parseJourneyLeg
if (st.aTimeR || st.aTimeS) { 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) { if (st.aTimeR && st.aTimeS) {
const realtime = profile.parseDateTime(profile, date, st.aTimeR, true) const realtime = profile.parseDateTime(profile, date, st.aTimeR, st.aTZOffset, true)
const planned = profile.parseDateTime(profile, date, st.aTimeS, true) const planned = profile.parseDateTime(profile, date, st.aTimeS, st.aTZOffset, true)
res.arrivalDelay = Math.round((realtime - planned) / 1000) res.arrivalDelay = Math.round((realtime - planned) / 1000)
} }
if (st.dTimeR || st.dTimeS) { 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) { if (st.dTimeR && st.dTimeS) {
const realtime = profile.parseDateTime(profile, date, st.dTimeR, true) const realtime = profile.parseDateTime(profile, date, st.dTimeR, st.dTZOffset, true)
const planned = profile.parseDateTime(profile, date, st.dTimeS, true) const planned = profile.parseDateTime(profile, date, st.dTimeS, st.dTZOffset, true)
res.departureDelay = Math.round((realtime - planned) / 1000) res.departureDelay = Math.round((realtime - planned) / 1000)
} }
@ -55,14 +57,16 @@ const createParseStopover = (profile, opt, data, date) => {
res.formerArrivalDelay = res.arrivalDelay res.formerArrivalDelay = res.arrivalDelay
res.arrival = res.arrivalDelay = null res.arrival = res.arrivalDelay = null
if (st.aTimeS) { 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) { if (st.dCncl) {
res.formerDepartureDelay = res.departureDelay res.formerDepartureDelay = res.departureDelay
res.departure = res.departureDelay = null res.departure = res.departureDelay = null
if (st.dTimeS) { if (st.dTimeS) {
res.formerScheduledDeparture = profile.parseDateTime(profile, date, st.dTimeS) const arr = profile.parseDateTime(profile, date, st.dTimeS, st.dTZOffset)
res.formerScheduledDeparture = arr
} }
} }
} }

View file

@ -35,9 +35,10 @@ const parseWarning = (profile, w, icons) => {
category: w.cat || null // todo: parse to sth meaningful category: w.cat || null // todo: parse to sth meaningful
} }
if (w.sDate && w.sTime) res.validFrom = parseDateTime(profile, w.sDate, w.sTime) // todo: pass tzOffset to `parseDateTime`
if (w.eDate && w.eTime) res.validUntil = parseDateTime(profile, w.eDate, w.eTime) if (w.sDate && w.sTime) res.validFrom = parseDateTime(profile, w.sDate, w.sTime, null)
if (w.lModDate && w.lModTime) res.modified = parseDateTime(profile, w.lModDate, w.lModTime) 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 return res
} }