diff --git a/enabler/src/de/schildbach/pte/NegentweeProvider.java b/enabler/src/de/schildbach/pte/NegentweeProvider.java index fd3e1e42..faaece5e 100644 --- a/enabler/src/de/schildbach/pte/NegentweeProvider.java +++ b/enabler/src/de/schildbach/pte/NegentweeProvider.java @@ -19,10 +19,10 @@ package de.schildbach.pte; import java.io.IOException; import java.io.Serializable; -import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; import java.util.Currency; import java.util.Date; import java.util.EnumSet; @@ -31,6 +31,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TimeZone; import javax.annotation.Nullable; @@ -75,11 +76,9 @@ public class NegentweeProvider extends AbstractNetworkProvider { private static final String SERVER_PRODUCT = "negentwee"; private static final Language DEFAULT_API_LANG = Language.NL_NL; + private static final TimeZone API_TIMEZONE = TimeZone.getTimeZone("Europe/Amsterdam"); private static final int DEFAULT_MAX_LOCATIONS = 50; - private static final SimpleDateFormat dateTimeParser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm"); - private static final SimpleDateFormat timeParser = new SimpleDateFormat("HH:mm"); - private static final EnumSet trainProducts = EnumSet.of(Product.HIGH_SPEED_TRAIN, Product.REGIONAL_TRAIN, Product.SUBURBAN_TRAIN); @@ -350,18 +349,36 @@ public class NegentweeProvider extends AbstractNetworkProvider { return typeValue.toString(); } + private String formatApiDateTime(Date date) { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HHmm"); + formatter.setTimeZone(API_TIMEZONE); + return formatter.format(date.getTime()); + } + private Date dateFromJSONObject(JSONObject obj, String key) throws JSONException { try { - return dateTimeParser.parse(obj.getString(key)); - } catch (ParseException e) { + Calendar cal = Calendar.getInstance(API_TIMEZONE); + ParserUtils.parseIsoDateTime(cal, obj.getString(key)); + return cal.getTime(); + } catch (RuntimeException e) { return null; } } private Date timeFromJSONObject(JSONObject obj, String key) throws JSONException { try { - return timeParser.parse(obj.getString(key)); - } catch (ParseException e) { + Calendar calParsed = Calendar.getInstance(API_TIMEZONE); + ParserUtils.parseIsoTime(calParsed, obj.getString(key)); + + // Assume this time is always between NOW-00:05 and NOW+23:55, allowing for a 5 minute delay. + Calendar calNow = Calendar.getInstance(); + calNow.add(Calendar.MINUTE, -5); + if (calParsed.before(calNow)) { + calNow.add(Calendar.HOUR, 24); + } + + return calParsed.getTime(); + } catch (RuntimeException e) { return null; } } @@ -858,8 +875,7 @@ public class NegentweeProvider extends AbstractNetworkProvider { Arrays.asList(new QueryParameter("from", locationToQueryParameterString(from)), new QueryParameter("to", locationToQueryParameterString(to)), new QueryParameter("searchType", dep ? "departure" : "arrival"), - new QueryParameter("dateTime", - new SimpleDateFormat("yyyy-MM-dd'T'HHmm").format(date.getTime())), + new QueryParameter("dateTime", formatApiDateTime(date)), new QueryParameter("sequence", "1"), new QueryParameter("realtime", "true"), new QueryParameter("before", "1"), new QueryParameter("after", "5"))); diff --git a/enabler/src/de/schildbach/pte/util/ParserUtils.java b/enabler/src/de/schildbach/pte/util/ParserUtils.java index a663cf6a..0ed6f05f 100644 --- a/enabler/src/de/schildbach/pte/util/ParserUtils.java +++ b/enabler/src/de/schildbach/pte/util/ParserUtils.java @@ -149,19 +149,30 @@ public final class ParserUtils { throw new RuntimeException("cannot parse: '" + str + "'"); } - private static final Pattern P_ISO_TIME = Pattern.compile("(\\d{2})-?(\\d{2})"); + private static final Pattern P_ISO_TIME = Pattern.compile("(\\d{2})[-:]?(\\d{2})([-:]?(\\d{2}))?"); public static final void parseIsoTime(final Calendar calendar, final CharSequence str) { final Matcher mIso = P_ISO_TIME.matcher(str); if (mIso.matches()) { calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(mIso.group(1))); calendar.set(Calendar.MINUTE, Integer.parseInt(mIso.group(2))); + calendar.set(Calendar.SECOND, mIso.group(4) != null ? Integer.parseInt(mIso.group(4)) : 0); + calendar.set(Calendar.MILLISECOND, 0); return; } throw new RuntimeException("cannot parse: '" + str + "'"); } + public static final void parseIsoDateTime(final Calendar calendar, final CharSequence str) { + final String[] timeParts = str.toString().split("T"); + if (timeParts.length != 2) + throw new RuntimeException("cannot parse :'" + str + "'"); + + parseIsoDate(calendar, timeParts[0]); + parseIsoTime(calendar, timeParts[1]); + } + private static final Pattern P_GERMAN_DATE = Pattern.compile("(\\d{2})[\\./-](\\d{2})[\\./-](\\d{2,4})"); public static final void parseGermanDate(final Calendar calendar, final CharSequence str) {