Hasan A Yousef
Hasan A Yousef

Reputation: 24988

How can I work with Date and Time at Room

I've the below model, which have among others, date and time fields

import androidx.room.Entity
import androidx.room.PrimaryKey
import java.time.LocalDate
import java.time.LocalTime

@Entity
data class Assignment(
    @PrimaryKey(autoGenerate = true) val tid: Int,

   // @PrimaryKey val uid: Int,
    @ColumnInfo(name = "id") val id: Int,
    @ColumnInfo(name = "task") val task: String?,
    @ColumnInfo(name = "address") val address: String?,
    @ColumnInfo(name = "datePicker") val datePicker: LocalDate?,
    @ColumnInfo(name = "timePicker") val timePicker: LocalTime?,
    @ColumnInfo(name = "status") val status: String?
)

But I got the below error:

Cannot figure out how to save this field into database. You can consider adding a type converter for it.

So, I wrote the below convertor:

import java.sql.Date
import java.time.LocalDate
import javax.persistence.AttributeConverter
import javax.persistence.Converter
import java.time.ZoneId.systemDefault

@Converter(autoApply = true)
class LocalDateAttributeConverter : AttributeConverter<LocalDate, Date> {

    override fun convertToDatabaseColumn(locDate: LocalDate?): Date? {
        return if (locDate == null) null else Date.valueOf(locDate.toString())
    }

    override fun convertToEntityAttribute(sqlDate: Date?): LocalDate? {
        val defaultZoneId = systemDefault()
        val instant = sqlDate?.toInstant()
        return instant?.atZone(defaultZoneId)?.toLocalDate()
    }
}

But did not know how to use this convertor with the database model?

UPDATE Based on the received answers, I changed the code to be as below, but still getting the same error:

Converter:

import androidx.room.TypeConverter
import java.sql.Date
import java.time.LocalDate
import java.time.ZoneId.systemDefault

class Converters {
    @TypeConverter
    fun convertToDatabaseColumn(locDate: LocalDate?): Date? {
        return locDate?.let { Date.valueOf(locDate.toString()) }
    }

    @TypeConverter
    fun convertToEntityAttribute(sqlDate: Date?): LocalDate? {
        val defaultZoneId = systemDefault()
        val instant = sqlDate?.toInstant()
        return instant?.atZone(defaultZoneId)?.toLocalDate()
    }
}

DataBase:

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import kotlinx.coroutines.CoroutineScope

@Database(entities = [Assignment::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun assignmentDao(): AssignmentDao

    companion object {
        @Volatile
        var INSTANCE: AppDatabase? = null

        fun getDatabase(context: Context,
                        scope: CoroutineScope
        ): AppDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null) {
                return tempInstance
            }
            synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "database-name"
                ).addCallback(AppDatabaseCallback(scope))
                    .build()
                INSTANCE = instance
                return instance
            }
        }
    }
}

And

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverters
import java.time.LocalDate
import java.time.LocalTime
//import javax.persistence.Convert

@Entity
data class Assignment(
    @PrimaryKey(autoGenerate = true) val tid: Int,

   // @PrimaryKey val uid: Int,
    @ColumnInfo(name = "id") val id: Int,
    @ColumnInfo(name = "task") val task: String?,
    @ColumnInfo(name = "address") val address: String?,
    @ColumnInfo(name = "timePicker") val timePicker: LocalTime?,
    @ColumnInfo(name = "status") val status: String?,

    @TypeConverters(Converters::class)
    @ColumnInfo(name = "datePicker") val datePicker: LocalDate?
)

Upvotes: 4

Views: 20750

Answers (3)

Farhan Ibn Wahid
Farhan Ibn Wahid

Reputation: 1012

long time = new Date().getTime();
long sysTime = System.currentTimeMillis();

Both above will return the same value which is long type. So, you can just use one of them. Which can even be converted to Hours, Minutes or Seconds like this (which can be useful when assigning values):

int hours = (int) TimeUnit.MILLISECONDS.toHours(System.currentTimeMillis());
int minutes = (int) TimeUnit.MILLISECONDS.toMinutes(System.currentTimeMillis());
int seconds = (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());

int date = new Date().getDate();
String formatedDate = new SimpleDateFormat("dd-MM-yyyy").format(new Date());

here, date will return the value as int (3 for today) and formatedDate will return the value as String (03-08-2019 for today).

So, what you can do in you room database class, declare long type variable for time and int/String (which you prefer) for date. Then, and when assigning values to those variables, use the above techniques.

In Kotlin can be done as:

button.setOnClickListener {
       val formatedDate = SimpleDateFormat("yyyy-MM-dd").format(Date())
       val formatedTime = SimpleDateFormat("HH:mm").format(Date())
       val DateTime = "$formatedDate  $formatedTime"
       appViewModel.updateLastUpdateById(this, task_id, DateTime)
}

And in the Model/Room, as:

@Entity
data class Assignment(
    @ColumnInfo(name = "lastUpdate") val lastUpdate: String?
)

Upvotes: 9

Abhinav Suthar
Abhinav Suthar

Reputation: 106

This is how I was using type converter

@Database(
entities = [EventCategory::class, Workshop::class, EventDetails::class, UserProfile::class, Message::class],
version = 2,
exportSchema = false
)
@TypeConverters(EventListConverter::class, WorkshopConverter::class, UserProfileConverter::class)
abstract class MyDatabase : RoomDatabase() {
@TypeConverters(EventListConverter::class)
abstract fun eventCategoryDao(): EventCategoryDao

@TypeConverters(WorkshopConverter::class)
abstract fun workshopListDao(): WorkshopListDao

abstract fun eventDetailsDao(): EventDetailsDao

@TypeConverters(UserProfileConverter::class)
abstract fun userProfileDao(): UserProfileDao

abstract fun confessionDao(): ConfessionDao
}

val db = Room.databaseBuilder(App.instance, MyDatabase::class.java, "database")
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.build()

You can use different type converters for different Dao

Upvotes: 0

CommonsWare
CommonsWare

Reputation: 1007474

But did not know how to use this convertor with the database model?

You are not using the right annotations. javax.persistence does not exist in the Android SDK — I would expect your code to fail to compile.

That error message is referring to Room's @TypeConverter and @TypeConverters annotations.

So, switch your class shown above to use @TypeConverter. Then, add @TypeConverters(LocalDateAttributeConverter::class) to your Assignment entity. Or, if you prefer, add @TypeConverters(LocalDateAttributeConverter::class) to your RoomDatabase subclass, which will allow you to use LocalDate properties in any entity.

Upvotes: 0

Related Questions