Back to project page GTFSOffline.
The source code is released under:
GNU General Public License
If you think the Android project GTFSOffline listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/* * Copyright 2011 Giles Malet.// w ww. j a v a2 s . c o m * Modified 2013 Wilson Brenna. * * This file is part of GTFSOffline. * * GTFSOffline is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GTFSOffline is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GTFSOffline. If not, see <http://www.gnu.org/licenses/>. */ package com.wbrenna.gtfsoffline; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.text.format.Time; import android.util.Log; import android.util.TimeFormatException; public class ServiceCalendar { private static final String TAG = "ServiceCalendar"; private static final String mDBQuery = "select * from calendar where service_id = ?"; private static final String mDBQueryDate = "select * from calendar_dates where date = ? and service_id = ?"; // Cache some results, to save db lookups private final HashMap<String, String> truemap; private final HashMap<String, String> falsemap; private final HashMap<String, String> trip2servicemap; //Since we are less than or equal to 8 hours search length: //private final int HOURTOGGLE = 8; // Match day number to a string and an abbreviation private static final String[] mWeekDays = { "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" }; private static final String[] mWeekDaysAbbrev = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; private SQLiteDatabase mDB = null; private String mDBName; private DatabaseHelper mDatabaseHelper; //private boolean ampm; //private Context mContext; public ServiceCalendar(String aDBName, SQLiteDatabase aDB, boolean ampmflag) { // Log.v(TAG, "ServiceCalendar()"); mDB = aDB; mDBName = aDBName; truemap = new HashMap<String, String>(32); falsemap = new HashMap<String, String>(32); trip2servicemap = new HashMap<String, String>(64); //mDatabaseHelper = aDatabaseHelper; //ampm = ampmflag; } public void setContext(Context aContext) { //mContext = aContext; } public void setDB(DatabaseHelper aDatabaseHelper) { mDatabaseHelper = aDatabaseHelper; } // Return string showing days this bus runs. // Cursor points to a row in the calendar table for this service_id. private static String getDays(Cursor csr) { String days = ""; for (int i = 0; i < 7; i++) { if (csr.getInt(csr.getColumnIndex(mWeekDays[i])) == 1) { days += mWeekDaysAbbrev[i] + " "; } } return days; } // Do the actual work of the getDays() call, but it makes // sure we close the cursor on exit. private String process_db(String service_id, String date, boolean limit, Cursor csr) { if (!csr.moveToFirst()) { return null; } // Make sure it's in a current schedule period final String start = csr.getString(csr.getColumnIndex("start_date")); final String end = csr.getString(csr.getColumnIndex("end_date")); if (date.compareTo(start) < 0 || date.compareTo(end) > 0) { //this just excludes data that isn't within our date range. //note that it doesn't mean the entire dataset is expired //the entire dataset expiry will return no results... //basically, need to watch RSS to get the right datasets. return null; } // If we're not limiting the display, return what we have if (!limit) { return getDays(csr); } // Check for exceptions final String[] selectargs = { date, service_id }; final Cursor exp = mDB.rawQuery(mDBQueryDate, selectargs); if (exp.moveToFirst()) { final int exception = exp.getInt(exp.getColumnIndex("exception_type")); exp.close(); if (exception == 2) { return null; } if (exception == 1) { return getDays(csr); // service added for this day } Log.e(TAG, "bogus exception type " + exception + " for service " + service_id + "!"); return null; } else { exp.close(); } // Check if the bus runs on the given day of the week. final Time t = new Time(); try { t.parse(date); t.normalize(false); } catch (final TimeFormatException e) { Log.e(TAG, "got bogus date \"" + date + "\""); return null; } final int weekday = t.weekDay; // 0--6 if (csr.getInt(csr.getColumnIndex(mWeekDays[weekday])) == 1) { return getDays(csr); } return null; // doesn't run on given date. } // Return a string showing the days a bus runs, or null if it doesn't // run on the given date. Limit to correct days of week, or not. public String getTripDaysofWeek(String trip_id, String date, boolean limittotoday) { String retstr; // Get and translate the service id String service_id; if (trip2servicemap.containsKey(trip_id)) { service_id = trip2servicemap.get(trip_id); } else { final String svsq = "select service_id from trips where trip_id = ?"; final String[] svsargs = { trip_id }; final Cursor svs = mDB.rawQuery(svsq, svsargs); if (svs.getCount() < 1) { Log.e(TAG, "Database error, probably corrupt."); return null; } svs.moveToFirst(); service_id = svs.getString(0); svs.close(); if (service_id != null && !service_id.equals("")) { trip2servicemap.put(trip_id, service_id); } } if (service_id == null) { return null; } // First check the cache if (limittotoday) { if (truemap.containsKey(service_id + date)) { retstr = truemap.get(service_id + date); // Log.v(TAG, "Retrieved " + service_id+":"+date + " -> " + retstr + " from truecache"); return retstr; } } else { if (falsemap.containsKey(service_id + date)) { retstr = falsemap.get(service_id + date); // Log.v(TAG, "Retrieved " + service_id+":"+date + " -> " + retstr + " from falsecache"); return retstr; } } final String[] selectargs = { service_id }; final Cursor csr = mDB.rawQuery(mDBQuery, selectargs); retstr = process_db(service_id, date, limittotoday, csr); csr.close(); //sometimes calendar_dates contains the trip and not calendar. = 0 //We therefore must also process here if retstr is null: if (retstr == null) { // Check for exceptions final String[] selectargs2 = { date, service_id }; final Cursor exp = mDB.rawQuery(mDBQueryDate, selectargs2); if (exp.moveToFirst()) { final int exception = exp.getInt(exp.getColumnIndex("exception_type")); exp.close(); if (exception == 1) { //retstr = getDays(csr); // service added for this day retstr = "Special Schedule (Holiday)"; } //Log.e(TAG, "bogus exception type " + exception + " for service " + service_id + "!"); //return null; //do nothing } else { exp.close(); } } // Save in cache if (limittotoday) { truemap.put(service_id + date, retstr); } else { falsemap.put(service_id + date, retstr); } return retstr; } public ArrayList<String[]> getNextDepartureTimesGen(Time t, String[] stops, int maxResultsPerStop, int hoursLookAhead, boolean earlyMorning) { final String timenow; final String timelimit; String q; String date; //process stops to be an array for sqlite, minimizing queries: //String stopsString = Arrays.toString(stops); //does not preserve quotation marks String stopsString = "( "; for (int i = 0; i < stops.length-1; i++) { String tmpstops = "\"" + stops[i] + "\""; stopsString += tmpstops + ", "; } stopsString += "\"" + stops[stops.length-1] + "\" )"; //stopsString = stopsString.replace("[","("); //stopsString = stopsString.replace("]",")"); //Log.w(TAG,"Stopstring is " + stopsString); //look for routes from last night if ( (t.hour <= hoursLookAhead) && (!earlyMorning) ) { timenow = String.format("%02d%02d%02d", t.hour+24, t.minute+1, t.second); timelimit = String.format("%02d%02d%02d", t.hour+hoursLookAhead+24,t.minute,t.second); q = "select distinct trip_id,departure_time,stop_id from stop_times where stop_id in " + stopsString + " and (departure_time >= ? and departure_time <= ?)"; Calendar cal = Calendar.getInstance(); cal.set(t.year, t.month, t.monthDay); cal.add(Calendar.DAY_OF_MONTH, -1); date = String.format("%04d%02d%02d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH)); } else if ( !earlyMorning ) { //look for tomorrow's routes timenow = String.format("%02d%02d%02d", 00, 00, 00); timelimit = String.format("%02d%02d%02d", t.hour+hoursLookAhead-24,t.minute,t.second); q = "select distinct trip_id,departure_time,stop_id from stop_times where stop_id in " + stopsString + " and (departure_time >= ? and departure_time <= ?)"; Calendar cal = Calendar.getInstance(); cal.set(t.year, t.month, t.monthDay); cal.add(Calendar.DAY_OF_MONTH, 1); date = String.format("%04d%02d%02d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH)); } else { //here we have earlyMorning toggled - search for today's routes. timenow = String.format("%02d%02d%02d", t.hour, t.minute+1, t.second); timelimit = String.format("%02d%02d%02d", t.hour+hoursLookAhead,t.minute,t.second); q = "select distinct trip_id,departure_time,stop_id from stop_times where stop_id in " + stopsString + "and departure_time >= ? and departure_time <= ?"; date = String.format("%04d%02d%02d", t.year, t.month+1, t.monthDay); } final String[] selectargs = new String[] { timenow, timelimit }; mDB = mDatabaseHelper.ReadableDB(mDBName, mDB); if( mDB == null ) { Log.e(TAG,"Couldn't access database!"); return null; } final Cursor csr = mDB.rawQuery(q, selectargs); final ArrayList<String[]> listdetails = new ArrayList<String[]>(0); final ArrayList<String[]> results = new ArrayList<String[]>(0); boolean more = csr.moveToFirst(); boolean stopsRemaining = true; int nStopsInDB = 0; int[] stopCounter = new int[stops.length]; //this is guaranteed to be zero by l.spec. ArrayList<String> stopsList = new ArrayList<String>(Arrays.asList(stops)); while (more && stopsRemaining) { final String trip_id = csr.getString(0); final String stop_id = csr.getString(2); final int indexOfStop = stopsList.indexOf(stop_id); if(indexOfStop == -1) { more = csr.moveToNext(); continue; } final String daysstr = this.getTripDaysofWeek(trip_id, date, true); // departure_time daystorun trip_id stop_id if (daysstr != null) { listdetails.add(new String[] { csr.getString(1), daysstr, trip_id, stop_id }); //now we keep track of the fav stops we've satisfied. if (stopCounter[indexOfStop] == 0) { nStopsInDB++; } if( (++stopCounter[indexOfStop]) >= maxResultsPerStop ) { stopsList.remove(indexOfStop); stopCounter[indexOfStop] = 0; } if(stopsList.isEmpty()) { stopsRemaining = false; } } //TODO: can probably make this better as well more = csr.moveToNext(); } csr.close(); if ( listdetails.size() > 0) { Collections.sort(listdetails, new Comparator <String[]>() { public int compare(String[] a, String[] b) { return (a[0].compareTo(b[0])); } }); for (int i = 0; i < Math.min(maxResultsPerStop*nStopsInDB,listdetails.size()); i++ ) { final String q2 = "select route_long_name, route_short_name, trip_headsign from routes " + "join trips on routes.route_id = trips.route_id where trip_id = ?"; final String[] selectargs2 = new String[] { listdetails.get(i)[2] }; final Cursor csr2 = mDatabaseHelper.ReadableDB(mDBName, mDB).rawQuery(q2, selectargs2); csr2.moveToFirst(); // departuretime runstoday trip_id route_short_name trip_headsign stop_id // 140300 1 34867 13 Route 13 Laurelwood xxxx if (csr2.getString(2).equals("")) { results.add(new String[] { listdetails.get(i)[0], listdetails.get(i)[1], listdetails.get(i)[2], csr2.getString(0), csr2.getString(2), listdetails.get(i)[3] }); } else { results.add(new String[] { listdetails.get(i)[0], listdetails.get(i)[1], listdetails.get(i)[2], csr2.getString(1), csr2.getString(2), listdetails.get(i)[3] }); } csr2.close(); } //mDatabaseHelper.CloseDB(mDB); return results; } else { //mDatabaseHelper.CloseDB(mDB); // No buses in the next "timelimit" (hour)! return null; } } /* Return the time and route details of the next bus for any route, or null if there isn't one today. */ public ArrayList<String[]> getNextDepartureTimes(Time t, String stopid, int maxResults, int hoursLookAhead, boolean earlyMorning) { final String timenow; final String timelimit; String q; String date; //look for routes from last night if ( (t.hour <= hoursLookAhead) && (!earlyMorning) ) { timenow = String.format("%02d%02d%02d", t.hour+24, t.minute+1, t.second); timelimit = String.format("%02d%02d%02d", t.hour+hoursLookAhead+24,t.minute,t.second); q = "select distinct trip_id,departure_time,stop_id from stop_times where stop_id = ?" + " and (departure_time >= ? and departure_time <= ?)"; Calendar cal = Calendar.getInstance(); cal.set(t.year, t.month, t.monthDay); cal.add(Calendar.DAY_OF_MONTH, -1); date = String.format("%04d%02d%02d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH)); } else if ( !earlyMorning ) { //look for tomorrow's routes timenow = String.format("%02d%02d%02d", 00, 00, 00); timelimit = String.format("%02d%02d%02d", t.hour+hoursLookAhead-24,t.minute,t.second); q = "select distinct trip_id,departure_time,stop_id from stop_times where stop_id = ?" + " and (departure_time >= ? and departure_time <= ?)"; Calendar cal = Calendar.getInstance(); cal.set(t.year, t.month, t.monthDay); cal.add(Calendar.DAY_OF_MONTH, 1); date = String.format("%04d%02d%02d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH)); } else { //add one minute to prevent negative-one minute errors timenow = String.format("%02d%02d%02d", t.hour, t.minute+1, t.second); timelimit = String.format("%02d%02d%02d", t.hour+hoursLookAhead,t.minute,t.second); q = "select distinct trip_id,departure_time from stop_times where stop_id = ? " + "and departure_time >= ? and departure_time <= ?"; date = String.format("%04d%02d%02d", t.year, t.month+1, t.monthDay); } final String[] selectargs = new String[] { stopid, timenow, timelimit }; mDB = mDatabaseHelper.ReadableDB(mDBName, mDB); if( mDB == null ) { Log.e(TAG,"Couldn't access database!"); return null; } final Cursor csr = mDB.rawQuery(q, selectargs); // Load the array for the list final ArrayList<String[]> listdetails = new ArrayList<String[]>(0); final ArrayList<String[]> results = new ArrayList<String[]>(0); //need to find route (shortname) and trip headsign in order to return a full string //do another query! boolean more = csr.moveToFirst(); while (more) { final String trip_id = csr.getString(0); final String daysstr = this.getTripDaysofWeek(trip_id, date, true); // Only add if the bus runs on this day. // the format here: // departure_time daystorun trip_id if (daysstr != null) { listdetails.add(new String[] { csr.getString(1), daysstr, trip_id }); } more = csr.moveToNext(); } csr.close(); //sort the results by departure time if ( listdetails.size() > 0) { Collections.sort(listdetails, new Comparator <String[]>() { public int compare(String[] a, String[] b) { return (a[0].compareTo(b[0])); } }); for (int i = 0; i < Math.min(maxResults,listdetails.size()); i++ ) { final String q2 = "select route_long_name, route_short_name, trip_headsign from routes " + "join trips on routes.route_id = trips.route_id where trip_id = ?"; //final String[] selectargs2 = new String[] { listdetails.get(i)[2], listdetails.get(i)[0] }; final String[] selectargs2 = new String[] { listdetails.get(i)[2] }; final Cursor csr2 = mDatabaseHelper.ReadableDB(mDBName, mDB).rawQuery(q2, selectargs2); csr2.moveToFirst(); //this should have only one element (trip_id is unique!) //the format of this: // departuretime runstoday trip_id route_short_name trip_headsign // 140300 1 34867 13 Route 13 Laurelwood //Some routes use only long_name, some use short_name. Also trip_headsign doesn't always exist. if (csr2.getString(2).equals("")) { results.add(new String[] { listdetails.get(i)[0], listdetails.get(i)[1], listdetails.get(i)[2], csr2.getString(0), csr2.getString(2) }); } else { results.add(new String[] { listdetails.get(i)[0], listdetails.get(i)[1], listdetails.get(i)[2], csr2.getString(1), csr2.getString(2) }); } csr2.close(); } //mDatabaseHelper.CloseDB(mDB); return results; } else { //mDatabaseHelper.CloseDB(mDB); // No buses in the next "timelimit" (hour)! return null; } //final String timetodeparture = Integer.toString(Integer.parseInt(listdetails.get(i)[0]) - t.hour*10000 - t.minute*100 - t.second); } /* Return the time of the next bus for a given route, or null if there isn't one today. */ public String getNextDepartureTime(Time t, String stopid, String routeid, String headsign, int maxResults, int hoursLookAhead, boolean earlyMorning) { final String timenow; final String timelimit; String q; String date; //look for routes from last night if ( (t.hour <= hoursLookAhead) && (!earlyMorning) ) { timenow = String.format("%02d%02d%02d", t.hour+24, t.minute+1, t.second); timelimit = String.format("%02d%02d%02d", t.hour+hoursLookAhead+24,t.minute,t.second); q = "select trip_id,departure_time from stop_times where stop_id = ? and departure_time >= ? and departure_time <= ?" + "join trips on trip_id = ? where trip_headsign = ?"; Calendar cal = Calendar.getInstance(); cal.set(t.year, t.month, t.monthDay); cal.add(Calendar.DAY_OF_MONTH, -1); date = String.format("%04d%02d%02d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH)); } else if ( !earlyMorning ) { //look for tomorrow's routes timenow = String.format("%02d%02d%02d", 00, 00, 00); timelimit = String.format("%02d%02d%02d", t.hour+hoursLookAhead-24,t.minute,t.second); q = "select trip_id,departure_time from stop_times where stop_id = ? and departure_time >= ? and departure_time <= ?" + "join trips on trip_id = ? where trip_headsign = ?"; Calendar cal = Calendar.getInstance(); cal.set(t.year, t.month, t.monthDay); cal.add(Calendar.DAY_OF_MONTH, 1); date = String.format("%04d%02d%02d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH)); } else { timenow = String.format("%02d%02d%02d", t.hour, t.minute+1, t.second); timelimit = String.format("%02d%02d%02d", t.hour+hoursLookAhead,t.minute,t.second); q = "select trip_id,departure_time from stop_times where stop_id = ? and departure_time >= ? and departure_time <= ?" + "join trips on trip_id = ? where trip_headsign = ?"; //Months are [0-11]! Weird, right? date = String.format("%04d%02d%02d", t.year, t.month+1, t.monthDay); } final String[] selectargs = new String[] { stopid, timenow, timelimit, routeid, headsign }; final Cursor csr = mDatabaseHelper.ReadableDB(mDBName, mDB).rawQuery(q, selectargs); // Load the array for the list final int maxcount = csr.getCount(); final ArrayList<String[]> listdetails = new ArrayList<String[]>(maxcount); //final ArrayList<String[]> results = new ArrayList<String[]>(maxResults); boolean more = csr.moveToFirst(); while (more) { final String trip_id = csr.getString(0); final String daysstr = this.getTripDaysofWeek(trip_id, date, true); // Only add if the bus runs on this day. if (daysstr != null) { listdetails.add(new String[] { csr.getString(1), daysstr, csr.getString(0) }); } more = csr.moveToNext(); } csr.close(); //mDatabaseHelper.CloseDB(mDB); // Find when the next bus leaves /*for (int i = 0; i < listdetails.size(); i++) { final String departure_time = listdetails.get(i)[1]; if (departure_time.compareTo(timenow) >= 0) { return departure_time; } }*/ if ( listdetails.size() > 0) { return listdetails.get(0)[1]; } else { // No more buses today. return null; } } /* Return a list of times that all buses for all routes depart a given stop, sorted by time. List is departure_time, * route_id, trip_headsign. */ public ArrayList<String[]> getRouteDepartureTimes(String stopid, String date, boolean dontlimittotoday, SQLiteDatabase aDB) { final String q = "select distinct departure_time as _id, trips.trip_id, routes.route_short_name, trip_headsign from stop_times " + "join trips on stop_times.trip_id = trips.trip_id " + "join routes on routes.route_id = trips.route_id " + "where stop_id = ? order by departure_time"; final String[] selectargs = new String[] { stopid }; final Cursor csr = aDB.rawQuery(q, selectargs); // Load the array for the list final int maxcount = csr.getCount(); final ArrayList<String[]> listdetails = new ArrayList<String[]>(maxcount); boolean more = csr.moveToFirst(); while (more) { final String trip_id = csr.getString(1); final String daysstr = getTripDaysofWeek(trip_id, date, !dontlimittotoday); // Only add if the bus runs on the correct day. if (daysstr != null) { listdetails.add(new String[] { csr.getString(0), daysstr, csr.getString(2), csr.getString(3) }); } more = csr.moveToNext(); } csr.close(); return listdetails; } public ArrayList<String[]> getRouteDepartureTimes(String stopid, String routeid, String headsign, String date, boolean dontlimittotoday, SQLiteDatabase aDB) { final String q = "select distinct departure_time as _id, trip_id from stop_times where stop_id = ? and trip_id in " + "(select trip_id from trips where route_id = ? and trip_headsign = ?) order by departure_time"; final String[] selectargs = new String[] { stopid, routeid, headsign }; final Cursor csr = aDB.rawQuery(q, selectargs); // Load the array for the list final int maxcount = csr.getCount(); final ArrayList<String[]> listdetails = new ArrayList<String[]>(maxcount); boolean more = csr.moveToFirst(); while (more) { final String trip_id = csr.getString(1); final String daysstr = getTripDaysofWeek(trip_id, date, !dontlimittotoday); // Only add if the bus runs on this day. if (daysstr != null) { listdetails.add(new String[] { csr.getString(0), daysstr, csr.getString(1) }); } more = csr.moveToNext(); } csr.close(); return listdetails; } /* Return a properly formatted time. Assumes nn:nn[:nn] input somewhere in the string, may return just that, or convert and * add annoying American `am/pm' suffix. */ /* **** Using hhmmss input - no colons, always seconds! */ public static String formattedTime(String time, boolean inampm) { //final int i = time.indexOf(':'); // Take note of where first colon is //final int j = time.lastIndexOf(':'); // and the last. String hours; String seconds; String newtime; final String minutes = time.substring(2,4); if (time.length() < 5) { seconds = ""; } else if (time.substring(4,6).equals("00")) { seconds = ""; } else { //seconds = ":" + time.substring(4,6); //This is weird, we don't need to show seconds! seconds = ""; } newtime = time.substring(0,2) + ":" + minutes + seconds; int inthours; try { inthours = Integer.parseInt(time.substring(0,2)); } catch (final NumberFormatException e) { Log.d(TAG, "NumberFormatException: " + e.getMessage() + ", for time `" + newtime + "'"); return newtime; } while(inthours >= 24) { inthours -= 24; } try { hours = Integer.toString(inthours) + ":"; } catch (final Error e) { Log.d(TAG, "Error converting integer to string!" + e.getMessage()); return newtime; } if (!inampm) { newtime = hours + minutes + seconds; //return newtime.replaceFirst(":", "h"); return newtime; } final String AM = " am", PM = " pm"; if (inthours > 12) { inthours -= 12; try { newtime = Integer.toString(inthours) + ":" + minutes + seconds + PM; } catch (final Error e) { Log.d(TAG, "Error converting integer to string!" + e.getMessage()); return newtime; } return newtime; } else if (inthours == 12) { try { newtime = Integer.toString(inthours) + ":" + minutes + seconds + PM; } catch (final Error e) { Log.d(TAG, "Error converting integer to string!" + e.getMessage()); return newtime; } return newtime; } else { try { newtime = Integer.toString(inthours) + ":" + minutes + seconds + AM; } catch (final Error e) { Log.d(TAG, "Error converting integer to string!" + e.getMessage()); return newtime; } return newtime; } } public String formattedDepartureTime(Time t, String hours, String minutes) { String departsIn; int hourdiff = Integer.parseInt(hours)-t.hour; while (hourdiff >= 24) { hourdiff -= 24; } if(hourdiff < 0) { hourdiff += 24; } int minutesdiff = Integer.parseInt(minutes)-t.minute; int totaldiff = hourdiff*60 + minutesdiff; if (totaldiff == minutesdiff) { if (minutesdiff == 1) { departsIn = "Departs in " + minutesdiff + " minute"; } else { departsIn = "Departs in " + minutesdiff + " minutes"; } } else if (totaldiff-hourdiff*60 <= 0) { //we have negative minutes if (minutesdiff == 1) { if (totaldiff/60 == 1) { departsIn = "Departs in " + totaldiff/60 + " hour and " + + totaldiff%60 + " minute"; } else if (totaldiff/60 == 0) { departsIn = "Departs in " + totaldiff%60 + " minute"; } else { departsIn = "Departs in " + totaldiff/60 + " hours and " + + totaldiff%60 + " minute"; } } else { if (totaldiff/60 == 1) { departsIn = "Departs in " + totaldiff/60 + " hour " + + totaldiff%60 + " minutes"; } else if (totaldiff/60 == 0) { departsIn = "Departs in " + totaldiff%60 + " minutes"; } else { departsIn = "Departs in " + totaldiff/60 + " hours " + + totaldiff%60 + " minutes"; } } } else { if ( hourdiff == 1) { if (minutesdiff == 1) { departsIn = "Departs in " + hourdiff + " hour and " + + minutesdiff + " minute"; } else { departsIn = "Departs in " + hourdiff + " hour " + + minutesdiff + " minutes"; } } else if ( hourdiff == 0) { if (minutesdiff == 1) { departsIn = "Departs in " + minutesdiff + " minute"; } else { departsIn = "Departs in " + minutesdiff + " minutes"; } } else { if (minutesdiff == 1) { departsIn = "Departs in " + hourdiff + " hours and " + + minutesdiff + " minute"; } else { departsIn = "Departs in " + hourdiff + " hours " + + minutesdiff + " minutes"; } } } return departsIn; } }