How can I truncate a Kotlin Duration? - kotlin

Java's Duration has a truncate function.
Duration d = myDuration.truncatedTo(ChronoUnit.SECONDS);
public Duration truncatedTo​(TemporalUnit unit)
Returns a copy of this Duration truncated to the specified unit.
Truncating the duration returns a copy of the original with conceptual fields smaller than the specified unit set to zero. For example, truncating with the MINUTES unit will round down to the nearest minute, setting the seconds and nanoseconds to zero.
However Kotlin has a different implementation for Duration, and this does not have an analogous truncation method.
I want to be able to divide or multiply a Duration by some number, and then (with an extension function) remove any time unit smaller than the one I supply.
import kotlin.time.*
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes
fun main() {
val duration = 10.days + 5.hours + 33.minutes
println(duration)
val divided = duration / 100.001
println(divided)
val truncatedToSeconds = divided.truncate(DurationUnit.SECONDS)
println(truncatedToSeconds) // expect: 2h 27m 19s
val truncatedToMinutes = divided.truncate(DurationUnit.MINUTES)
println(truncatedToMinutes) // expect: 2h 27m
val truncatedToHours = divided.truncate(DurationUnit.HOURS)
println(truncatedToHours) // expect: 2h
}
fun Duration.truncate(unit: DurationUnit): Duration {
/// ...
return this
}

One option is to convert it to a Long and back again, using your desired unit:
fun Duration.truncate(unit: DurationUnit): Duration =
toLong(unit).toDuration(unit)

Related

How to get the difference betweeen two dates (jetpack compose/kotlin)?

I have to calculate how many days are left between the date selected by the user through a DatePicker and the current date
I was trying to write something like this:
val simpleDate = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
val date = simpleDate.parse(event!!.date!!)
val diff = Duration.between(LocalDate.now(), date.toInstant())
val leftDays = diff.toDays()
Your mix of outdated (SimpleDateFormat, 'Date') and modern (LocalDate) APIs is not optimal, I think:
I would use plain java.time here, because…
you can obviously use it in your application
it has a specific class for datetime Strings of the pattern you have shown in your question: an OffsetDateTime and
there's a java.time.Duration which you have tried to use
Here's an example:
fun main(args: Array<String>) {
// example input, some future datetime
val input = "2022-12-24T13:22:51.837Z"
// parse that future datetime
val offsetDateTime = OffsetDateTime.parse(input)
// build up a duration between the input and now, use the same class
val duration = Duration.between(OffsetDateTime.now(), offsetDateTime)
// get the difference in full days
val days = duration.toDays()
// print the result as "days left"
println("$days days left")
}
Output:
110 days left
If you don't receive a datetime but a date without time (just day of month, month of year and year), then use a LocalDate and calculate the ChronoUnit.DAYS.between(today, futureDate)
fun main(args: Array<String>) {
// example input, some future date
val input = "2022-12-24"
// parse that
val futureDate = LocalDate.parse(input)
// get the difference in full days
val days = ChronoUnit.DAYS.between(LocalDate.now(), futureDate)
// print the result
println("$days days left")
}
Output (again):
110 days left
Try below code -
val previousTimeDouble: Double = previousTime.toDouble()
val nowTimeDouble: Double = System.currentTimeMillis().toDouble()
val dateNowString: String = dateNow.toString();
val time: Long = (nowTimeDouble - previousTimeDouble).toLong()
val difference: String= differenceBetweenTwoTimes(time)
Log.d("difference", difference)
Function for converting time difference into units -
fun differenceBetweenTwoTimes(time: Long): String {
var x: Long = time / 1000
var seconds = x % 60
x /= 60
var minutes = x % 60
x /= 60
var hours = (x % 24).toInt()
x /= 24
var days = x
return String.format("%02d:%02d:%02d", hours, minutes, seconds)
}

How to convert milliseconds to Stopwatch format in Kotlin? (HH:MM:SS)

I have time in milliseconds which i'm getting by:
val past = System.currentTimeMillis()
val future = System.currentTimeMillis() + 1000L
// getting duration every second. imagine working stopwatch here
val duration = future - past
// Imconvert duration to HH:MM:SS
. I need to convert it to stopwatch format (HH:MM:SS). I know there is a lot of options. But what is the most modern and easiest way to do it?
Be careful how you get milliseconds in the first place
First and foremost, you should not use System.currentTimeMillis() for elapsed time. This clock is meant for wallclock time and is subject to drifting or leap second adjustments that can mess up your measurements significantly.
A better clock to use would be System.nanoTime(). But in Kotlin you don't need to call it explicitly if you want to measure elapsed time. You can use nice utilities like measureNanoTime, or the experimental measureTime which directly returns a Duration that you can format:
val durationNanos = measureNanoTime {
// run the code to measure
}
val duration = measureTime {
// run the code to measure
}
Convert milliseconds to Duration
If you don't want to use measureTime and still have just a number of milliseconds or nanoseconds, you can convert them to a Duration by using one of the extension properties of Duration.Companion:
import kotlin.time.Duration.Companion.milliseconds
val durationMillis: Long = 1000L // got from somewhere
val duration: Duration = durationMillis.milliseconds
However, that is quite awkward and that's the reason why those extensions were deprecated for a while. They were restored because they are nice to use with number literals, but they are not so nice with variable names. Instead, you can use Long.toDuration():
import kotlin.time.*
val durationMillis = 1000L // got from somewhere
val duration = durationMillis.toDuration(DurationUnit.MILLISECONDS)
Format Duration
If you just want a nice visual format, note that the kotlin.time.Duration type is already printed nicely thanks to its nice toString implementation:
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Duration.Companion.milliseconds
fun main() {
val duration = 4.minutes + 67.seconds + 230.milliseconds
println(duration) // prints 5m 7.23s
}
See it in the playground: https://pl.kotl.in/YUT6FZA0l
If you really want the format you're asking for, you may also use toComponents as #Can_of_awe mentioned:
// example duration, built from extensions on number literals
val duration = 4.minutes + 67.seconds + 230.milliseconds
val durationString = duration.toComponents { hours, minutes, seconds, _ ->
"%02d:%02d:%02d".format(hours, minutes, seconds)
}
println(durationString) // prints 00:05:07
A more Kotlin-style straightforward way of doing this:
val durationString = duration.milliseconds.toComponents { hours, minutes, seconds, _ ->
"%02d:%02d:%02d".format(hours, minutes, seconds)
}
Where the .milliseconds extension is from import kotlin.time.Duration.Companion.milliseconds

For loop must have an iterator()

I need this service in which if the person stays for longer than 30 minutes, they have to pay an extra $10 every 15 minutes (and for the fraction of the 15 as well).
I designed it like this so far:
var checkInTime: Calendar
val totalTime: Long
get() = (Calendar.getInstance().timeInMillis - checkInTime.timeInMillis) / MIN_IN_MILISEC
fun getTime(totalTime:Long): Int{
var finalPrice = 0
var initialPrice = 20
if(totalTime<31){
finalFee = initialPrice
} else {
val extraPrice = 10
val extraTime = 15
finalFee = initialPrice
for(extraTime in totalTime){
finalFee += extraTime
}
return finalFee
}
I get the error "For loop must have an iterator()" when I try to loop through the totalTime when it's more than 30 minutes so that I can add $10 every 15 extra minutes. I need some help as to how to add to the finalFee every extra 15 minutes the person stays since my method is not working.
Thank you.
Let's take a look at your getTime function:
You're using a Long as totalTime. You can measure it in minutes to simplify your calculation (since all time values are measured in minutes). Since a Long type in Kotlin stores a integer up to 9,223,372,036,854,775,807 and no soul on Earth will use your service for that long (this represents 17 billion milleniums), you can just use an Int.
You're not declaring the finalFee variable, thus the code will raise an
"Unresolved reference" error. Since you're not using the finalPrice variable, I'm assuming you wanted to use this instead.
You're trying to iterate over a numeric value (in this case, totalTime, which is a Long). You can iterate over each element of a List, but how would you iterate over each element of an integer? I'm assuming you want to do a certain action totalTime number of times. In this case, you would use ranges.
You're also not using the variables extraPrice and extraTime.
There's code that's common to both if-else conditions (finalPrice = initialPrice), so you can extract that to outside the if-statement.
Refactoring your function:
fun getTime(totalTime: Int): Int {
var finalPrice = 20
if (totalTime >= 30) {
(0 until totalTime).forEach {
finalPrice += 15
}
}
return finalPrice
}
It's shorter, but still doesn't do what it's supposed to: let's suppose totalTime is equal to 45. The person got 30 minutes costing $20 and only have to pay $10 for every 15 minutes, therefore will only pay $30 total. Your function is considering that the person will have to pay $15 for every minute they stayed, because it uses a for-loop that goes from 0 to totalTime. For that, you need a for-loop that goes from 30 (the time limit) from the total time (the totalTime) every 15 minutes:
fun getTime(totalTime: Int): Int {
var finalPrice = 20
if (totalTime > 30) {
(30 until totalTime step 15).forEach {
finalPrice += 10
}
}
return finalPrice
}
Better yet, you don't even need a for-loop, you can just use maths:
fun getTime(totalTime: Int): Int {
var finalPrice = 20
if (totalTime > 30) {
finalPrice += ((totalTime - 30) / 15) * 10
// ^^^^^^^^^^^^^^^^ Get the exceeding time
// ^^^^^^^^^^^^^^^^^^^^^^^ How many 15 minutes are there?
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Put $10 for every 15 minutes
}
return finalPrice
}
The last part: your question said you need to consider the fraction of 15 as well. Therefore, you need to use a real value, not an integer. Let's change it to a Double:
fun getTime(totalTime: Int): Double {
var finalPrice = 20.0
if (totalTime > 30) {
finalPrice += ((totalTime - 30) / 15.0) * 10
}
return finalPrice
}
Let's test your function:
fun main() {
println(getTime(0)) // Outputs 20.0
println(getTime(10)) // Outputs 20.0
println(getTime(30)) // Outputs 20.0
println(getTime(45)) // Outputs 30.0
println(getTime(60)) // Outputs 40.0
println(getTime(70)) // Outputs 46.666...
}

Format a number to string, fill 0 when it's not enough two characters

I want to format the number to String and fill 0 when it's not enough two characters
fun formatDuration(val duration):String {
val minutes = duration.toInt() / 60
return "$minutes"
}
For example, if minutes is 6, it should displayed 06 rather than 6.
You can padStart the toString() result of minutes.
I tried your code in the Kotlin Playground and it wasn't compilable / runnable. For the following example, I had to change parts of your fun:
fun main() {
println(formatDuration(364.34))
}
fun formatDuration(duration: Double): String {
val minutes = duration.toInt() / 60
// fill the result to be of 2 characters, use 0 as padding char
return minutes.toString().padStart(2, '0')
}
Executing this results in the output 06.
Alternatively, you can simply use String.format() from Java, just
return "%02d".format(minutes)
instead of return minutes.toString().padStart(2, '0'), the result stays the same.
You can achive this with padStart
Example:
val padWithSpace = "125".padStart(5)
println("'$padWithSpace'") // ' 125'
val padWithChar = "a".padStart(5, '.')
println("'$padWithChar'") // '....a'
// string is returned as is, when its length is greater than the specified
val noPadding = "abcde".padStart(3)
println("'$noPadding'") // 'abcde'

Find 10 nearest points from an array of coordinates for each coordinate in Kotlin

I have an array
var poses = arrayOf<Array<Double>>()
That I populate using a loop.
The output looks something like this:
poses.forEach {
println(Arrays.toString(it))
}
[-71.42510166478651, 106.43593221597114]
[104.46430594348055, 78.62761919208839]
[100.27031925094859, 79.65568893000942]
[311.2433803626159, 233.67219485640456]
[330.3015877764689, -114.9000129699181]
[34.76986782382592, -383.71914014833436]
[355.477931403836, -173.29388985868835]
[322.72821807215564, -45.99138725647516]
...
Is there an efficient way to find 10 nearest points from this list for each coordinate?
For example:
Find 10 nearest points for [-71.42510166478651, 106.43593221597114], then [104.46430594348055, 78.62761919208839] and so on.
I tried looking into numpy-like libraries for Kotlin but seeing as though I'm new to the language I couldn't figure out how to do it.
You can write a distance function with the Pythagorean theorem. (This GeeksforGeeks page might be helpful too.)
You could also use a data class for the points, instead of using an array with two double values. The code below uses the approach that Mateen Ulhaq suggested in his comment, with two modifications:
The addition of "point to" lets us create a map from a point to the ten nearest points (so we know which point the ten points are related to).
The call to ".drop(1)" before ".take(10)" keeps the point itself out of its list (since the distance to itself is 0).
This code uses a list of points, determines the nearest points and prints them for each point:
fun main() {
val poses = listOf(
Point(-71.42510166478651, 106.43593221597114),
Point(104.46430594348055, 78.62761919208839),
Point(100.27031925094859, 79.65568893000942),
Point(311.2433803626159, 233.67219485640456),
Point(330.3015877764689, -114.9000129699181),
Point(34.76986782382592, -383.71914014833436),
Point(355.477931403836, -173.29388985868835),
Point(322.72821807215564, -45.99138725647516)
)
val nearestPoints = poses.map {
point -> point to poses.sortedBy { point.distance(it) }.drop(1).take(10)
}
println("Nearest points:")
nearestPoints.forEach {
println("${it.first} is closest to ${it.second}")
}
}
data class Point(val x: Double, val y: Double) {
fun distance(that: Point): Double {
val distanceX = this.x - that.x
val distanceY = this.y - that.y
return sqrt(distanceX * distanceX + distanceY * distanceY)
}
}
If the points are evenly (or almost evenly) distributed in some area, I suggest dividing them into rectangular chunks with size area.size.x / poses.size * 10 by area.size.y / poses.size * 10.
Then to find the nearest points for any point, you only need to check neighboring chunks. Since points are evenly distributed, you can find the nearest points for all points in O(kn) where n is a number of points and k = 10.
If the points are not guaranteed to be evenly (or almost evenly) distributed, you have to divide the area into several chunks and then recursively repeat the same process for each chunk until all the sub-chunks contain at most x points. (It's hard to tell what is optimal x and optimal count of sub-chunks per chunk, you need to do some research to find it out).
Then you can find the nearest points for any point, just as you did with evenly distributed points.
A few tricks to improve performance:
Use distanceSquared instead of distance. Here is how you can implement distanceSquared:
fun Point.distanceSquared(other: Point) = (x - other.x).squared() + (y - other.y).squared()
typealias Point = Array<Double>
val Point.x get() = this[0]
val Point.y get() = this[1]
fun Double.squared() = this * this
Use PriorityQueue<Point>(10, compareBy { -it.distanceSquared(destination) }) to store nearest points, and offer(point, 10) to add points to it:
fun <E : Any> PriorityQueue<E>.offer(element: E, maxSize: Int) {
if (size < maxSize) offer(element)
else if (compare(element, peek()) > 0) {
poll()
offer(element)
}
}
// if `comparator()` returns `null` queue uses naturalOrder and `E` is `Comparable<E>`
#Suppress("UNCHECKED_CAST")
fun <E : Any> PriorityQueue<E>.compare(o1: E, o2: E) =
comparator()?.compare(o1, o2) ?: (o1 as Comparable<E>).compareTo(o2)
Divide your points into several groups and run the calculation for each group in a separate thread. It will let your program to use all available cores.