Add x axis as datetime label in MPAndroidChart? (Kotlin) - android

I believe this question and answer explains how to format time series data into readable date labels in Java. How do you do the same thing in Kotlin?

You could create a custom formatter class extending the IAxisValueFormatter:
class MyCustomFormatter() : IAxisValueFormatter
{
override fun getFormattedValue(value: Float, axis: AxisBase?): String
{
val dateInMillis = value.toLong()
val date = Calendar.getInstance().apply {
timeInMillis = dateInMillis
}.time
return SimpleDateFormat("dd MMM", Locale.getDefault()).format(date)
}
}
Then assign it to your chart with
chart?.xAxis?.valueFormatter = MyCustomFormatter()

Using version 3.0+ of the MPAndroidChart:
Set formatter to the x axis:
// Formatter to adjust epoch time to readable date
lineChart.xAxis.valueFormatter = LineChartXAxisValueFormatter()
Create a new class LineChartXAxisValueFormatter:
class LineChartXAxisValueFormatter : IndexAxisValueFormatter() {
override fun getFormattedValue(value: Float): String? {
// Convert float value to date string
// Convert from seconds back to milliseconds to format time to show to the user
val emissionsMilliSince1970Time = value.toLong() * 1000
// Show time in local version
val timeMilliseconds = Date(emissionsMilliSince1970Time)
val dateTimeFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault())
return dateTimeFormat.format(timeMilliseconds)
}
}
When the entries are added to the ChartDataArray they are added in seconds, not milliseconds, to avoid potential precision issues with inputting as a float (i.e. milliseconds divided by 1000).
chartDataArray.add(Entry(secondsSince1970.toFloat(), yValue.toFloat()))
Happy coding!

Related

How can I add months Int to a choosen date on date picker in android Kotlin?

I have a working date picker in my app that replaces an EditText after a date selection. I want to add duration through a RadioGroup button that prints an Int to provoke an end date. How can I do that? I've spent the last two days without getting the result I'm looking to get.
Here is what I've got so far.
val datePicker = findViewById<DatePicker>(R.id.date_Picker)
val today = Calendar.getInstance()
datePicker.init(
today.get(Calendar.YEAR),
today.get(Calendar.MONTH),
today.get(Calendar.DAY_OF_MONTH
) { view, year, month, day ->
val month = month + 1
val startDay = ("$day-$month-$year")
binding.fechadeinicio.text = fechainicio
val duration = when (binding.duracion.checkedRadioButtonId) {
R.id.doce -> 12
R.id.veinticuatro -> 24
R.id.treintayseis -> 36
else -> 36
}
// val endDate = startDate.plusMonths(Duration.toLong())
// binding.endDate.text = endDate.toString()
}
Here is the closest one to the result I'm looking to get. Yet, I want to use the selected date val startDay, instead of val date = LocalDate.parse("2020-05-03"). When I replace it, the app crashes.
val date = LocalDate.parse("2020-05-03")
// Displaying date
println("Date : $date")
// Add 2 months to the date
val newDate = date.plusMonths(2)
println("New Date : $newDate")
Please, let me know how I can get the desired result?
Thanks.
I want to use the selected date that is val startDay instead of val date = LocalDate.parse("2020-05-03"). When I replace it, the app crashes.
val startDay = ("$day-$month-$year") here you've created date as dd-MM-yyyy, but by default LocalDate.parse uses DateTimeFormatter.ISO_LOCAL_DATE to parse a string, that parses a string of format yyyy-MM-dd to LocalDate. That's why it's crashing as your date is invalid according to that format.
You have to provide a DateTimeFormatter of pattern dd-MM-yyyy to parse your date.
You can do it like this
val startDay = ("$day-$month-$year")
val dateFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy")
binding.fechadeinicio.text = fechainicio
val duration = when (binding.duracion.checkedRadioButtonId) {
R.id.doce -> 12
R.id.veinticuatro -> 24
R.id.treintayseis -> 36
else -> 36
}
val startDate = LocalDate.parse(startDay, dateFormatter)
val endDate = startDate.plusMonths(duration.toLong()).format(dateFormatter)
binding.endDate.text = endDate
DatePicker returns year, month and day int values, now on creating date like val startDay = ("$day-$month-$year") would result in single digit for days and months less than 10, which would return 1 Jan 2020 as 1-1-2020 but date formatter is expecting it to be 01-01-2020.
To deal with this, we've to format int values before assigning it to the startDay, we can use format method of String to return 2 digits like this "%02d".format(intValue)
Change
val startDay = ("$day-$month-$year")
to
val startDay = "${"%02d".format(day)}-${"%02d".format(month)}-$year"
You can use LocalDate using of function.
Example here:
val date = LocalDate.of(year, month, day)
// Displaying date
println("Date : $date")
// Add 2 months to the date
val newDate = date.plusMonths(2)
println("New Date : $newDate")

How to get the list of dates in a given range with recursion

I want to create a function that returns a list of dates in the given range, with recursion. I will provide the starting date, the ending date, and the type of recursion. I am not sure how to start. Any suggestions would be helpful. Is there any library for this, or do I have to do it on my own?
data class Date(
val day: Int,
val month: Int,
val year: Int
)
enum class Recursion {
NEVER,
EVERY_DAY,
EVERY_WORK_DAY,
EVERY_WEEK,
EVERY_MONTH,
ANNUAL
}
fun createListOfEvents(startDate: Date, endDate: Date, recursion: Recursion): List<Date>{
}
You need to enable desugaring if targeting SDK < 26.
Then you can use the LocalDate class for this. Your Date class is kind of redundant, but you could convert inside the function to LocalDate if you want to keep it.
fun createListOfEvents(startDate: LocalDate, endDate: LocalDate, recursion: Recursion): List<LocalDate> {
val step = when (recursion) {
Recursion.NEVER -> return emptyList()
Recursion.EVERY_DAY, Recursion.EVERY_WORK_DAY -> Period.ofDays(1)
Recursion.EVERY_WEEK -> Period.ofWeeks(1)
Recursion.EVERY_MONTH -> Period.ofMonths(1)
Recursion.ANNUAL -> Period.ofYears(1)
}
var date = startDate
val list = mutableListOf<LocalDate>()
while (date <= endDate) {
list.add(date)
date += step
}
if (recursion == Recursion.EVERY_WORK_DAY) {
val weekend = listOf(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY)
list.removeAll { it.dayOfWeek in weekend }
}
return list
}
Well, basically this is an example of how you can do this:
data class Recursion(val field : TemporalUnit, val step: Long)
val step1Day = Recursion(field = ChronoUnit.DAYS, step = 1)
fun createListOfEvents(startDate: LocalDate, endDate: LocalDate, recursion: Recursion): List<LocalDate>{
var currentDate = startDate
val listOfDates = mutableListOf<LocalDate>()
while (currentDate.isBefore(endDate)) {
listOfDates.add(currentDate)
currentDate = startDate.plus(recursion.step, recursion.field)
}
return listOfDates
}
This method returns list of dates from startDate until endDate with the step of Recursion.
As you can see I've used java.time.* classes for this, but eventually you can convert them into your own Date and Recursion and back.
Here TemporalUnit can be DAYS, WEEKS, MONTHS, YEARS (and other).
It covers most of your needs, working days you will have to manage manually.
Hope this makes sense )

How to display different date/time format in textview using hr, day and calender

I am trying to work on displaying dates and I am experiencing a challenge. So I have this one textview in my adapter class and I want the time displayed to be in 3 different format, Say for instance when its past 24hrs say date e.g Saturday if its very old just display date 02/9/2021 and if a day has not ended just display time 12:00am my problem is how do I achieve this on Android using Kotlin? here is a sample image I got
It's simple just get difference between now and your date and use SimpleDateFormat to formate date
fun getDate(date:Long){
val nowCal = Calendar.getInstance()
val dateCal = Calendar.getInstance().apply {
//just for test, replace with your date timestamp
timeInMillis += TimeUnit.DAYS.toMillis(9)
}
val nowDay = TimeUnit.MILLISECONDS.toDays(nowCal.timeInMillis)
val dateDay = TimeUnit.MILLISECONDS.toDays(dateCal.timeInMillis)
when {
(dateDay - nowDay) <= 1L -> {
val formatter = SimpleDateFormat("hh:mm a")
val dateStr = formatter.format(dateCal.time)
println(dateStr)
}
(dateDay - nowDay) <= 6L -> {
val dateStr = dateCal.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.ENGLISH)
println(dateStr)
}
else ->{
val formatter = SimpleDateFormat("M/d/yy")
val dateStr = formatter.format(dateCal.time)
println(dateStr)
}
}
}

cant subtract 2 timstamp long values

i'm trying to subtract two long(timestamp) but it kept crashing the application
heres the code
//converting timestamp to actual time;
val calendar = Calendar.getInstance(Locale.ENGLISH)
calendar.timeInMillis = chat!!.timestamp!!.toLong()
val t24 = 86400000 //24hrs in milliseconds
val currentTime = System.currentTimeMillis().toInt()
val chatTime: Int = chat.timestamp!!.toInt()
val date = holder.date
if ((currentTime - chatTime ) < t24 ) {
val time =
DateFormat.format("hh:mm aa", calendar).toString()
date!!.text = time
}else{
val time =
DateFormat.format("dd/MM//yyyy hh:mm aa", calendar).toString()
date!!.text = time
}
and this is the error
java.lang.NumberFormatException: For input string: "1594502477561"
at java.lang.Integer.parseInt(Integer.java:618)
at java.lang.Integer.parseInt(Integer.java:650)
at com.google.meetchat.Adapters.ChatAdapter.onBindViewHolder(ChatAdapter.kt:64)
at com.google.meetchat.Adapters.ChatAdapter.onBindViewHolder(ChatAdapter.kt:18)
line 64 is where chatTIme Variable is initialized
You are using not-null assertion operator (!!) on some variables. It converts any value to a non-null type and throws an exception if the value is null.
I suspect you are getting NullPointerException when you use that operator on a variable. Please make sure that these variables are initialized before using operator !! on them:
chat, chat.timestamp, holder.date
UPDATE:
Please use Long for timestamps, don't convert them to Int. For example replace:
val currentTime = System.currentTimeMillis().toInt()
val chatTime: Int = chat.timestamp!!.toInt()
with:
val currentTime = System.currentTimeMillis()
val chatTime = chat.timestamp!!.toLong()
That's because timestamps are represented in milliseconds (or seconds if it is Unix timestamp) and the value can be higher than max value of Int type which is public const val MAX_VALUE: Int = 2147483647.

Kotlin: setting custom labels on x axis in line chart Mpandroidchart

i have an arrayList of String with lots of dates (4500 app), i need put all this dates on the xAxis of my chart create with MPAndroidChart, how can do this i Kotlin?
Here is my code:
val chart= ChartUtils()
val mChart:LineChart = root.findViewById(R.id.lineChartEvolCuota) as LineChart
mChart.isDragEnabled = true
mChart.setScaleEnabled(false)
mChart.axisRight.isEnabled=false
val test : MutableList<object> = db.Object()
val yArray= ArrayList<Entry>()
val xLabel = ArrayList<String>()
var c = 0f
for (i in test){
yArray.add(Entry(c, i.value!!.toFloat()))
xLabel.add(i.date.toString())
c+=1f
}
val xaxis:XAxis = mChart.xAxis
xaxis.granularity=1f
val set1 = LineDataSet(yFondoA, "TEST")
set1 .color = Color.parseColor(color)
set1 .setDrawCircles(false)
set1 .setDrawValues(false)
val data = LineData(set1)
mChart.data = data
mChart.setVisibleXRangeMaximum(365f)
mChart.moveViewToX(c)
SOLUTION
val xaxis:XAxis = mChart.xAxis
xaxis.granularity=1f
xaxis.setValueFormatter(object : ValueFormatter() {
val pattern = "dd MMM yy"
private val mFormat = SimpleDateFormat(pattern)
private val inputFormat = SimpleDateFormat("yyyy-MM-dd")
override fun getFormattedValue(value: Float): String {
val millis = TimeUnit.HOURS.toMillis(value.toLong())
return mFormat.format(inputFormat.parse(xLabel[value.toInt()]))
}
})
I found a nice java example here that I think will help you:
xAxis.setValueFormatter(new ValueFormatter() {
private final SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm", Locale.ENGLISH);
#Override
public String getFormattedValue(float value) {
long millis = TimeUnit.HOURS.toMillis((long) value);
return mFormat.format(new Date(millis));
}
});
When copying the java segment to your code, your jetbrains IDE will ask you to automatically convert to kotlin.
Using a custom formatter is cleaner than manually setting the labels.
Depending on how your dates are extracted from the data you will possibly need to adjust the following line from the example:
long millis = TimeUnit.HOURS.toMillis((long) value);
Also note that you added your labels to a list but never use them afterwards, which is why you are not seeing them.

Categories

Resources