Reputation: 1073
I am storing time in Milliseconds using a Material Time Picker Dialog. The problem is if I choose a time instance which is like a single valued hour (between 1-9) or single valued minute(1-9) then I am able to get the value in milliseconds. But if I set the bigger values of time I get an exception java.lang.ArrayIndexOutOfBoundsException: length=17; index=20
What am I doing wrong here? Or what is the better approach to do this.
Log for reference :
My Code (Not the complete code but what is required for this question) :
//Time Picker
var picker: MaterialTimePicker = MaterialTimePicker()
//Long values to be stored in database
val timeStart: Long = 0
//Setting Start Time
binding.timeStart.setOnClickListener {
openTimePicker()
picker.addOnPositiveButtonClickListener {
val h = picker.hour
val m = picker.minute
binding.timeStart.text = "$h:$m"
Timber.d("Start Time - h: $h m: $m")
try {
val calendar = Calendar.getInstance()
calendar.get(h)
calendar.get(m)
val timeInMilliSeconds = calendar.timeInMillis
Timber.d("$timeInMilliSeconds")
} catch (e: Exception) {
Timber.d("$e")
}
}
}
private fun openTimePicker() {
val isSystem24Hour = is24HourFormat(requireContext())
val clockFormat = if (isSystem24Hour) TimeFormat.CLOCK_24H else TimeFormat.CLOCK_12H
picker = MaterialTimePicker.Builder()
.setTimeFormat(clockFormat)
.setHour(12)
.setMinute(10)
.setTitleText("Set Start Time")
.build()
picker.show(childFragmentManager, "TAG")
}
Upvotes: 0
Views: 185
Reputation: 19602
It's happening here:
val h = picker.hour
val m = picker.minute
binding.timeStart.text = "$h:$m"
Timber.d("Start Time - h: $h m: $m")
try {
val calendar = Calendar.getInstance()
calendar.get(h)
calendar.get(m)
val timeInMilliSeconds = calendar.timeInMillis
Timber.d("$timeInMilliSeconds")
}
Specifically when you call calendar.get()
public int get (int field)
Returns the value of the given calendar field Throws
ArrayIndexOutOfBoundsException
if the specified field is out of range(field < 0 || field >= FIELD_COUNT)
public static final int FIELD_COUNT
The number of distinct fields recognized by
get
andset
. Field numbers range from0..FIELD_COUNT-1
Constant Value: 17
That's why your exception is complaining that the array has 17 items and you're trying to access index 20, because you're passing values representing hours and minutes instead of field constants.
I'm not sure what you want to do exactly, but look at the set
or add
methods in the Calendar
class, if you want to specify an exact time or you want to add x hours and y minutes to the current time. You'll need to use the field constants to specify which value you're changing, e.g. HOUR_OF_DAY
edit Ok, so I think there's a bit of confusion about what Calendar
does, and you've explained what you're trying to do, so I'll show you a few options you have.
I'm not sure which of these you want to do though - when the user enters a time on the clock, do you:
Here's how you can handle both of those things:
Total hours + mins, in millis
// basic calculation - 1000s in 1ms, 60s in 1m, 60m in 1h
val minsAsMillis = m * 60 * 1000
val hoursAsMillis = h * 60 * 60 * 1000
val totalMillis = minsAsMillis + hoursAsMillis
// or use a utility class like Duration, TimeUnit etc
import java.util.concurrent.TimeUnit
val millis = TimeUnit.HOURS.toMillis(hours)
+ TimeUnit.MINUTES.toMillis(mins)
// the java.time classes require desugaring on APIs earlier than 26 I think - see below
import java.time.Duration
val millis = Duration.ofMinutes(m.toLong())
.plusHours(hours.toLong())
.toMillis()
Future time, calculate millis from now til then
This one's trickier, because it involves getting now as a clock time, which relates to things like the user's current locale, the time of year (for things like daylight savings changes) and so on. This is what Calendar
is for, working with times and dates.
There are a lot of utility classes around time, and I'm not super-familiar with it all, so this might not be the best way to do this - but here's something that should work:
// implies the system default locale and time-zone - you can provide those if necessary
val now = Calendar.getInstance()
val future = Calendar.getInstance()
// set the future time
future.set(Calendar.HOUR_OF_DAY, h)
future.set(Calendar.MINUTE, m)
// need to clear the millis field (since it's based off the current time)
future.set(Calendar.MILLIS, 0)
// since the user's setting a clock time, that could be -earlier- than the
// current time (e.g. it's 5pm and they've chosen 10:50am). Assuming that
// should be treated as -tomorrow-, we might need to add a day
// using !after so it also matches -right now-, just in case
if (!future.after(now)) future.add(Calendar.HOUR_OF_DAY, 24)
Now you have your two Calendars
(which represent a specific date and time remember) you can work out the difference between them. You were calling getTimeInMillis()
before - because a Calendar
really just represents a point in time, that method gives you the number of milliseconds since the epoch, i.e. the start of the year 1970. Probably a bigger number than you were expecting!
But that's a useful number to have (e.g. the current time is System.currentTimeInMillis()
) because when you have two of them, you can compare the distance between them. So we can do this:
val durationInMillis = future.getTimeInMillis() - now.getTimeInMillis()
There are some other classes like LocalDateTime
, Instant
, Duration
etc that might be useful (and even preferable, this seems like a fairly simple use-case though) but you might have to enable desugaring to get them on earlier APIs since they're Java 8+ features. Info here if you want to look into that
Upvotes: 1
Reputation: 1073
A better approach to get the time in milliseconds using TimeUnit
instead of Calendar. I used TimeUnit.HOURS.toMillis(myHours)
and TimeUnit.MINUTES.toMillis(myMinutes)
to get my hours and minutes in milliseconds and then add them to get the time in milliseconds.
Code :
binding.timeStart.setOnClickListener {
openTimePicker()
picker.addOnPositiveButtonClickListener {
val h = picker.hour
val m = picker.minute
binding.timeStart.text = "$h:$m"
Timber.d("Start Time - $h: $m")
try {
val hour = TimeUnit.HOURS.toMillis(h.toLong())
val minute = TimeUnit.MINUTES.toMillis(m.toLong())
val totalTime = hour + minute
Timber.d("Hour - $hour, Minute - $minute, Total = $totalTime")
timeStart = totalTime
} catch (e: Exception) {
Timber.d("$e")
}
}
}
private fun openTimePicker() {
picker = MaterialTimePicker.Builder()
.setTimeFormat(TimeFormat.CLOCK_12H)
.setHour(12)
.setMinute(10)
.setTitleText("Set Start Time")
.build()
picker.show(childFragmentManager, "TAG")
}
Upvotes: 0