1

I have a recyclerview in my Android app with many dates. In order to sort data & insert separators between items grouped by month+year, i use following funs:

private fun monthFromDate(date: Date): Int {
    val calendar = Calendar.getInstance()
    calendar.time = date
    return calendar.get(Calendar.MONTH)
}

private fun yearFromDate(date: Date): Int {
    val calendar = Calendar.getInstance()
    calendar.time = date
    return calendar.get(Calendar.YEAR)
}

I think its bad idea always call Calendar.getInstance(). I don't want to introduce a local variable either. (btw all methods like date.month date.year are deprecated)

The question: I have two util.java.Date instances. Let them be equal if both month & year are equal. How can I implement this fun in fast & optimal way?

Valentin Baryshev
  • 2,195
  • 3
  • 15
  • 24
  • `calling Calendar.getInstance() takes too much unnecessary work` care to describe what this unnecessary work is that you are looking to optimize it? – Martin Marconcini Nov 28 '17 at 23:06
  • In which time zone do you want this? Not asking because it makes a radical difference for the answer; just to point out that you need to decide. – Ole V.V. Nov 29 '17 at 14:29
  • 1
    This code uses troublesome old date-time classes now supplanted by the java.time classes. For older Java and Android, see the *ThreeTen-Backport* and *ThreeTenABP* projects. – Basil Bourque Nov 29 '17 at 16:43

3 Answers3

2

Date.getMonth() although deprecated is fine. Java keeps backward compatability.

If using Java 8, you can do some benchmark of your version Calendar.getInstance().get(Calendar.MONTH) vs the new LocalDate.ofEpochDay(new Date().getTime()).getMonthValue()

ShaharT
  • 442
  • 5
  • 13
  • 1
    I am using android. There is no java 8 – Valentin Baryshev Nov 28 '17 at 23:10
  • 2
    @ValentinBaryshev This package was finally added in API level 26: https://developer.android.com/reference/java/time/package-summary.html. And for previous versions consider https://github.com/JakeWharton/ThreeTenABP. – Alexey Romanov Nov 29 '17 at 09:02
  • 1
    `Date.getMonth()` is unreliable across time zones. It was deprecated for a reason. – Ole V.V. Nov 29 '17 at 10:50
  • Using Java 8 (or ThreeTen Backport in Java 6 or 7) is a good idea. Feeding `Date.getTime()` into `LocalDate.ofEpochDay()` will give you the wrong result: the former gives milliseconds, the latter expects days. – Ole V.V. Nov 29 '17 at 14:27
2

java.time

ShaharT in an answer already mentioned Java 8 as an option. Even on Android Java 7 I believe it’s worth considering using JSR-310, the modern Java date and time API also known as java.time for your task. It came out with Java 8, but you can use it too. It is generally much nicer to work with. It includes a class YearMonth for representing a year and a month, just what you need, and this saves you from using two functions and instantiating a Calendar object for each of those two method calls. So I wanted to show you the correct way to make that conversion.

I am sorry that I cannot write Kotlin code. You will have to make do with Java code, and I trust you to adapt. The Java code is:

private ZoneId zone = ZoneId.of("Europe/Moscow");

private YearMonth yearMonthFromDate(Date date) {
    ZonedDateTime dateTime = date.toInstant().atZone(zone);
    return YearMonth.from(dateTime);
}

Please insert your desired time zone if you didn’t want Europe/Moscow.

Now you are at it, if you go down this avenue, you may also want to consider whether you want Date objects in you app at all, or you prefer classes from JSR-310. The Date and Calendar classes are considered long outmoded.

Performance

On my Mac I ran 100 000 Date objects through the above method in 37 milliseconds. It may not be the same on an average Android device, but I’m not convinced that you need to worry about how fast the conversion is. General rule of thumb is you shouldn’t worry about it until you see a problem in a realistic setting. If not sure, then test in a realistic setting at your earliest convenience.

To use on Android

To use JSR-310 on Android you will need to have the ThreeTenABP (ABP for Android backport). It is all very well and thoroughly explained in this question: How to use ThreeTenABP in Android Project.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
1

It is not very clear, why is Calendar.getInstance() is sub optimal. However, for your requirement, how about coding it like this:

private fun getYearMonth(date: Date): Int {
    val calendar = Calendar.getInstance()
    calendar.time = date
    return calendar.get(Calendar.YEAR) * 100 + calendar.get(Calendar.MONTH) 
}

Gives you access to yearMonth in single interger, like 201711

closure
  • 7,412
  • 1
  • 23
  • 23