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
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.
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:
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
Now retrieving the time from database
You can further change the String format in a way so that you can show your time like ("2 hours ago")
If
post.writeTime.toLong()
causes ajava.lang.NumberFormatException: For input string: "2022-08-31T04:20:45.265Z"
, I’ll have to conclude that the type ofpost.writeTime
isString
in ISO standard.There’s a simple way to parse those ISO standards in
java.time
, in this case you can doand use that to calculate the difference / time elapsed until now (in code:
OffsetDateTime.now(ZoneOffset.UTC)
). You will need aZoneOffset
because theString
returned bypost.writeTime
also has one (theZ
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
: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):Example in a
main
using the timeString
from your comment below another answer and the code mentioned above (in this answer):Result (attention, it depends on the runtime of the code):
Some examples with
Duration
s of differen values:Output: