Can anybody explain how I can release an app-level kotlin function in Android Studio project? I have an Android application and I try do someting like this:
var date: Date = Date()
/////////////////////////////////////////////////////////
// this block must be app-level fun
val format = “dd.MM.yyyy”
val simpleDateFormat = SimpleDateFormat(format)
var formattedDate = simpleDateFormat.format(date)
/////////////////////////////////////////////////////////
convert Date object to String with custom format. I do it many times (in different activities and fragments) in my project, so I think it will be good idea to releas this code as function (or class, if it will be more efficient). Thus I have date and format as input parameters and formattedDate as output. Also it will be good to set default format value
You can create an extension function on Date that accepts a format and uses it to convert the date to that format. You can also define the default format on the input parameter. Something like:
fun Date.toFormattedString(format: String = "dd.MM.yyyy"): String {
val simpleDateFormat = SimpleDateFormat(format)
return simpleDateFormat.format(this)
}
Place it in a file where the whole app can access it (e.g., a file named Extensions.kt in a module/package where you put all reusable and/or helper code) and then just use the function like someDate.toFormattedString().
Here is an example of my function contained in separate kotlin file called Time.kt
fun timeConverter(string: String?, i: Int): String {
val isoFormat = "yyyy-MM-dd'T'HH:mm:ss"
var expectedFormat = "dd/MM"
when(i){
0 -> expectedFormat = "dd/MM"
1 -> expectedFormat = "EEE"
2 -> expectedFormat = "HH:mm"
3 -> expectedFormat = "EEE, dd/MM"
}
val dateFormat = SimpleDateFormat(isoFormat, Locale.getDefault())
val date = dateFormat.parse(string)
return SimpleDateFormat(expectedFormat).format(date)
}
Make the function part of an object.
https://www.baeldung.com/kotlin-objects
Objects (not class) in Kotlin are static. If you import the object from where you use the function, it can be used anywhere without being instantiated.
You can have a DateUtil class that holds a format function as companion. You will be able to use it anywhere in your app without instantiating it.
class DateUtil{
companion object {
fun format(date: Date):String{
val format = "dd.MM.yyyy"
val simpleDateFormat = SimpleDateFormat(format)
return simpleDateFormat.format(date)
}
}
}
Then you call it: DateUtil.format(Date())
Related
I have a date with that format: 2027-02-14T14:20:00.000
I would like to take hours and minutes from it like in that case: 14:20
I was trying to do something like this:
val firstDate = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).parse("2027-02-14T14:20:00.000")
val firstTime = SimpleDateFormat("H:mm").format(firstDate)
but I got crash java.text.ParseException: Unparseable date
How to take hours and minutes from that string ?
One of the RECOMMENDED WAYs
In case you can use java.time, here's a commented example:
import java.time.LocalDateTime
import java.time.LocalDate
import java.time.format.DateTimeFormatter
fun main() {
// example String
val input = "2027-02-14T14:20:00.000"
// directly parse it to a LocalDateTime
val localDateTime = LocalDateTime.parse(input)
// print the (intermediate!) result
println(localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
// then extract the date part
val localDate = localDateTime.toLocalDate()
// print that
println(localDate)
}
This outputs 2 values, the intermediate LocalDateTime parsed and the extracted LocalDate (the latter simply invoking its toString() method implicitly):
2027-02-14T14:20:00
2027-02-14
NOT RECOMMENDED but still possible:
Still use the outdated API (might be necessary when it comes to large amounts of legacy code, which I doubt you will find written in Kotlin):
import java.text.SimpleDateFormat
fun main() {
val firstDate = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS")
.parse("2027-02-14T14:20:00.000")
val firstTime = SimpleDateFormat("yyyy-MM-dd").format(firstDate)
println(firstTime)
}
Output:
2027-02-14
Part 1:
I have date as string in the following format: "2020-10-15T22:54:54Z"
I have to convert it into date object.
Tried the following:
val dateString = "2020-10-15T22:54:54Z"
val dateFormatter = DateTimeFormatter.ofPattern("%Y-%m-%dT%H:%M:%SZ")
val updatedAtDate = LocalDate.parse(dateString, dateFormatter)
val today = LocalDateTime.now()
println("Updated At: $updatedAtDate")
Gives the following error: "Unknown pattern letter: T"
Part 2:
Once I have the date object from above, I have to calculate the difference between today (Current Date) and the above date. How to do it in Kotlin?
tl;dr You don't need to create a custom formatter…
… for the val dateString = "2020-10-15T22:54:54Z" here, it is formatted in an ISO standard. Therefore, you can simply do this (if you want a LocalDate, just year, month and day):
val thatDate: LocalDate = OffsetDateTime.parse(dateString).toLocalDate()
Fully adressing all the aspects of your post:
You can utilize a java.time.Period.between(LocalDate, LocalDate) in order to calculate the difference between two LocalDates. You only have to make sure the older date is the first argument, otherwise you will get a negative result. Here's a full example:
import java.time.OffsetDateTime
import java.time.LocalDate
import java.time.Period
fun main(args: Array<String>) {
val dateString = "2020-10-15T22:54:54Z"
// create an OffsetDateTime directly
val thatDate = OffsetDateTime.parse(dateString)
// and extract the date
.toLocalDate()
// get the current day (date only!)
val today = LocalDate.now()
// calculate the difference in days and make sure to get a positive result
val difference = if (today.isBefore(thatDate))
Period.between(today, thatDate).days
else
Period.between(thatDate, today).days
// print something containing both dates and the difference
println("[$today] Updated At: $thatDate, difference: $difference days")
}
Output at Dec 10, 2021:
[2021-12-10] Updated At: 2020-10-15, difference: 25 days
I'm sorry for this question name.
So I have a data class:
data class Day(
val date: Int = 0,
val night: Time? = null,
val morning: Time? = null,
val noon: Time? = null,
val evening: Time? = null,
)
I'm making variable with type of this class and i need to set values to this variable:
val days = ArrayList<Day>()
val listOfDays = this
.groupBy { it.getDayOfMonth() }
.map { WeatherForDay(it.value) }
.take(FIVE_DAYS_FORECAST)
listOfDays.forEach {
val day = Day()
var weather: WeatherList
for (hour in it.weather.indices) {
weather = it.weather[hour]
val time = Time(
weather.main.temp,
weather.main.feels_like,
weather.wind.speed,
weather.wind.gust,
weather.wind.deg,
weather.main.pressure,
)
when (it.weather[hour].getTime()) {
NIGHT_DAYTIME -> day.night = time
MORNING_DAYTIME -> day.morning = time
DAY_DAYTIME -> day.noon = time
EVENING_DAYTIME -> day.evening = time
}
day.date = weather.dt
}
days.add(day)
But i can't because my attributes in data class are values. And i need it to be values. So how can i make this code in a right style with val?
Rearrange your code so you only instantiate the class when you have all the parameters you need.
val listOfDays = this
.groupBy { it.getDayOfMonth() }
.map { WeatherForDay(it.value) }
.take(FIVE_DAYS_FORECAST)
val days = listOfDays.map { listOfDaysDay ->
val date = listOfDaysDay.weather.firstOrNull()?.dt ?: 0
val timeItemByTime = listOfDaysDay.weather
.associateBy { it.getTime() }
.map { with (it) {
Time(
main.temp
main.feels_like,
wind.speed,
wind.gust,
wind.deg,
main.pressure
)
} }
Day(
date,
timeItemByTime[NIGHT_DAYTIME],
timeItemByTime[MORNING_DAYTIME],
timeItemByTime[DAY_DAYTIME],
timeItemByTime[EVENING_DAYTIME]
)
}
I might have some errors above. It's kind of hard to keep your types straight because of your naming scheme. You have a "listOfDays" that is not a list of your Day class but of some other entity. And you have a class named Time that isn't a time but just a bunch of concurrent weather conditions. And you have something else called "time" that is apparently String or Int constants that actually do represent time.
And a suggestion. When you have two totally separate classes with a bunch of the same public properties, you might want to create an intermediate class so you can pass them around without having to manually copy six property values each time. Then if you want to add one more property you don't have to modify a whole bunch of classes.
You may use the day.copy(night = time) method that exists for each data class. If you don't want new allocations the only way that I see is to define local variables for each data class property and set them in the loop, and then, in the end of an iteration, create the instance of the data class on the base of these variables.
In Java, i can easily pass static function to layout xml using:
public static String formatUnixTime(long timeInSeconds, String pattern) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern, Locale.US);
String value = simpleDateFormat.format(new Date(timeInSeconds * 1000));
return value;
}
in xml:
android:text='#{Utils.formatUnixTime(model.start_time, "hh:mm:ss")}'
But i tried in Kotlin with companion but no luck. It said
error: cannot find symbol
import my.package.name.HistoryItemBindingImpl;
^
symbol: class HistoryItemBindingImpl
location: package my.package.name
This is what i tried in kotlin
class Utils {
companion object {
fun formatUnixTime(timeInSeconds : Long, pattern: String) : String {
val simpleDateFormat = SimpleDateFormat(pattern, Locale.US)
val value = simpleDateFormat.format(Date(timeInSeconds * 1000))
return value
}
}
And in xml
android:text='#{Utils.Companion.formatUnixTime(model.start_time, "hh:mm:ss")}'
Really hope someone can help. Thanks!
Update
With #Max Aves help. I fixed my code and below code will work. Maybe it will help someone.
class Utils {
companion object {
#JvmStatic
fun formatUnixTime(timeInSeconds : Long, pattern: String) : String {
val simpleDateFormat = SimpleDateFormat(pattern, Locale.US)
val value = simpleDateFormat.format(Date(timeInSeconds * 1000))
return value
}
And you can use this in xml
android:text='#{Utils.formatUnixTime(model.start_time, "hh:mm:ss")}'
Have you tried adding #JvmStatic annotation? It should help!
From official source:
Specifies that an additional static method needs to be generated from
this element if it's a function. If this element is a property,
additional static getter/setter methods should be generated.
Utilities are generally created as Kotlin File. Because Kotlin files methods are global. Which you can use from anywhere without making it #JvmStatic.
BindingAdapterDefault.kt
fun formatUnixTime(timeInSeconds: Long, pattern: String): String {
val simpleDateFormat = SimpleDateFormat(pattern, Locale.US)
return simpleDateFormat.format(Date(timeInSeconds * 1000))
}
That will work for you same, NO class, brackets, companion, object etc...
From XML
<import type="com.innovanathinklabs.sample.ui2.BindingAdapterDefaultKt"/>
android:text="#{BindingAdapterDefaultKt.formatUnixTime(1540966388,`hh:mm:ss`)}"
That's all you need.
I want add more info about this. You can directly call this method from Java and Kotlin too.
From Java class
import static com.package.BindingAdapterDefaultKt.formatUnixTime;
formatUnixTime(454545, "hh:mm:ss");
From Kotlin class
import com.package.formatUnixTime
formatUnixTime(454545, "hh:mm:ss");
Suggestion
I prefer creating BindingAdapter which was introduced with Data Binding, and is very powerful thing. It gives me more flexibility across the app.
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:formatSeconds="#{1540966388}"
app:pattern="#{`hh:mm:ss`}"
/>
BindingAdapterDefault.kt
#BindingAdapter(value = ["formatSeconds", "pattern"])
fun secondsToDateText(textView: TextView, timeInSeconds: Long, pattern: String) {
val simpleDateFormat = SimpleDateFormat(pattern, Locale.US)
textView.text = simpleDateFormat.format(Date(timeInSeconds * 1000))
}
If you don't want to use #JvmStatic, you can use object instead of class:
object Utils {
fun formatUnixTime(timeInSeconds : Long, pattern: String) : String {
val simpleDateFormat = SimpleDateFormat(pattern, Locale.US)
val value = simpleDateFormat.format(Date(timeInSeconds * 1000))
return value
}
}
From xml import the Utils singleton class and then:
android:text='#{Utils.INSTANCE.formatUnixTime(model.start_time, "hh:mm:ss")}'
I want to use the Date value of my Data class in view via Databinding.
If I use the toString() method on the Date field it works. But I want to customize the Date value.
So I created the Utils object with Method. This is the Util object
object DateUtils {
fun toSimpleString(date: Date) : String {
val format = SimpleDateFormat("dd/MM/yyy")
return format.format(date)
}
}
But if I want to use this method in the xml like this
<data>
<import type="de.mjkd.journeylogger.Utils.DateUtils"/>
<variable
name="journey"
type="de.mjkd.journeylogger.data.Journey"/>
</data>
...
android:text="#{DateUtils.toSimpleString(journey.date)}"
I get an error cannot find method toSimpleString(java.util.Date) in class ...
This is my Dataclass:
data class Journey(var title: String, var date: Date?, var destination: String)
Whats wrong with this code?
Using the reserved word object in kotlin, that you really doing is declare a single instance. the equivalent in java is something more or less like:
class DataUtils {
static DataUtils INSTANCE;
public String toSimpleString()...
}
then when you call it you do a DateUtils.INSTANCE.toSimpleString()
You should capable to use DateUtils.INSTANCE.toSimpleString() in your xml
In order to make toSimpleString accessible from static context, you have to flag the method with#JvmStatic
object DateUtils {
#JvmStatic
fun toSimpleString(date: Date) : String {
val format = SimpleDateFormat("dd/MM/yyy")
return format.format(date)
}
}
Using extension function(doc)
#file:JvmName("DateUtils")//Use this to change your class name in java, by default is <the file name>Kt (DateUtilsKt in your case)
fun Date.toSimpleString() : String {
val format = SimpleDateFormat("dd/MM/yyy")
return format.format(this)
}
Then you can use it directly in xml as you are already doing:
android:text="#{DateUtils.toSimpleString(journey.date)}"
Why don't you just use a top-level function which is static by default? A top-level function is not defined in any class.
fun main(args: Array<String>){
println(toSimpleString(Date()))
}
fun toSimpleString(date: Date?) = with(date ?: Date()) {
SimpleDateFormat("dd/MM/yyy").format(this)
}
Also, notice how Jouney's date is nullable in your example and your toSimpleString only accepts a non-nullable Date!
I changed it, so that it will return the string of the current date in case null is passed.
More easy way would be to make a getDateString in model class.
android:text="#{journey.dateString)}"
class Journey {
lateinit var date: Date
fun getDateString(){
return DataUtils.toSimpleString(date)
}
}
I like this way because I don't need to import any class in this case.