skip to Main Content

I’m trying to make a string format that, when a user post is uploaded, it expresses the upload time as "~ minutes ago" or "~ hours ago" and so on.

My application works in the way like below

upload post in PostActivity > firebase saves the data of the post (the post is consist of postId, writerId, message, writeTime, bgUri for image, commentCount) > MainActivity gets data from firebase and shows the post on RecycleView by MyAdapter.kt

Below is the PostActivity.kt

class PostActivity : AppCompatActivity() {

   override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_post)

      supportActionBar?.title = "New Post"
      val layoutManager = LinearLayoutManager(this@PostActivity)
      layoutManager.orientation = LinearLayoutManager.HORIZONTAL

    
      recyclerView2.layoutManager=layoutManager
      recyclerView2.adapter = MyAdapter()

      postButton.setOnClickListener {

         val newRef = FirebaseDatabase.getInstance().getReference("Posts").push()
         
         post.writeTime= DateTime().toString()

         newRef.setValue(post)
         finish()
     }
 }

The writeTime field of post is DateTime().toString().

For the string format, I made a function getdiffTimeText() at MyAdapter.kt which is below.

class MyAdapter(private val posts : MutableList<Post>) :  RecyclerView.Adapter<MyAdapter.MyViewHolder>() { //line 20
   
   override fun onBindViewHolder(holder: MyAdapter.MyViewHolder, position: Int) {
       val post = posts[position]

       holder.timeTextView.text = getDiffTimeText(post.writeTime) //line 32 
   }

   public class MyViewHolder(itemView : View) : 
       val timeTextView = itemView?.findViewById<TextView>(R.id.timeTextView) //line 51           
   }
}
fun getDiffTimeText(targetTime: String): String {

   val curDateTime = DateTime()
   val targetDateTime = DateTime(targetTime)

   val diffDay = Days.daysBetween(curDateTime, targetDateTime).days
   val diffHours = Hours.hoursBetween(targetDateTime, curDateTime).hours
   val diffMinutes = Minutes.minutesBetween(targetDateTime, curDateTime).minutes
   if (diffDay == 0) {
       if (diffDay == 0 && diffMinutes == 0) {
           return "just before"
       }
       return if (diffHours > 0) {
           "" + diffHours + "hours ago"
       } else "" + diffMinutes + "minutes ago"

   } else {
       val format = SimpleDateFormat("yyyy.MM.dd")
       return format.format(Date(targetTime))
   }
}

Below is the MainActivity

class MainActivity : AppCompatActivity() {

   val posts: MutableList<Post> = mutableListOf()

   private lateinit var dbref: DatabaseReference

   override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)

      getUserData()
   }

   private fun getUserData() {
      dbref = FirebaseDatabase.getInstance().getReference("/Posts")
      dbref.addValueEventListener(object : ValueEventListener {
         override fun onDataChange(snapshot: DataSnapshot) {
            if (snapshot.exists()) {
                for (userSnapshot in snapshot.children) {
                    val post = userSnapshot.getValue(Post::class.java)
                    posts.add(post!!)
                }
                recyclerView_main.adapter = MyAdapter(posts)
             }
         }

         override fun onCancelled(error: DatabaseError) {
    Toast.makeText(this@MainActivity,"failed",Toast.LENGTH_SHORT).show()
         }
     })
  }
}

Below is Post.kt the class of user’s post.

class Post {
var postId = ""
var writerId = ""
var message = ""

var writeTime = ""

var bgUri = ""
var commentCount = ""
}

When I run the code, app crashes with the error below.

java.lang.IllegalArgumentException: Invalid format: "1661861458" is malformed at "8"
   
    at org.joda.time.DateTime.<init>(DateTime.java:257)
    at <<package name>>.MyAdapterKt.getDiffTimeText(MyAdapter.kt:51)
    at <<package name>>.MyAdapter.onBindViewHolder(MyAdapter.kt:32)
    at <<package name>>.MyAdapter.onBindViewHolder(MyAdapter.kt:20)
   

To test the fuction getDiffTimeText() I tried the code below in different activity.

val testTime = DateTime().toString()

val testText = findViewById<TextView>(R.id.testing)
val testText2 = findViewById<TextView>(R.id.testing2)

testText.text = testTime
testText2.text = getDiffTimeText(testTime)

The testTime is String type just like the Post.kt where the type of writeTime field is String.

As the result, textview testText shows 2022-08-31T05:37:55.778Z which is the current time, and testText2 shows just ago.
So it seems the function getDiffTimeText works in this way. But It doesn’t work in holder.timeTextView.text = getDiffTimeText(post.writeTime) which is MyAdapter.kt line 32, and the app crashes.
How should I solve this?

*edited my question for clearence, codes that are less relevant are excluded.

4

Answers


  1. Chosen as BEST ANSWER

    there were data on my database which I stored before, and I missed that those data had different structure about writetime. So that caused the crash on loading data. After removing those, it worked well.


  2. Seem the timestamp you passed in line 32 holder.timeTextView.text = getDiffTimeText(post.writeTime) is counting with second instead of millis.

    You can try to changed your remote time to millis or just do like that:

    val timeInMillis = post.writeTime.toLong() * 1000L // maybe toLongOrNull() for safe usage.
    holder.timeTextView.text = getDiffTimeText(timeInMillis.toString())
    
    Login or Signup to reply.
  3. I used this approach to fix my issue: Though in my case I was just calculating the time and not date. But you can have some idea.

    So I was trying to store the time in milliseconds and then retrieving it back to show in a desired format:

    Here are the steps I followed:

    First saving the time in database

    1. Getting the time from Time Picker :
    val h = picker.hour
    val m = picker.minute
    
    1. Converting the total time to milliseconds
    val hour = TimeUnit.HOURS.toMillis(h.toLong())
    val minute = TimeUnit.MINUTES.toMillis(m.toLong())
    val totalTime = hour + minute
    
    1. Saving the time as a string. (in my case it was Room DB, you can do on Firebase)

    Now retrieving the time from database

    1. Initially converted the entered time back to Long from String
      val timeMilliseconds = totalTime.toLong()
    
    1. Converting the milliseconds to hours, minutes and seconds with help of function
     val startTime = formatToDigitalClock(timeMilliseconds)
    
    1. The function
    //Converting received long time to proper digital format
        private fun formatToDigitalClock(milliseconds: Long): String {
            val hours = TimeUnit.MILLISECONDS.toHours(milliseconds).toInt() % 24
            val minutes = TimeUnit.MILLISECONDS.toMinutes(milliseconds).toInt() % 60
            val seconds = TimeUnit.MILLISECONDS.toSeconds(milliseconds).toInt() % 60
    
            return when {
                hours > 0 -> String.format("%d:%02d:%02d", hours, minutes, seconds)
                minutes > 0 -> String.format("%02d:%02d", minutes, seconds)
                seconds > 0 -> String.format("00:%02d", seconds)
    
                else -> {
                    "00:00"
                }
            }
        }
    

    You can further change the String format in a way so that you can show your time like ("2 hours ago")

    Login or Signup to reply.
  4. If post.writeTime.toLong() causes a java.lang.NumberFormatException: For input string: "2022-08-31T04:20:45.265Z", I’ll have to conclude that the type of post.writeTime is String in ISO standard.

    There’s a simple way to parse those ISO standards in java.time, in this case you can do

    OffsetDateTime.parse(post.writeTime)
    

    and use that to calculate the difference / time elapsed until now (in code: OffsetDateTime.now(ZoneOffset.UTC)). You will need a ZoneOffset because the String returned by post.writeTime also has one (the Z at the end is UTC resp. an offset of +00:00 hours).

    The elapsed time can be calculated by means of a java.time.Duration:

    val duration = Duration.between(
                       OffsetDateTime.parse(post.writeTime),
                       OffsetDateTime.now(ZoneOffset.UTC)
                   )
    

    A Duration represents elapsed time in hours, minutes, seconds, millis and even nanos, I think. However, only hours, minutes and seconds should matter here (correct me if I’m wrong).

    Now the Kotlin magic comes into play: We can write an extension function for Duration, one that simply checks the values top-down (hours first, if zero, use minutes, if zero, use seconds, if zero write some statement):

    fun Duration.timeAgo(): String {
        return when {
            this.toHours() > 0 -> "${this.toHours()} hours ago"
            this.toMinutes() > 0 -> "${this.toMinutes()} minutes ago"
            this.toSeconds() > 0 -> "${this.toSeconds()} seconds ago"
            else -> "a moment ago"
        }
    }
    

    Example in a main using the time String from your comment below another answer and the code mentioned above (in this answer):

    fun main() {
        val duration = Duration.between(
            OffsetDateTime.parse("2022-08-31T04:20:45.265Z"),
            OffsetDateTime.now(ZoneOffset.UTC)
        )
    
        println(duration.timeAgo())
    }
    

    Result (attention, it depends on the runtime of the code):

    3 hours ago
    

    Some examples with Durations of differen values:

    fun main() {
        val durOne = Duration.ofHours(2).plusMinutes(13).plusSeconds(53)
        val durTwo = Duration.ofMinutes(13).plusSeconds(53)
        val durThree = Duration.ofSeconds(53).plusMillis(20)
        val durFour = Duration.ofMillis(20)
    
        println("${durOne.timeAgo()}")
        println("${durTwo.timeAgo()}")
        println("${durThree.timeAgo()}")
        println("${durFour.timeAgo()}")
    }
    

    Output:

    2 hours ago
    13 minutes ago
    53 seconds ago
    a moment ago
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search