1

I have written some code for my Android app which checks if an event registration is in the next hour. To do this, I got the current time, and added 60 minutes to that to get the end time for the hour slot. This code works fine and all, but I see it doesn't work if the 60 minute time slot laps into the next day (if the start time is 23:11 and the end time is 00:11). Upon inspecting my date objects, I noticed Android Studio says my Date objects have some date information (1 Jan.. ). This is weird because I specified my time format to only have time information. Here is some of the code:

  DateFormat timeFormat = new SimpleDateFormat("hh:mm aa", Locale.ENGLISH);
  DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
  DateFormat scanDateFormat = new SimpleDateFormat("yyyy/MM/dd");
  //Some logic has been cut out
  if (currentEventRegistrations.size() > 0) {
    //We need to check for the earliest events
    Event earliestEvent = null; //Keep a reference to the earlist event
    for (int a = 0; a < currentEvents.size(); a++) {
      Event thisEvent = currentEvents.get(a);
      if (a == 0) {
        earliestEvent = thisEvent;
      } else {
        Date nextEventDate = timeFormat.parse(thisEvent.getTime());
        Date currentEventDate = timeFormat.parse(earliestEvent.getTime());
        if (nextEventDate.compareTo(currentEventDate) <= 0) {
          earliestEvent = thisEvent;
        }
      }
    }
    //Now that we have the earliest event happening today, we need to check if it is within 30 minutes
    earliestEvent.setTime(earliestEvent.getTime().toUpperCase());
    Date earlyEventDate = timeFormat.parse(earliestEvent.getTime());
    Calendar now = Calendar.getInstance();
    Date later = now.getTime();
    String time = timeFormat.format(later);
    now.add(Calendar.MINUTE, 60);
    String timeEnd = timeFormat.format(now.getTime());
    Date laterStart = timeFormat.parse(time);
    Date laterEnd = timeFormat.parse(timeEnd);
    if (earlyEventDate.compareTo(laterStart) >= 0 && earlyEventDate.compareTo(laterEnd) <= 0)
    {  //If the event is in the next 30 minute time frame, save all the event data
      finalEvent = earliestEvent;
      finalEventRegistration = getEventRegistration(earliestEvent.getEventId());
      finalRoute = getRoute(finalEventRegistration.getSelectedRoute());
      if (finalEvent!= null && finalEventRegistration != null && finalRoute != null) {
        return true;
      } else {
        return false;
      }
    }

What would be the best way of converting a time string into a time object? Also, what would the best way be around dealing with time frames that may overlap into the next day?

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
Okuhle
  • 842
  • 3
  • 14
  • 31

3 Answers3

2

A simple approach would be to always store the time in milliseconds. I.e., get the current time using

long now = System.currentTimeMillis());

And for the events (if you are using Calendar), use

long eventTime = calendar.getTimeInMillis();

And then check if now + 60*60*1000l > eventTime.

Moahh
  • 29
  • 1
  • 6
  • 2
    **Warning**: do it like this and you will have troubles scheduling events during the transitions in daylight saving times (when hours may be missing or added). To do it properly this needs to be done using a "calendar" which is aware of locale timezones and the DST transitions and ["roll" the minutes](https://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html#roll(int,%20int)) to get the timeslot borders. – Adrian Colomitchi Oct 05 '16 at 23:19
2

You seem to be working too hard. And you are using the notoriously troublesome old date-time classes, now legacy, supplanted by the java.time classes.

The java.time classes first arrived as a built-in part of Java 8. Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP (see How to use…).

Your code is convoluted. But basically it seems you want to gather a target date-time from the user via text, and see if that lands within the next hour.

Data Collection

The easiest way to collect data is ask the user to input by standard ISO 8601 formats.

String dateInput = "2016-01-23";
String timeInput = "20:05:00";

The java.time classes use the standard formats by default when parsing/generating strings that represent date-time values. To use other formats, search Stack Overflow for Java DateTimeFormatter.

Parsing as “Local…” objects

The Local… types lack any information about offset-from-UTC or time zone.

LocalDate ld = LocalDate.parse( dateInput );
LocalDate lt = LocalTime.parse( timeInput );
LocalDateTime ldt = LocalDateTime.of( ld , lt );

This LocalDateTime object is not an actual moment, not a point on the timeline. Without the context of an offset-from-UTC or a time zone, this value has no real meaning.

Zoned

You should also ask the user the time zone as the reference for that date-time. Or you can assume a zone, or use their JVM’s current default time zone. For more info, search Stack Overflow for Java ZoneId.

ZoneId z = ZoneId.of( "America/Montreal" );

Apply that zone to make sense of the LocalDateTime. Apply a ZoneId to get a ZonedDateTime, an actual moment on the timeline.

ZonedDateTime zdt = ldt.atZone( z );

UTC

Convert that ZonedDateTime object to a simple UTC value.

The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

Instant instant = zdt.toInstant();

Get current moment.

Instant now = Instant.now();

Get an hour later.

Instant later = now.plus( 1 , ChronoUnit.HOURS );

If you need to show that time limit to the user in their desired/expected time zone, you can convert from Instant to ZonedDateTime by applying a ZoneId. Call toString or use DateTimeFormatter to generate strings for presentation.

ZonedDateTime zdtNow = now.atZone( z );
ZonedDateTime zdtLater = later.atZone( z );

Compare

Using the Half-Open approach, ask if your target time (instant) is equal to or later than the beginning (now) and earlier than the ending (later). A shorter way of saying “equal to or later than” is “not before”.

Boolean isTargetWithinTimeFrame = 
    ( ! instant.isBefore( now ) ) 
    && 
    instant.isBefore( later ) 
;

To learn more about the logic of the Half-Open approach, search Stack Overflow for java time Half-Open.

Even easier is to use the Interval class provided by the ThreeTen-Extra project.

Interval interval = Interval.of( now , later );
Boolean isTargetWithinTimeFrame = interval.contains( instant );

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to java.time.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.

Where to obtain the java.time classes?

  • Java SE 8 and SE 9 and later
    • Built-in.
    • Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
  • Java SE 6 and SE 7
    • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
0

Kotlin Code Snippet

fun isTimeLiesBetweenStartAndEndTimeSlot(
    nowCalendar: Calendar,
    startHH: Int,//24H-Format
    startMM: Int,
    endHH: Int,//24H-Format
    endMM: Int
): Boolean {

    val nowHH = nowCalendar.get(Calendar.HOUR_OF_DAY)
    val nowMM = nowCalendar.get(Calendar.MINUTE)

    if (nowHH in startHH..endHH) {
        return when (nowHH) {
            startHH -> //compare start min if current hh == start hh
                nowMM >= startMM
            endHH -> //compare end min if current hh == end hh
                nowMM <= endMM
            else -> true//no need to compare min if currentHH > startHH && currentHH < endHH because it lies b/w time slot
        }
    }
    return false
}

Note: I ignored the seconds in above function but you can add it and apply condition in similar way as for min.