Removed fallback of using platform specific DateFormatter

This commit is contained in:
dankito 2024-09-11 01:09:50 +02:00
parent 7ddeb88475
commit fd9eadf45e
9 changed files with 46 additions and 108 deletions

View File

@ -5,6 +5,6 @@ import kotlin.reflect.KClass
interface IMessageLogAppender {
fun logError(loggingClass: KClass<*>, message: String, e: Exception? = null)
fun logError(loggingClass: KClass<*>, message: String, e: Throwable? = null)
}

View File

@ -69,7 +69,7 @@ open class MessageLogCollector(
addMessageLogEntry(type, context, messageTrace, prettyPrintMessage, null, parsedSegments)
}
open fun logError(loggingClass: KClass<*>, message: String, context: MessageContext, e: Exception? = null) {
open fun logError(loggingClass: KClass<*>, message: String, context: MessageContext, e: Throwable? = null) {
val type = MessageLogEntryType.Error
val messageTrace = createMessageTraceString(type, context)

View File

@ -1,12 +0,0 @@
package net.codinux.banking.fints.mapper
import kotlinx.datetime.LocalDate
/**
* Be aware that Java DateFormat is not thread safe!
*/
expect class DateFormatter constructor(pattern: String) {
fun parseDate(dateString: String): LocalDate?
}

View File

@ -82,7 +82,7 @@ open class JobContext(
messageLogCollector.addMessageLog(type, message, createMessageContext(), parsedSegments)
}
override fun logError(loggingClass: KClass<*>, message: String, e: Exception?) {
override fun logError(loggingClass: KClass<*>, message: String, e: Throwable?) {
messageLogCollector.logError(loggingClass, message, createMessageContext(), e)
}

View File

@ -6,7 +6,6 @@ import net.codinux.log.logger
import net.codinux.banking.fints.log.IMessageLogAppender
import net.codinux.banking.fints.model.Amount
import net.codinux.banking.fints.transactions.mt940.model.*
import net.codinux.banking.fints.mapper.DateFormatter
/*
@ -66,8 +65,6 @@ abstract class Mt94xParserBase<T: AccountStatementCommon>(
const val AmountOfCreditPostingsCode = "90C"
val DateFormatter = DateFormatter("yyMMdd") // TODO: replace with LocalDate.Format { }
val CreditDebitCancellationRegex = Regex("C|D|RC|RD")
val AmountRegex = Regex("\\d+,\\d*")
@ -215,7 +212,7 @@ abstract class Mt94xParserBase<T: AccountStatementCommon>(
val isDebit = fieldValue.startsWith("D")
val bookingDateString = fieldValue.substring(1, 7)
val statementDate = parseMt940Date(bookingDateString)
val statementDate = parseDate(bookingDateString)
val currency = fieldValue.substring(7, 10)
val amountString = fieldValue.substring(10)
val amount = parseAmount(amountString)
@ -259,7 +256,7 @@ abstract class Mt94xParserBase<T: AccountStatementCommon>(
*/
protected open fun parseStatementLine(fieldValue: String): StatementLine {
val valueDateString = fieldValue.substring(0, 6)
val valueDate = parseMt940Date(valueDateString)
val valueDate = parseDate(valueDateString)
val creditMarkMatchResult = CreditDebitCancellationRegex.find(fieldValue)
val isDebit = creditMarkMatchResult?.value?.endsWith('D') == true
@ -469,12 +466,7 @@ abstract class Mt94xParserBase<T: AccountStatementCommon>(
}
open fun parseMt940Date(dateString: String): LocalDate {
// TODO: this should be necessary anymore, isn't it?
// SimpleDateFormat is not thread-safe. Before adding another library i decided to parse
// this really simple date format on my own
if (dateString.length == 6) {
open fun parseDate(dateString: String): LocalDate {
try {
var year = dateString.substring(0, 2).toInt()
val month = dateString.substring(2, 4).toInt()
@ -503,24 +495,22 @@ abstract class Mt94xParserBase<T: AccountStatementCommon>(
}
return LocalDate(year , month, day)
} catch (e: Exception) {
} catch (e: Throwable) {
logError("Could not parse dateString '$dateString'", e)
throw e
}
}
return DateFormatter.parseDate(dateString)!! // fallback to not thread-safe SimpleDateFormat. Works in most cases but not all
}
/**
* Booking date string consists only of MMDD -> we need to take the year from value date string.
*/
protected open fun parseMt940BookingDate(bookingDateString: String, valueDateString: String, valueDate: LocalDate): LocalDate {
val bookingDate = parseMt940Date(valueDateString.substring(0, 2) + bookingDateString)
val bookingDate = parseDate(valueDateString.substring(0, 2) + bookingDateString)
// there are rare cases that booking date is e.g. on 31.12.2019 and value date on 01.01.2020 -> booking date would be on 31.12.2020 (and therefore in the future)
val bookingDateMonth = bookingDate.month
if (bookingDateMonth != valueDate.month && bookingDateMonth == Month.DECEMBER) {
return parseMt940Date("" + (valueDate.year - 1 - 2000) + bookingDateString)
return parseDate("" + (valueDate.year - 1 - 2000) + bookingDateString)
}
return bookingDate
@ -534,7 +524,7 @@ abstract class Mt94xParserBase<T: AccountStatementCommon>(
}
open fun parseDateTime(dateTimeString: String): Instant {
val date = parseMt940Date(dateTimeString.substring(0, 6))
val date = parseDate(dateTimeString.substring(0, 6))
val time = parseTime(dateTimeString.substring(6, 10))
@ -555,10 +545,8 @@ abstract class Mt94xParserBase<T: AccountStatementCommon>(
}
protected open fun logError(message: String, e: Exception?) {
logAppender?.let { logAppender ->
logAppender.logError(Mt94xParserBase::class, message, e)
}
protected open fun logError(message: String, e: Throwable?) {
logAppender?.logError(Mt94xParserBase::class, message, e)
?: run {
log.error(e) { message }
}

View File

@ -15,8 +15,8 @@ import kotlin.test.assertContains
class Mt940ParserTest : MtParserTestBase() {
private val underTest = object : Mt940Parser() {
public override fun parseMt940Date(dateString: String): LocalDate {
return super.parseMt940Date(dateString)
public override fun parseDate(dateString: String): LocalDate {
return super.parseDate(dateString)
}
}
@ -229,28 +229,28 @@ class Mt940ParserTest : MtParserTestBase() {
@Test
fun parseDate() {
val result = underTest.parseMt940Date("240507")
val result = underTest.parseDate("240507")
assertEquals(LocalDate(2024, 5, 7), result)
}
@Test
fun parseDateBeforeYear2000() {
val result = underTest.parseMt940Date("990507")
val result = underTest.parseDate("990507")
assertEquals(LocalDate(1999, 5, 7), result)
}
@Test
fun parseDate_FixSparkasse29thOFFebruaryInNonLeapYearBug() {
val result = underTest.parseMt940Date("230229")
val result = underTest.parseDate("230229")
assertEquals(LocalDate(2023, 3, 1), result)
}
@Test
fun parseDate_FixSparkasse30thOfFebruaryBug() {
val result = underTest.parseMt940Date("230229")
val result = underTest.parseDate("230229")
assertEquals(LocalDate(2023, 3, 1), result)
}

View File

@ -1,11 +0,0 @@
package net.codinux.banking.fints.mapper
import kotlinx.datetime.LocalDate
actual class DateFormatter actual constructor(pattern: String) {
actual fun parseDate(dateString: String): LocalDate? {
return null // is only used in rare cases, don't implement right now
}
}

View File

@ -1,16 +0,0 @@
package net.codinux.banking.fints.mapper
import kotlinx.datetime.LocalDate
import kotlinx.datetime.toKotlinLocalDate
import java.time.format.DateTimeFormatter
actual class DateFormatter actual constructor(pattern: String) {
private val formatter = DateTimeFormatter.ofPattern(pattern)
actual fun parseDate(dateString: String): LocalDate? {
return java.time.LocalDate.parse(dateString, formatter)?.toKotlinLocalDate()
}
}

View File

@ -1,11 +0,0 @@
package net.codinux.banking.fints.mapper
import kotlinx.datetime.LocalDate
actual class DateFormatter actual constructor(pattern: String) {
actual fun parseDate(dateString: String): LocalDate? {
return null // is only used in rare cases, don't implement right now
}
}