Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ private void loadRealTimeStatusFromWWW(@NonNull RouteDirectionStop rds) {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
CleverDevicesPredictionsDataHandler handler = new CleverDevicesPredictionsDataHandler(this, newLastUpdateInMs, AgencyUtils.getRDSAgencyTimeZoneId(context), sourceLabel, rds);
CleverDevicesPredictionsDataHandler handler = new CleverDevicesPredictionsDataHandler(this, newLastUpdateInMs, AgencyUtils.getAgencyTimeZoneId(context), sourceLabel, rds);
xr.setContentHandler(handler);
xr.parse(new InputSource(httpUrlConnection.getInputStream()));
Collection<POIStatus> statuses = handler.getStatuses();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -577,17 +577,17 @@ private static String getAGENCY_TIME_AM_PM_FORMAT(@NonNull Context context) {
}

@Nullable
private static String agencyTimeZone = null;
private static String agencyTimeZoneId = null;

/**
* Override if multiple {@link GTFSRealTimeProvider} implementations in same app.
*/
@NonNull
public static String getAGENCY_TIME_ZONE(@NonNull Context context) {
if (agencyTimeZone == null) {
agencyTimeZone = context.getResources().getString(R.string.gtfs_real_time_agency_time_zone);
public static String getAGENCY_TIME_ZONE_ID(@NonNull Context context) {
if (agencyTimeZoneId == null) {
agencyTimeZoneId = context.getResources().getString(R.string.gtfs_real_time_agency_time_zone);
}
return agencyTimeZone;
return agencyTimeZoneId;
}

@Override
Expand Down Expand Up @@ -1244,9 +1244,9 @@ private ThreadSafeDateFormatter getTimeParser(@NonNull Context context) {
formatter += StringUtils.SPACE_STRING + getAGENCY_TIME_AM_PM_FORMAT(context);
}
timeParser = new ThreadSafeDateFormatter(formatter, Locale.ENGLISH);
String agencyTimeZoneId = getAGENCY_TIME_ZONE(context);
String agencyTimeZoneId = getAGENCY_TIME_ZONE_ID(context);
if (TextUtils.isEmpty(agencyTimeZoneId)) {
agencyTimeZoneId = AgencyUtils.getRDSAgencyTimeZoneId(context);
agencyTimeZoneId = AgencyUtils.getAgencyTimeZoneId(context);
}
if (!TextUtils.isEmpty(agencyTimeZoneId)) {
timeParser.setTimeZone(TimeZone.getTimeZone(agencyTimeZoneId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1072,7 +1072,7 @@ private void loadPredictionsFromWWW(@NonNull Context context, @NonNull RouteDire
final SAXParserFactory spf = SAXParserFactory.newInstance();
final SAXParser sp = spf.newSAXParser();
final XMLReader xr = sp.getXMLReader();
final NextBusPredictionsDataHandler handler = new NextBusPredictionsDataHandler(this, sourceLabel, newLastUpdateInMs, AgencyUtils.getRDSAgencyTimeZoneId(context));
final NextBusPredictionsDataHandler handler = new NextBusPredictionsDataHandler(this, sourceLabel, newLastUpdateInMs, AgencyUtils.getAgencyTimeZoneId(context));
xr.setContentHandler(handler);
xr.parse(new InputSource(response.body().byteStream()));
final Collection<? extends POIStatus> statuses = handler.getStatuses();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ private void loadPredictionsFromWWW(@NonNull Context context, @NonNull RouteDire

private Collection<POIStatus> parseAgencyJSON(@NonNull Context context, @Nullable String jsonString, @NonNull RouteDirectionStop rds, @Nullable String sourceLabel, long newLastUpdateInMs) {
try {
final String localTimeZoneId = AgencyUtils.getRDSAgencyTimeZoneId(context);
final String localTimeZoneId = AgencyUtils.getAgencyTimeZoneId(context);
ArrayList<POIStatus> result = new ArrayList<>();
JSONObject json = jsonString == null ? null : new JSONObject(jsonString);
if (json != null && json.has(JSON_DATA)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ private ArrayList<News> loadAgencyNewsDataFromWWW(@NonNull Context context, Stri
final boolean ignoreGUID = RssNewProviderUtils.pickIgnoreGUID(context, i);
final boolean ignoreLink = RssNewProviderUtils.pickIgnoreLink(context, i);
final Pair<String, String> dateLinkFallback = RssNewProviderUtils.pickDateLinkFallback(context, i);
final String localTimeZoneId = AgencyUtils.getRDSAgencyTimeZoneId(context);
final String localTimeZoneId = AgencyUtils.getAgencyTimeZoneId(context);
final RSSDataHandler handler = new RSSDataHandler(
url,
authority,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ object AgencyUtils {
private val _defaultTimeZoneId: String by lazy { TimeZone.getDefault().id }

@JvmStatic
fun getRDSAgencyTimeZoneId(context: Context) =
fun getAgencyTimeZoneId(context: Context) =
context.getAgencyString(
R.string.poi_agency_timezone,
R.string.gtfs_rts_timezone, // do not change to avoid breaking compat w/ old modules
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.mtransit.android.commons.provider.gtfs

import kotlinx.datetime.LocalDate
import kotlinx.datetime.atStartOfDayIn
import org.mtransit.android.commons.MTLog
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Instant
import kotlinx.datetime.TimeZone as KtTimeZone

object GTFSDateTimeUtils : MTLog.Loggable {

private val LOG_TAG: String = GTFSDateTimeUtils::class.java.simpleName

override fun getLogTag() = LOG_TAG

fun parseToDateTime(gtfsDateStr: String?, gtfsTimeStr: String?, agencyTimeZone: KtTimeZone): Instant? {
try {
val cleanedDate = gtfsDateStr?.trim() ?: return null
if (cleanedDate.length != 8 || cleanedDate.any { !it.isDigit() }) {
MTLog.w(this, "Invalid GTFS date format '$cleanedDate'! Must be YYYYMMDD")
return null
}
Comment thread
mmathieum marked this conversation as resolved.
val parts = gtfsTimeStr?.trim()?.split(":") ?: return null
if (parts.size != 3) {
MTLog.w(this, "Invalid GTFS time format '$gtfsTimeStr'! Must be HH:MM:SS")
return null
}

val hoursCount = parts.getOrNull(0)?.toIntOrNull()?.takeIf { it >= 0 } ?: return null
val minutesCount = parts.getOrNull(1)?.toIntOrNull()?.takeIf { it in 0..59 } ?: return null
val secondsCount = parts.getOrNull(2)?.toIntOrNull()?.takeIf { it in 0..59 } ?: return null
val durationFromStartOfDay = hoursCount.hours + // compat with 24+ hours
minutesCount.minutes + secondsCount.seconds

val year = cleanedDate.substring(0, 4).toIntOrNull() ?: return null
val month = cleanedDate.substring(4, 6).toIntOrNull() ?: return null
val day = cleanedDate.substring(6, 8).toIntOrNull() ?: return null
val startOfDay = LocalDate(year, month, day).atStartOfDayIn(agencyTimeZone)

return startOfDay + durationFromStartOfDay
} catch (e: Exception) {
MTLog.w(this, e, "Error while parsing GTFS date '$gtfsDateStr' & time '$gtfsTimeStr'!")
return null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.mtransit.android.commons.data.Stop
import org.mtransit.android.commons.data.toRouteDirection
import org.mtransit.android.commons.provider.GTFSRealTimeProvider
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.MT_HASH_SECRET_AND_DATE
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAGENCY_TIME_ZONE
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAGENCY_TIME_ZONE_ID
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAGENCY_URL_HEADER_NAMES
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAGENCY_URL_HEADER_VALUES
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAGENCY_URL_TOKEN
Expand Down Expand Up @@ -39,7 +39,8 @@ import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate as GTU
val Int.isValidDirection get() = this in 0..1
val GTFSRealTimeProvider.ignoreDirection get() = isIGNORE_DIRECTION(requireContextCompat())
val GTFSRealTimeProvider.targetAuthority get() = getTARGET_AUTHORITY(requireContextCompat())
val GTFSRealTimeProvider.timeZone get() = getAGENCY_TIME_ZONE(requireContextCompat())
val GTFSRealTimeProvider.timeZoneId get() = getAGENCY_TIME_ZONE_ID(requireContextCompat())
val GTFSRealTimeProvider.optTimeZoneId get() = timeZoneId.takeIf { it.isNotBlank() }

fun GTFSRealTimeProvider.parseAgencyId(es: GEntitySelector) = es.optAgencyIdNotEmpty?.let { parseAgencyId(it) }
fun GTFSRealTimeProvider.parseAgencyId(gAgencyId: String) = gAgencyId.originalIdToId(agencyIdCleanupPattern)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public static ScheduleTimestamps getScheduleTimestamps(@NonNull GTFSProvider pro
final Context context = provider.requireContextCompat();
final ThreadSafeDateFormatter dateFormat = GTFSStatusProvider.getDateFormat(context);
final ThreadSafeDateFormatter timeFormat = GTFSStatusProvider.getTimeFormat(context);
final TimeZone timeZone = TimeZone.getTimeZone(AgencyUtils.getRDSAgencyTimeZoneId(context));
final TimeZone timeZone = TimeZone.getTimeZone(AgencyUtils.getAgencyTimeZoneId(context));
final Calendar startsAt = TimeUtils.getNewCalendar(timeZone, startsAtInMs);
startsAt.add(Calendar.DATE, -1); // starting yesterday
Set<Schedule.Timestamp> dayTimestamps;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ public static POIStatus getNewStatus(@NonNull GTFSProvider provider, @NonNull St
static ThreadSafeDateFormatter getDateFormat(@NonNull Context context) {
if (dateFormat == null) {
dateFormat = new ThreadSafeDateFormatter(DATE_FORMAT_PATTERN, Locale.ENGLISH);
final String localTimeZoneId = AgencyUtils.getRDSAgencyTimeZoneId(context);
final String localTimeZoneId = AgencyUtils.getAgencyTimeZoneId(context);
dateFormat.setTimeZone(TimeZone.getTimeZone(localTimeZoneId));
}
return dateFormat;
Expand All @@ -189,7 +189,7 @@ static ThreadSafeDateFormatter getDateFormat(@NonNull Context context) {
static ThreadSafeDateFormatter getTimeFormat(@NonNull Context context) {
if (timeFormat == null) {
timeFormat = new ThreadSafeDateFormatter(TIME_FORMAT_PATTERN, Locale.ENGLISH);
final String localTimeZoneId = AgencyUtils.getRDSAgencyTimeZoneId(context);
final String localTimeZoneId = AgencyUtils.getAgencyTimeZoneId(context);
timeFormat.setTimeZone(TimeZone.getTimeZone(localTimeZoneId));
}
return timeFormat;
Expand Down Expand Up @@ -245,7 +245,7 @@ private static ArrayList<Schedule.Timestamp> findTimestamps(@NonNull GTFSProvide
final Context context = provider.requireContextCompat();
final ThreadSafeDateFormatter dateFormat = getDateFormat(context);
final ThreadSafeDateFormatter timeFormat = getTimeFormat(context);
final String localTimeZoneId = AgencyUtils.getRDSAgencyTimeZoneId(context);
final String localTimeZoneId = AgencyUtils.getAgencyTimeZoneId(context);
final TimeZone timeZone = TimeZone.getTimeZone(localTimeZoneId);
final Calendar startsAt = TimeUtils.getNewCalendar(timeZone, timestamp);
if (lookBehindInMs > PROVIDER_PRECISION_IN_MS) {
Expand Down Expand Up @@ -436,7 +436,7 @@ static Set<Schedule.Timestamp> findScheduleList(
BufferedReader br = null;
String line = null;
final Context context = provider.requireContextCompat();
final String localTimeZoneId = AgencyUtils.getRDSAgencyTimeZoneId(context);
final String localTimeZoneId = AgencyUtils.getAgencyTimeZoneId(context);
String fileName = String.format(getSTOP_SCHEDULE_RAW_FILE_FORMAT(context), stopId);
try {
@SuppressLint("DiscouragedApi")
Expand Down Expand Up @@ -604,7 +604,7 @@ private static ArrayList<Schedule.Frequency> findFrequencies(@NonNull GTFSProvid
final Context context = provider.requireContextCompat();
final ThreadSafeDateFormatter dateFormat = getDateFormat(context);
final ThreadSafeDateFormatter timeFormat = getTimeFormat(context);
final TimeZone timeZone = TimeZone.getTimeZone(AgencyUtils.getRDSAgencyTimeZoneId(context));
final TimeZone timeZone = TimeZone.getTimeZone(AgencyUtils.getAgencyTimeZoneId(context));
final Calendar startsAt = TimeUtils.getNewCalendar(timeZone, timestamp);
startsAt.add(Calendar.DATE, -1); // starting yesterday
HashSet<Schedule.Frequency> dayFrequencies;
Expand Down Expand Up @@ -766,7 +766,7 @@ private static Long convertToTimestamp(Context context, int timeInt, String date
private static ThreadSafeDateFormatter getToTimestampFormat(Context context) {
if (toTimestampFormat == null) {
toTimestampFormat = new ThreadSafeDateFormatter(TO_TIMESTAMP_FORMAT_PATTERN, Locale.ENGLISH);
final String localTimeZoneId = AgencyUtils.getRDSAgencyTimeZoneId(context);
final String localTimeZoneId = AgencyUtils.getAgencyTimeZoneId(context);
toTimestampFormat.setTimeZone(TimeZone.getTimeZone(localTimeZoneId));
}
return toTimestampFormat;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@ object GtfsRealtimeExt {

val GTripUpdate.optTrip get() = if (hasTrip()) trip else null
val GTripUpdate.optVehicle get() = if (hasVehicle()) vehicle else null
val GTripUpdate.optStopTimeUpdateList get() = stopTimeUpdateList?.takeIf { it.isNotEmpty() }
fun GTripUpdate.hasStopTimeUpdateList() = stopTimeUpdateList.isNotEmpty()
val GTripUpdate.optStopTimeUpdateList get() = if (hasStopTimeUpdateList()) stopTimeUpdateList else null
val GTripUpdate.optTimestamp get() = if (hasTimestamp()) timestamp else null
val GTripUpdate.optTimestampMs get() = optTimestamp?.secToMs()
val GTripUpdate.optDelay get() = if (hasDelay()) delay else null
Expand Down Expand Up @@ -299,6 +300,9 @@ object GtfsRealtimeExt {
val GTUStopTimeEvent.optScheduledTime get() = if (hasScheduledTime()) scheduledTime else null
val GTUStopTimeEvent.optScheduledTimeMs get() = optScheduledTime?.secToMs()
val GTUStopTimeEvent.optScheduledTimeInstant get() = optScheduledTime?.secsToInstant()
fun GTUStopTimeEvent.hasTimeOrScheduledTime() = hasTime() || hasScheduledTime()
val GTUStopTimeEvent.optTimeOrScheduledTime get() = optTime ?: optScheduledTime
val GTUStopTimeEvent.optTimeOrScheduledTimeMs get() = optTimeOrScheduledTime?.secToMs()

@JvmStatic
@JvmOverloads
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,9 @@ object GTFSRealTimeServiceAlertsProvider : MTLog.Loggable {

// Best practices: 10 minutes
// https://gtfs.org/documentation/realtime/realtime-best-practices/#feed-publishing-general-practices
// Montréal-based competitor: no limit
@JvmStatic
val SERVICE_ALERTS_MAX_AGE_MS = 10.times(3).minutes.inWholeMilliseconds
val SERVICE_ALERTS_MAX_AGE_MS = 10.times(13).minutes.inWholeMilliseconds
Comment thread
mmathieum marked this conversation as resolved.
Comment thread
mmathieum marked this conversation as resolved.

@JvmStatic
fun GTFSRealTimeProvider.getNew(filter: ServiceUpdateProviderContract.Filter): ServiceUpdates? {
Expand Down
Loading