How should I format and parse a DateTimeFormatter object? - kotlin

I am trying to parse a date in parseServerTimeOffsetDateTime(), but I keep getting errors. Can't seem to make it work.
Here is what I have done so far:
For some reason the function keeps returning a Unit data type or TemporalAccessor if I try to parse the function with default parse function.
I tried converting it into a String or DataTimeFormatter and similar data types but in those cases it shows errors in try/catch block like a type missmatch and it won't compile.
I checked in documentation but am still unclear what TemporalAccessor is.
I thought of writing my own parser TimeParser, but I am not sure how I should implement it. Since the format of the date is 2022-05-24T07:52:03Z I thought of putting 'T' and 'Z' as a character where it should be parsed.
private val serverTimeFormatterOffsetDateTimeFormatter =
DateTimeFormatter.ISO_ZONED_DATE_TIME
private val fallbackServerTimeFormatterOffsetDateTime =
DateTimeFormatter.ISO_ZONED_DATE_TIME
fun OffsetDateTime.toServerDateFormatOffsetDateTime() = DateTimeFormatter.ofPattern("yyyy-MM-dd")
fun OffsetDateTime.toServerTimeFormatOffsetDateTime() =
serverTimeFormatterOffsetDateTimeFormatter.format(this)
fun String.parseServerTimeOffsetDateTime() = try {
serverTimeFormatterOffsetDateTimeFormatter.TimeParser(this)
} catch (pe: ParseException) {
fallbackServerTimeFormatterOffsetDateTime.TimeParser(this)
}
fun DateTimeFormatter.TimeParser(time: String) {
// Haven't done anything yet here
val delimeter = "T"
val delimeter2 = "Z"
// code for parsing
}
How should I format and parse a DateTimeFormatter object?

Related

how to clear the list after using

This is how I clear list after using, invalidDestination.clear(), but each time after I enter some invalid input, it will shows the previous error message.
for example
1st error message
"add fields abcd"
second time when I enter an invalid data like bcda, it should only return "add fields bcda", but the error message is, I already clear the list, what else should I do?
"add fields abcd, bcda"
private val validationErrors = mutableSetOf<ValidationError>()
private fun validateConfigTypeBduilder(configTypeBuilderList: List<ConfigTypeBuilder>, ruleAttributes: List<String>, destinationFieldList: List<String>) {
if (ruleAttributes.isNotEmpty()) {
var invalidDestination = mutableListOf<String>()
for (destinationField in destinationFieldList) {
if (!ruleAttributes.contains(destinationField)) {
invalidDestination.add(destinationField)
}
}
if (invalidDestination.firstOrNull { invalidDestination.contains(configTypeBuilder.destinationField) } != null)
addValidationError(""someMessage", "someMessage", $ADD_FIELDS $invalidDestination")
invalidDestination.clear()
}
}
private fun addValidationError(fieldPath: String, field: Any, error: String) {
logDataPathValidationError(fieldPath, field, error)
validationErrors.add(
ValidationError(
fieldPath,
error
)
)
}
internal fun logDataPathValidationError(dataPath: String, value: Any?, constraint: String) {
logger.info("{} {} value violates {} constraint", dataPath, value, constraint)
}
Thanks for Eric's inspire, I realized I should add clear destinationFieldList not invalidDestination after the if loop, to make sure next time calling validateConfigTypeBduilder with empty destinationFieldList
destinationFieldList.clear()
Each time you are calling validateConfigTypeBduilder a new list called invalidDestination is created. Then in the first loop you populate that list.
Then inside the if statement (the condition is a little weird the firstOrNull and the null check are irrelevant and removing them will produce the same result) you call addValidationError which logs the message and then clear the list.
If you are calling validateConfigTypeBduilder with the full list destinationFieldList then the number of errors reported will increase, since the list is produced and populated inside the function.
The list is getting cleared properly, but your code stops using it after clearing it.
Instead of sending the whole list addValidationError (assuming the invalidDestination list is ordered) you can send only the last element.
Changing this:
if (invalidDestination.firstOrNull { invalidDestination.contains(configTypeBuilder.destinationField) } != null)
addValidationError(""someMessage", "someMessage", $ADD_FIELDS $invalidDestination")
invalidDestination.clear()
}
to something like this:
invalidDestination.lastOrNull()?.let {
addValidationError(""someMessage", "someMessage", $ADD_FIELDS $it")
}
I don't fully understand you condition in the if statemente, so this is only an aproximation of the changes

Write text and Print in Kotlin

I have the following code:
package com.zetcode
import java.io.File
fun main() {
val fileName = "P3.txt"
val content = File(P3.txt).readText()
println(content)
}
My goal is to write a code in kotlin that reads the text file (P3.txt) and prints its content. I know there is something wrong because I keep receiving "unresolved reference.
File takes a string as input, you should change the line:
val content = File(P3.txt).readText()
to
val content = File("P3.txt").readText()
The difference is that without the quotes, kotlin thinks P3 is a reference that is not declared anywhere and you get the error you've mentioned.

File I/O in Kotlin with (potentially) unknown encoding

This is my first attempt to learn and use Kotlin. I have a simple task: read a file line by line, preprocess each line and put a specific portion into a map. Each line is tab-separated.
When trying to preprocess, things start going horribly wrong. I tried to debug, and instead of the normal characters, this is what I can see:
In between every two adjacent readable characters, there is the strange-looking block with horizontal lines.
Here is the code I wrote:
fun mapUserToId(path: String): MutableMap<String, Int> {
val user2id = mutableMapOf<String, Int>()
val bufferedReader = File(path).bufferedReader()
bufferedReader.useLines { lines ->
lines.drop(1).forEach { // dropping the first line with column headers
val components: List<String> = it.trim().split("\t") // split by tab delimiter
val user: String = components[2]
println(user.length) // length is nearly double due to the strange block-like characters
val id: String = components[3]
user2id[user] = id.toInt() // fails due to number format exception, because of those block-like characters
}
}
return user2id
}
This looks like a charset issue, but I can't figure out what the charset could be, and how to specify that charset in the above code. Opening the file in vim looks perfectly normal (as in, one would suspect that this file has UTF-8 encoding).
This is, indeed, an encoding issue. The problem is resolved by specifying the encoding while creating the buffered reader as follows:
val bufferedReader: BufferedReader = File(path).bufferedReader(Charsets.UTF_16)

Kotlin map a string to another type?

In swift, I can do
"Some String".map { SomeObject($0) }
In kotlin it seems like the string is treated as a char array so the result is the map of each character. Is it possible to get similar behavior like the swift code I posted?
"Some String".map { SomeObject(it) }
You can accomplish something like that with let:
"Some String".let { SomeObject(it) }
If you have an appropriate constructor in place (e.g. constructor(s : String) : this(...)) you can also call it as follows:
"Some String".let(::SomeObject)
run and with work also, but are usually taken if you want to rather call a method of the receiver on it. Using run/with for this would look as follows:
"Some String".run { SomeObject(this) }
with ("Some String") { SomeObject(this) }
// but run / with is rather useful for things like the following (where the shown function calls are functions of SomeObject):
val x = someObject.run {
doSomethingBefore()
returningSomethingElse()
}
Besides using let, run or with, you can also write an extension method:
fun String.toSomeObject() = SomeObject(this)
Then use it like follows:
"SomeObject".toSomeObject()

Why does IntelliJ suggest to convert a call chain into a Sequence?

Assume the following Kotlin example that maps the source set src to a destination set dst:
private val src: Set<String> = setOf("hello", "world")
private val dst: Set<Int> = src.map { it.length }.toSet()
This works fine. However, IntelliJ's code inspection suggests: Call chain on collection should be converted into 'Sequence':
Applying this suggestion results in
private val dst: Set<Int> = src.asSequence().map { it.length }.toSet()
What is the benefit of this?
In this case the suggestion is suboptimal. The correct way to rewrite this code (which also doesn't result in any IntelliJ warnings) is:
src.mapTo(hashSetOf()) { it.length }
This will avoid the creation of an intermediate list that will be subsequently converted to a set; the data will be added to the resulting set right away.
Set.map returns a list, which you then immediately throw away after converting it to a set. The benefit of asSequence is that the sequence does the conversion, presumably without a temporary list.