Skip to content

ArriveBy bug for midnight-crossing trips #6962

@svedbod

Description

@svedbod

Expected behavior

In arriveBy searches, trips should only be included if they arrive before or at the specified latestArrivalTime time. For midnight-crossing trips (trips with service dates from the previous day but scheduled times > 24 hours), the actual arrival time should be correctly calculated and compared against the latestArrivalTime.

Observed behavior

Midnight-crossing trips that arrive after the specified latestArrivalTime time are incorrectly included in search results. These trips appear with incorrect arrival times in the response, making them seem to arrive before the latestArrivalTime when they actually arrive after it.

Example

A trip scheduled to arrive at 03:39 CEST (01:39 UTC) appears as arriving at 01:46 CEST (23:46 UTC) and is included in results for an arriveBy search with latestArrivalTime 00:01 UTC, even though it actually arrives 1h 38min after the latestArrivalTime.

Version of OTP used (exact commit hash or JAR name)

2.9.0-SNAPSHOT commit 69dfcbe (dev-2.x)

Data sets in use (links to GTFS and OSM PBF files)

Skånetrafiken NeTEx data (ST_netex.zip) containing the following midnight-crossing ServiceJourney:

  <ServiceJourney version="any" id="SE:276:ServiceJourney:1120000357544809">
      <TransportMode>rail</TransportMode>
      <dayTypes>
          <DayTypeRef ref="SE:276:DayType:848"/>
      </dayTypes>
      <JourneyPatternRef ref="SE:276:JourneyPattern:1129420000114658" version="any"/>
      <passingTimes>
          <TimetabledPassingTime version="any" id="SE:276:TimetabledPassingTime:1120000357544795">
              <StopPointInJourneyPatternRef ref="SE:276:StopPointInJourneyPattern:1129420000114658_1120000357543682" version="any"/>
              <DepartureTime>02:35:00</DepartureTime>
              <DepartureDayOffset>1</DepartureDayOffset>
          </TimetabledPassingTime>
          <!-- ... intermediate stops ... -->
          <TimetabledPassingTime version="any" id="SE:276:TimetabledPassingTime:1120000357544805">
              <StopPointInJourneyPatternRef ref="SE:276:StopPointInJourneyPattern:1129420000114658_1120000357543692" version="any"/>
              <ArrivalTime>03:39:00</ArrivalTime>
              <ArrivalDayOffset>1</ArrivalDayOffset>
          </TimetabledPassingTime>
      </passingTimes>
  </ServiceJourney>

Note the DayOffset=1 indicating this is a midnight-crossing trip arriving at 03:39 on the day after the service date.

Command line used to start OTP

java -Xmx16G -Dlogging.level.org.opentripplanner.routing.algorithm.raptoradapter.router.TransitRouter=DEBUG -Dlogging.level.org.opentripplanner.ext.realtimeresolver.RealtimeResolver=DEBUG -Dlogging.level.org.opentripplanner.routing.algorithm.mapping.RoutingResponseMapper=DEBUG -jar otp-shaded/target/otp-shaded-2.9.0-SNAPSHOT.jar --load /Users/ivanaladjoff/dev/skane/otpbygg/default

Router config and graph build config JSON

Relevant router config:

  {
    "routingDefaults": {
      "itineraryFilters": {
        "filterItinerariesWithSameFirstOrLastTrip": false,
        "groupSimilarityKeepOne": 0.99,
        "transitGeneralizedCostLimit": {
          "costLimitFunction": "900 + 1.5 x",
          "intervalRelaxFactor": 0.4
        }
      },
      "transferSlack": 180,
      "accessEgress": {
        "maxDuration": "PT50M"
      }
    },
    "transit": {
      "pagingSearchWindowAdjustments": [ "12h", "0h"],
      "dynamicSearchWindow": {
        "maxWindow": "PT24H"
      }
    },
    "updaters": [
      {
        "type": "siri-azure-et-updater",
        "customMidnight": "4",
        "fuzzyTripMatching": false,
        "feedId": "ST"
      }
    ]
  }

Note: customMidnight: 4 indicates service day changes at 04:00.

build-config.json:

{
  "areaVisibility": true,
  "platformEntriesLinking": true,
  "dataImportReport": true,
  "maxTransferDuration": "PT1504S",
  "maxStopToShapeSnapDistance": 500,
  "transitModelTimeZone": "Europe/Stockholm",
  "transitServiceStart": "2023-11-01",
  "transitServiceEnd": "2025-12-31",
  "islandPruning": {
    "islandWithoutStopsMaxSize": 5,
    "islandWithStopsMaxSize": 2
  },
  "osmDefaults": {
    "timeZone": "Europe/Stockholm",
    "osmTagMapping": "norway"
  },
  "netexDefaults" : {
    "feedId": "ST",
    "sharedFilePattern" : "ST_stops.xml",
    "sharedGroupFilePattern" : "(\\w{2})_shared_data.xml",
    "groupFilePattern" : "(\\w{2})_line.*\\.xml",
    "noTransfersOnIsolatedStops": true
  },
  "gtfsDefaults": {
    "discardMinTransferTimes": true
  },
  "transitRouteToStationCentroid": [ ...

Steps to reproduce the problem

  1. Build a graph with GTFS data containing midnight-crossing trips (trips with times > 24:00:00)
  2. Execute the following GraphQL query:
{
   trip(
     from: { coordinates: { latitude: 55.609224, longitude: 13.000547 } }
     to: { coordinates: { latitude: 55.708507, longitude: 13.191408 } }
     dateTime: "2025-10-13T00:01:00Z"
     arriveBy: true
     numTripPatterns: 5
   ) {
     tripPatterns {
       legs {
         mode
         aimedEndTime
         serviceDate
         toPlace { name }
         serviceJourney { id }
       }
     }
   }
 }
  1. Observe that ServiceJourney ST:SE:276:ServiceJourney:1120000357544809 is returned with:
    - Displayed arrival time: 01:46 CEST (23:46 UTC) - appears before latestArrivalTime
    - Actual arrival time at that stop: 03:39 CEST (01:39 UTC) - after latestArrivalTime
  2. The trip should be excluded from results since it arrives at 01:39 UTC, which is after the 00:01 UTC latestArrivalTime specified in the query.

Note: The bug specifically affects arriveBy searches with midnight-crossing trips. Regular trips and departBy searches work correctly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions