Skip to content

Commit 8762ba7

Browse files
Improve time handling
1 parent 52204f4 commit 8762ba7

File tree

7 files changed

+75
-37
lines changed

7 files changed

+75
-37
lines changed

application/src/ext/java/org/opentripplanner/ext/ojp/mapping/StopEventResponseMapper.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -187,22 +187,22 @@ private static boolean isNone(PickDrop pickDrop) {
187187

188188
private ServiceDepartureStructure serviceDeparture(TripTimeOnDate tripTimeOnDate) {
189189
var departure = new ServiceDepartureStructure()
190-
.withTimetabledTime(new XmlDateTime(tripTimeOnDate.scheduledDepartureAt(zoneId)));
190+
.withTimetabledTime(new XmlDateTime(tripTimeOnDate.scheduledDeparture().atZone(zoneId)));
191191
tripTimeOnDate
192-
.realtimeDepartureAt(zoneId)
192+
.realtimeDeparture()
193193
.filter(d -> optionalFeatures.contains(REALTIME_DATA))
194-
.ifPresent(time -> departure.withEstimatedTime(new XmlDateTime(time)));
194+
.ifPresent(time -> departure.withEstimatedTime(new XmlDateTime(time.atZone(zoneId))));
195195
return departure;
196196
}
197197

198198
private ServiceArrivalStructure serviceArrival(TripTimeOnDate tripTimeOnDate) {
199199
var arrival = new ServiceArrivalStructure()
200-
.withTimetabledTime(new XmlDateTime(tripTimeOnDate.scheduledArrivalAt(zoneId)));
200+
.withTimetabledTime(new XmlDateTime(tripTimeOnDate.scheduledArrival().atZone(zoneId)));
201201

202202
tripTimeOnDate
203-
.realtimeArrivalAt(zoneId)
203+
.realtimeArrival()
204204
.filter(d -> optionalFeatures.contains(REALTIME_DATA))
205-
.ifPresent(time -> arrival.withEstimatedTime(new XmlDateTime(time)));
205+
.ifPresent(time -> arrival.withEstimatedTime(new XmlDateTime(time.atZone(zoneId))));
206206
return arrival;
207207
}
208208

application/src/main/java/org/opentripplanner/model/TripTimeOnDate.java

+20-16
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
import java.time.Instant;
44
import java.time.LocalDate;
5-
import java.time.ZoneId;
6-
import java.time.ZonedDateTime;
75
import java.util.ArrayList;
86
import java.util.Comparator;
97
import java.util.List;
@@ -169,8 +167,11 @@ public int getScheduledArrival() {
169167
return tripTimes.getScheduledArrivalTime(stopIndex);
170168
}
171169

172-
public ZonedDateTime scheduledArrivalAt(ZoneId zoneId) {
173-
return toZonedDateTime(getScheduledArrival(), zoneId);
170+
/**
171+
* Returns the scheduled arrival as an Instant.
172+
*/
173+
public Instant scheduledArrival() {
174+
return toInstant(getScheduledArrival());
174175
}
175176

176177
/**
@@ -184,8 +185,11 @@ public int getScheduledDeparture() {
184185
return tripTimes.getScheduledDepartureTime(stopIndex);
185186
}
186187

187-
public ZonedDateTime scheduledDepartureAt(ZoneId zoneId) {
188-
return toZonedDateTime(getScheduledDeparture(), zoneId);
188+
/**
189+
* Returns the scheduled departure as an Instant.
190+
*/
191+
public Instant scheduledDeparture() {
192+
return toInstant(getScheduledDeparture());
189193
}
190194

191195
public int getRealtimeArrival() {
@@ -393,17 +397,17 @@ public List<TripTimeOnDate> nextTimes() {
393397
}
394398

395399
/**
396-
* Returns the real time arrival, if available, at the given zone.
400+
* Returns the real time arrival, if available.
397401
*/
398-
public Optional<ZonedDateTime> realtimeArrivalAt(ZoneId zoneId) {
399-
return optionalZonedDateTime(getRealtimeArrival(), zoneId);
402+
public Optional<Instant> realtimeArrival() {
403+
return optionalInstant(getRealtimeArrival());
400404
}
401405

402406
/**
403-
* Returns the real time departure, if available, at the given zone.
407+
* Returns the real time departure, if available.
404408
*/
405-
public Optional<ZonedDateTime> realtimeDepartureAt(ZoneId zoneId) {
406-
return optionalZonedDateTime(getRealtimeDeparture(), zoneId);
409+
public Optional<Instant> realtimeDeparture() {
410+
return optionalInstant(getRealtimeDeparture());
407411
}
408412

409413
private TripTimeOnDate atStopIndex(int stopIndex) {
@@ -420,15 +424,15 @@ private TripTimeOnDate atStopIndex(int stopIndex) {
420424
* If real time data is available for this stop (call) then it is returned or an empty Optional
421425
* otherwise.
422426
*/
423-
private Optional<ZonedDateTime> optionalZonedDateTime(int secondsSinceMidnight, ZoneId zoneId) {
427+
private Optional<Instant> optionalInstant(int secondsSinceMidnight) {
424428
if (isCancelledStop() || isRealtime()) {
425-
return Optional.of(toZonedDateTime(secondsSinceMidnight, zoneId));
429+
return Optional.of(toInstant(secondsSinceMidnight));
426430
} else {
427431
return Optional.empty();
428432
}
429433
}
430434

431-
private ZonedDateTime toZonedDateTime(int secondsSinceMidnight, ZoneId zoneId) {
432-
return Instant.ofEpochSecond(midnight).plusSeconds(secondsSinceMidnight).atZone(zoneId);
435+
private Instant toInstant(int secondsSinceMidnight) {
436+
return Instant.ofEpochSecond(midnight).plusSeconds(secondsSinceMidnight);
433437
}
434438
}

application/src/main/java/org/opentripplanner/standalone/config/sandbox/TriasApiConfig.java

+9
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ public TriasApiConfig(String parameterName, NodeAdapter root) {
4141
.of("timeZone")
4242
.since(V2_8)
4343
.summary("If you don't want to use the feed's timezone, configure it here.")
44+
.description(
45+
"""
46+
By default the input feed's timezone is used. However, there may be cases when you want the
47+
API to use a different timezone.
48+
49+
**Think hard about changing the timezone! We recommend that you keep the feed's time zone and
50+
convert the time in the client which will make debugging OTP much easier.**
51+
"""
52+
)
4453
.asZoneId(null);
4554

4655
if (hideFeedId && hardcodedInputFeedId == null) {

application/src/test/java/org/opentripplanner/model/TripTimeOnDateTest.java

+22-3
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,29 @@
55
import static org.junit.jupiter.api.Assertions.assertFalse;
66
import static org.junit.jupiter.api.Assertions.assertTrue;
77

8+
import java.time.Instant;
9+
import java.time.LocalDate;
810
import java.time.LocalTime;
911
import java.util.List;
1012
import org.junit.jupiter.api.Test;
11-
import org.opentripplanner.model.plan.PlanTestConstants;
13+
import org.opentripplanner._support.time.ZoneIds;
1214
import org.opentripplanner.transit.model._data.TimetableRepositoryForTest;
1315
import org.opentripplanner.transit.model.basic.TransitMode;
1416
import org.opentripplanner.transit.model.framework.Deduplicator;
1517
import org.opentripplanner.transit.model.site.RegularStop;
1618
import org.opentripplanner.transit.model.timetable.TripTimesFactory;
19+
import org.opentripplanner.utils.time.ServiceDateUtils;
1720

18-
class TripTimeOnDateTest implements PlanTestConstants {
21+
class TripTimeOnDateTest {
1922

2023
private static final TimetableRepositoryForTest TEST_MODEL = TimetableRepositoryForTest.of();
2124

25+
private static final LocalDate DATE = LocalDate.of(2025, 3, 18);
26+
private static final Instant MIDNIGHT = ServiceDateUtils.asStartOfService(
27+
DATE,
28+
ZoneIds.BERLIN
29+
).toInstant();
30+
2231
@Test
2332
void gtfsSequence() {
2433
var pattern = TEST_MODEL.pattern(TransitMode.BUS).build();
@@ -78,6 +87,16 @@ void nextTimes() {
7887
assertThat(secondLast.nextTimes().getFirst().nextTimes()).isEmpty();
7988
}
8089

90+
@Test
91+
void atZone() {
92+
var subject = tripTimeOnDate();
93+
var departure = subject.scheduledDeparture();
94+
assertEquals(
95+
"2025-03-18T11:10+01:00",
96+
departure.atZone(ZoneIds.BERLIN).toOffsetDateTime().toString()
97+
);
98+
}
99+
81100
private static TripTimeOnDate tripTimeOnDate() {
82101
var trip = TimetableRepositoryForTest.trip("123").build();
83102
var stopTimes = TEST_MODEL.stopTimesEvery5Minutes(5, trip, "11:00");
@@ -86,6 +105,6 @@ private static TripTimeOnDate tripTimeOnDate() {
86105
.withStopPattern(TimetableRepositoryForTest.stopPattern(stops))
87106
.build();
88107
var tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator());
89-
return new TripTimeOnDate(tripTimes, 2, pattern);
108+
return new TripTimeOnDate(tripTimes, 2, pattern, DATE, MIDNIGHT);
90109
}
91110
}

application/src/test/java/org/opentripplanner/transit/service/TripTimesOnDateTest.java

+3-6
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,15 @@ void onFirstStop() {
4545

4646
assertThat(result).hasSize(1);
4747
var tt = result.getFirst();
48-
assertEquals(instant("12:01"), tt.scheduledDepartureAt(TIME_ZONE).toInstant());
48+
assertEquals(instant("12:01"), tt.scheduledDeparture());
4949
}
5050
{
5151
var result = transitService.findTripTimesOnDate(
5252
TripTimeOnDateRequest.of(List.of(STOP_B1)).withTime(instant).build()
5353
);
5454
assertThat(result).hasSize(1);
5555
var tt = result.getFirst();
56-
assertEquals(instant("12:11"), tt.scheduledDepartureAt(TIME_ZONE).toInstant());
56+
assertEquals(instant("12:11"), tt.scheduledDeparture());
5757
}
5858
}
5959

@@ -69,10 +69,7 @@ void nextDay() {
6969

7070
assertThat(result).hasSize(1);
7171
var tt = result.getFirst();
72-
assertEquals(
73-
instant("12:01").plus(Duration.ofDays(1)),
74-
tt.scheduledDepartureAt(TIME_ZONE).toInstant()
75-
);
72+
assertEquals(instant("12:01").plus(Duration.ofDays(1)), tt.scheduledDeparture());
7673
}
7774

7875
@Test

doc/user/sandbox/TriasApi.md

+15-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ following to `router-config.json`.
4545
|--------------------------------------------------------|:-----------:|------------------------------------------------------------------|:----------:|---------------|:-----:|
4646
| [hardcodedInputFeedId](#triasApi_hardcodedInputFeedId) | `string` | The hardcoded feedId to add to all input ids. | *Optional* | | 2.8 |
4747
| [hideFeedId](#triasApi_hideFeedId) | `boolean` | Hide the feed id in all API output, and add it to input ids. | *Optional* | `false` | 2.8 |
48-
| timeZone | `time-zone` | If you don't want to use the feed's timezone, configure it here. | *Optional* | | 2.8 |
48+
| [timeZone](#triasApi_timeZone) | `time-zone` | If you don't want to use the feed's timezone, configure it here. | *Optional* | | 2.8 |
4949

5050

5151
### Details
@@ -68,6 +68,20 @@ Hide the feed id in all API output, and add it to input ids.
6868

6969
Only turn this feature on if you have unique ids across all feeds, without the feedId prefix.
7070

71+
<h4 id="triasApi_timeZone">timeZone</h4>
72+
73+
**Since version:** `2.8`**Type:** `time-zone`**Cardinality:** `Optional`
74+
**Path:** /triasApi
75+
76+
If you don't want to use the feed's timezone, configure it here.
77+
78+
By default the input feed's timezone is used. However, there may be cases when you want the
79+
API to use a different timezone.
80+
81+
**Think hard about changing the timezone! We recommend that you keep the feed's time zone and
82+
convert the time in the client which will make debugging OTP much easier.**
83+
84+
7185

7286

7387

utils/src/test/java/org/opentripplanner/utils/lang/StringUtilsTest.java

-5
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,12 @@
77
import static org.junit.jupiter.api.Assertions.assertThrows;
88
import static org.junit.jupiter.api.Assertions.assertTrue;
99

10-
import java.time.LocalDate;
11-
import java.time.LocalTime;
12-
import java.time.OffsetDateTime;
13-
import java.time.ZoneId;
1410
import java.util.stream.Stream;
1511
import org.junit.jupiter.api.Test;
1612
import org.junit.jupiter.params.ParameterizedTest;
1713
import org.junit.jupiter.params.provider.Arguments;
1814
import org.junit.jupiter.params.provider.MethodSource;
1915
import org.junit.jupiter.params.provider.ValueSource;
20-
import org.opentripplanner.utils.time.ZoneIds;
2116

2217
class StringUtilsTest {
2318

0 commit comments

Comments
 (0)