8

I need to be able to Store and Retrieve LocalDateTime properties on my sqlite database using Rooms TypeConverters.

After some researching I implemented the below converter. However, this converter appears to only store the default date time (1970-01-01). So isn't converting correctly Does anyone have a working LocalDateTime Converter? or have any improvements for this one?

public class LocalDateTimeConverter {
@TypeConverter
public static LocalDateTime toDate(Long timestamp) {
    LocalDateTime ldt;
    if (timestamp == null){
        return null;
    }else{
        ldt = Instant.ofEpochMilli(timestamp).atZone(ZoneId.systemDefault()).toLocalDateTime();
    }
    return ldt;
}

@TypeConverter
public static Long toTimestamp(LocalDateTime date) {
    if (date == null){
        return  null;
    }else {
        return date.getLong(ChronoField.CLOCK_HOUR_OF_DAY);
    }
}}
Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
bodovix
  • 299
  • 2
  • 4
  • 11
  • 4
    **`LocalDateTime` is the *wrong* class** for this purpose. Lacking any concept of time zone or offset-from-UTC means this class cannot represent a moment. When tracking moments (such as a count of milliseconds since 1970-01-01T00:00:00Z), use the `Instant` or `OffsetDateTime` or `ZonedDateTime` classes. These three classes *do* represent a moment, while `LocalDateTime` does *not*. `Instant.ofEpochMilli( milliseconds )` – Basil Bourque Feb 28 '19 at 20:09
  • @BasilBourque What would you recommend as the go to DateTime (would be in c#) parameter type ? – bodovix Feb 28 '19 at 20:17
  • 3
    As I showed in the code at the end of my comment, `Instant` is the basic building-block class of *java.time*. An `Instant` is always in UTC, by definition. For other offsets, use `OffsetDateTime`. For full time zones rather than a mere offset, use `ZonedDateTime`. For date-only, `LocalDate`. This has been discussed many many times already on Stack Overflow, so search for more info. You could start with [my Answer](https://stackoverflow.com/a/54912754/642706) yesterday. Then search Stack Overflow using the `site:stackoverflow.com` trick in DuckDuckGo/Google/Bing for the class names. – Basil Bourque Feb 28 '19 at 20:22

1 Answers1

16

I have implemented a LocalDateTimeConverter in my library, Suitcase, here. Keep in mind that this extends my BaseConverter class here. Note: This is all in Kotlin.

If you want to implement yourself, I would recommend storing is as a String as opposed to a timestamp. If you check out the links above, you will see that I first convert the date to a String using toString(), and then back to a LocalDateTime using the parse() function.

Here it is in Java:

public class LocalDateTimeConverter {

    @TypeConverter
    public static LocalDateTime toDate(String dateString) {
        if (dateString == null) {
            return null;
        } else {
            return LocalDateTime.parse(dateString); 
        }
    }

    @TypeConverter
    public static String toDateString(LocalDateTime date) {
        if (date == null) {
            return null;
        } else {
            return date.toString();
        }
    }
}

Edit:

The reason the code you are using above isn't working is because in your toTimestamp() function you are simply asking for the hour of day as a Long. So given a LocalDateTime that is January 1, 2019, 12:00 PM, you are storing 12. So in toDate, you are asking to convert the timestamp 12 to a LocalDateTime, which would be 12 milliseconds after midnight on January 1, 1970.

You can choose to continue using timestamps to store your date by simply changing that line in toTimestamp() to date.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(). This will give you the actual timestamp for the LocalDateTime in the current time zone. Be aware of timezones here: as you are using the system's default timezone to save/load to/from the database, you may get a wrong value should the system changes timezones. If I use your app in New York, and then reopen the app in San Francisco all of the times will be off by 3 hours. It would be better to use a set timezone to save/load, and then convert that to the device's current timezone.

jguerinet
  • 1,429
  • 1
  • 14
  • 21
  • 1
    clean and simple: My original code was over complicating the task. – bodovix Feb 28 '19 at 16:24
  • LocalDateTime.parse(dateString) is only available in api 26. Any backwards compatible alternatives? I have minSdk 24 – Gober Mar 23 '20 at 08:59
  • I typically use [AndroidThreeTen](https://github.com/JakeWharton/ThreeTenABP) which allows you to use these classes in a backwards compatible matter. However it seems like Google is planning to add this backwards compatibility right within Android Studio 4.0, as you can see [here](https://developer.android.com/studio/preview/features#j8-desugar). – jguerinet Mar 24 '20 at 14:23
  • 1
    @Gober, try using library desugaring - a new feature (speaking as of today) by android. https://developer.android.com/studio/write/java8-support#library-desugaring – Faisal Karim May 31 '21 at 01:01