Replaced own Date implementation with kotlinx-datetime
This commit is contained in:
parent
fa378f6249
commit
da9d5c018b
|
@ -45,6 +45,8 @@ kotlin {
|
|||
dependencies {
|
||||
api project(":multiplatform-utils")
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-datetime:0.3.2"
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
|
||||
|
||||
implementation "io.ktor:ktor-client-core:$ktorVersion"
|
||||
|
@ -92,8 +94,16 @@ kotlin {
|
|||
|
||||
// jsMain {
|
||||
// dependencies {
|
||||
// implementation npm("@js-joda/timezone", "2.3.0")
|
||||
// implementation "io.ktor:ktor-client-js:$ktorVersion"
|
||||
// }
|
||||
/* Plus:
|
||||
@JsModule("@js-joda/timezone")
|
||||
@JsNonModule
|
||||
external object JsJodaTimeZoneModule
|
||||
|
||||
private val jsJodaTz = JsJodaTimeZoneModule
|
||||
*/
|
||||
// }
|
||||
//
|
||||
// jsTest {
|
||||
|
|
|
@ -2,13 +2,15 @@ package net.dankito.banking.fints
|
|||
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.datetime.*
|
||||
import net.dankito.banking.fints.callback.FinTsClientCallback
|
||||
import net.dankito.utils.multiplatform.extensions.minusDays
|
||||
import net.dankito.utils.multiplatform.extensions.todayAtEuropeBerlin
|
||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.*
|
||||
import net.dankito.banking.fints.model.*
|
||||
import net.dankito.banking.fints.response.BankResponse
|
||||
import net.dankito.banking.fints.response.client.*
|
||||
import net.dankito.banking.fints.response.segments.*
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
|
||||
|
@ -144,7 +146,8 @@ open class FinTsClient @JvmOverloads constructor(
|
|||
}
|
||||
|
||||
protected open fun createGetAccountTransactionsOfLast90DaysParameter(bank: BankData, account: AccountData): GetAccountTransactionsParameter {
|
||||
val ninetyDaysAgo = Date.today.addDays(-90)
|
||||
// Europe/Berlin: we're communicating with German bank servers, so we have to use their time zone
|
||||
val ninetyDaysAgo = LocalDate.todayAtEuropeBerlin().minusDays(90)
|
||||
|
||||
return GetAccountTransactionsParameter(bank, account, account.supportsRetrievingBalance, ninetyDaysAgo, abortIfTanIsRequired = true)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package net.dankito.banking.fints
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
import net.dankito.banking.fints.messages.MessageBuilder
|
||||
import net.dankito.banking.fints.messages.MessageBuilderResult
|
||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.VersionDesSicherheitsverfahrens
|
||||
|
@ -16,8 +17,11 @@ import net.dankito.banking.fints.tan.FlickerCodeDecoder
|
|||
import net.dankito.banking.fints.tan.TanImageDecoder
|
||||
import net.dankito.banking.fints.util.TanMethodSelector
|
||||
import net.dankito.utils.multiplatform.log.LoggerFactory
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import net.dankito.utils.multiplatform.ObjectReference
|
||||
import net.dankito.utils.multiplatform.extensions.millisSinceEpochAtEuropeBerlin
|
||||
import net.dankito.utils.multiplatform.extensions.minusDays
|
||||
import net.dankito.utils.multiplatform.extensions.todayAtEuropeBerlin
|
||||
import net.dankito.utils.multiplatform.extensions.todayAtSystemDefaultTimeZone
|
||||
|
||||
|
||||
/**
|
||||
|
@ -236,9 +240,9 @@ open class FinTsJobExecutor(
|
|||
val successful = response.tanRequiredButWeWereToldToAbortIfSo
|
||||
|| (response.successful && (parameter.alsoRetrieveBalance == false || balance != null))
|
||||
val fromDate = parameter.fromDate
|
||||
?: parameter.account.countDaysForWhichTransactionsAreKept?.let { Date.today.addDays(it * -1) }
|
||||
?: bookedTransactions.map { it.valueDate }.sortedBy { it.millisSinceEpoch }.firstOrNull()
|
||||
val retrievedData = RetrievedAccountData(parameter.account, successful, balance, bookedTransactions, unbookedTransactions, fromDate, parameter.toDate ?: Date.today, response.internalError)
|
||||
?: parameter.account.countDaysForWhichTransactionsAreKept?.let { LocalDate.todayAtSystemDefaultTimeZone().minusDays(it) }
|
||||
?: bookedTransactions.minByOrNull { it.valueDate.millisSinceEpochAtEuropeBerlin }?.valueDate
|
||||
val retrievedData = RetrievedAccountData(parameter.account, successful, balance, bookedTransactions, unbookedTransactions, fromDate, parameter.toDate ?: LocalDate.todayAtEuropeBerlin(), response.internalError)
|
||||
|
||||
callback(GetAccountTransactionsResponse(context, response, retrievedData,
|
||||
if (parameter.maxCountEntries != null) parameter.isSettingMaxCountEntriesAllowedByBank else null))
|
||||
|
|
|
@ -6,8 +6,8 @@ import net.dankito.banking.fints.model.MessageLogEntryType
|
|||
import net.dankito.utils.multiplatform.log.Logger
|
||||
import net.dankito.utils.multiplatform.log.LoggerFactory
|
||||
import net.dankito.utils.multiplatform.StackTraceHelper
|
||||
import net.dankito.utils.multiplatform.extensions.format
|
||||
import net.dankito.utils.multiplatform.extensions.getInnerException
|
||||
import net.dankito.utils.multiplatform.extensions.toStringWithTwoDigits
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
|
@ -80,7 +80,7 @@ open class MessageLogCollector {
|
|||
}
|
||||
|
||||
protected open fun twoDigits(number: Int): String {
|
||||
return number.format("%02d")
|
||||
return number.toStringWithTwoDigits()
|
||||
}
|
||||
|
||||
protected open fun getMessageTypeString(type: MessageLogEntryType): String {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package net.dankito.banking.fints.messages
|
||||
|
||||
import net.dankito.utils.multiplatform.extensions.randomWithSeed
|
||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.Aufsetzpunkt
|
||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.KundensystemID
|
||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.Synchronisierungsmodus
|
||||
|
@ -22,9 +23,7 @@ import net.dankito.banking.fints.model.*
|
|||
import net.dankito.banking.fints.response.InstituteSegmentId
|
||||
import net.dankito.banking.fints.response.segments.*
|
||||
import net.dankito.banking.fints.util.FinTsUtils
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
/**
|
||||
|
@ -453,7 +452,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
|
|||
}
|
||||
|
||||
protected open fun createControlReference(): String {
|
||||
return Random(Date().millisSinceEpoch).nextInt().absoluteValue.toString()
|
||||
return randomWithSeed().nextInt().absoluteValue.toString()
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
import net.dankito.banking.fints.messages.Existenzstatus
|
||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import net.dankito.utils.multiplatform.DateFormatter
|
||||
import net.dankito.utils.multiplatform.extensions.toStringWithMinDigits
|
||||
import net.dankito.utils.multiplatform.extensions.toStringWithTwoDigits
|
||||
import net.dankito.utils.multiplatform.log.LoggerFactory
|
||||
|
||||
|
||||
|
@ -17,17 +18,15 @@ open class Datum(date: Int?, existenzstatus: Existenzstatus) : NumerischesDatene
|
|||
companion object {
|
||||
const val HbciDateFormatString = "yyyyMMdd"
|
||||
|
||||
val HbciDateFormat = DateFormatter(HbciDateFormatString)
|
||||
|
||||
|
||||
private val log = LoggerFactory.getLogger(Datum::class)
|
||||
|
||||
|
||||
fun format(date: Date): String {
|
||||
return HbciDateFormat.format(date) // TODO: is this correct?
|
||||
fun format(date: LocalDate): String { // create HbciDateFormatString
|
||||
return date.year.toStringWithMinDigits(4) + date.monthNumber.toStringWithTwoDigits() + date.dayOfMonth.toStringWithTwoDigits() // TODO: is this correct?
|
||||
}
|
||||
|
||||
fun parse(dateString: String): Date {
|
||||
fun parse(dateString: String): LocalDate {
|
||||
// do not use DateFormatter as Java DateFormat is not thread safe, resulting in a lot of curious errors in parallel execution
|
||||
|
||||
if (dateString.length == 8) {
|
||||
|
@ -36,7 +35,7 @@ open class Datum(date: Int?, existenzstatus: Existenzstatus) : NumerischesDatene
|
|||
val month = dateString.substring(4, 6)
|
||||
val day = dateString.substring(6, 8)
|
||||
|
||||
return Date(year.toInt(), month.toInt(), day.toInt())
|
||||
return LocalDate(year.toInt(), month.toInt(), day.toInt())
|
||||
} catch (e: Exception) {
|
||||
log.error(e) { "Could not parse date string '$dateString' to HBCI date" }
|
||||
}
|
||||
|
@ -47,7 +46,7 @@ open class Datum(date: Int?, existenzstatus: Existenzstatus) : NumerischesDatene
|
|||
}
|
||||
|
||||
|
||||
constructor(date: Date?, existenzstatus: Existenzstatus)
|
||||
constructor(date: LocalDate?, existenzstatus: Existenzstatus)
|
||||
: this(date?.let { format(it).toInt() }, existenzstatus)
|
||||
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
package net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate
|
||||
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import net.dankito.utils.multiplatform.extensions.of
|
||||
import net.dankito.banking.fints.messages.Existenzstatus
|
||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.ZiffernDatenelement
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import net.dankito.utils.multiplatform.DateFormatter
|
||||
import net.dankito.utils.multiplatform.extensions.toStringWithTwoDigits
|
||||
import net.dankito.utils.multiplatform.log.LoggerFactory
|
||||
|
||||
|
||||
|
@ -18,17 +19,15 @@ open class Uhrzeit(time: Int?, existenzstatus: Existenzstatus) : ZiffernDatenele
|
|||
companion object {
|
||||
const val HbciTimeFormatString = "HHmmss"
|
||||
|
||||
val HbciTimeFormat = DateFormatter(HbciTimeFormatString)
|
||||
|
||||
|
||||
private val log = LoggerFactory.getLogger(Uhrzeit::class)
|
||||
|
||||
|
||||
fun format(time: Date): String {
|
||||
return HbciTimeFormat.format(time) // TODO: is this correct?
|
||||
fun format(time: LocalDateTime): String { // parse to HbciTimeFormatString
|
||||
return time.hour.toStringWithTwoDigits() + time.minute.toStringWithTwoDigits() + time.second.toStringWithTwoDigits() // TODO: is this correct?
|
||||
}
|
||||
|
||||
fun parse(timeString: String): Date {
|
||||
fun parse(timeString: String): LocalDateTime {
|
||||
// do not use DateFormatter as Java DateFormat is not thread safe, resulting in a lot of curious errors in parallel execution
|
||||
|
||||
if (timeString.length == 6) {
|
||||
|
@ -37,18 +36,18 @@ open class Uhrzeit(time: Int?, existenzstatus: Existenzstatus) : ZiffernDatenele
|
|||
val minute = timeString.substring(2, 4)
|
||||
val second = timeString.substring(4, 6)
|
||||
|
||||
return Date(0, 0, 0, hour.toInt(), minute.toInt(), second.toInt())
|
||||
return LocalDateTime.of(hour.toInt(), minute.toInt(), second.toInt())
|
||||
} catch (e: Exception) {
|
||||
log.error(e) { "Could not parse time string '$timeString' to HBCI time" }
|
||||
}
|
||||
}
|
||||
|
||||
throw IllegalArgumentException("Cannot parse '$timeString' to HBCI Time. Only times in format '${Uhrzeit.HbciTimeFormatString}' are allowed in HBCI / FinTS.")
|
||||
throw IllegalArgumentException("Cannot parse '$timeString' to HBCI Time. Only times in format '${HbciTimeFormatString}' are allowed in HBCI / FinTS.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
constructor(time: Date?, existenzstatus: Existenzstatus)
|
||||
constructor(time: LocalDateTime?, existenzstatus: Existenzstatus)
|
||||
: this(time?.let { format(time).toInt() }, existenzstatus)
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package net.dankito.banking.fints.messages.datenelemente.implementierte.tan
|
||||
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import kotlinx.datetime.LocalDate
|
||||
|
||||
|
||||
open class TanGeneratorTanMedium(
|
||||
|
@ -9,8 +9,8 @@ open class TanGeneratorTanMedium(
|
|||
val cardNumber: String,
|
||||
val cardSequenceNumber: String?,
|
||||
val cardType: Int?,
|
||||
val validFrom: Date?,
|
||||
val validTo: Date?,
|
||||
val validFrom: LocalDate?,
|
||||
val validTo: LocalDate?,
|
||||
mediumName: String?
|
||||
) : TanMedium(mediumClass, status, mediumName) {
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package net.dankito.banking.fints.messages.segmente.implementierte.sepa
|
||||
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import net.dankito.utils.multiplatform.extensions.nowAtUtc
|
||||
import net.dankito.utils.multiplatform.DateFormatter
|
||||
|
||||
|
||||
|
@ -131,8 +132,8 @@ open class SepaMessageCreator : ISepaMessageCreator {
|
|||
override fun createXmlFile(messageTemplate: PaymentInformationMessages, replacementStrings: Map<String, String>): String {
|
||||
var xmlFile = messageTemplate.xmlTemplate
|
||||
|
||||
val now = Date()
|
||||
val nowInIsoDate = IsoDateFormat.format(now)
|
||||
val now = LocalDateTime.nowAtUtc()
|
||||
val nowInIsoDate = now.toString() // applies formatting to ISO date time string
|
||||
|
||||
if (replacementStrings.containsKey(MessageIdKey) == false) {
|
||||
xmlFile = replacePlaceholderWithValue(xmlFile, MessageIdKey, nowInIsoDate)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package net.dankito.banking.fints.model
|
||||
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import kotlinx.datetime.LocalDate
|
||||
import net.dankito.utils.multiplatform.extensions.atUnixEpochStart
|
||||
|
||||
|
||||
open class AccountTransaction(
|
||||
|
@ -8,12 +9,12 @@ open class AccountTransaction(
|
|||
val amount: Money,
|
||||
val isReversal: Boolean,
|
||||
val unparsedReference: String,
|
||||
val bookingDate: Date,
|
||||
val bookingDate: LocalDate,
|
||||
val otherPartyName: String?,
|
||||
val otherPartyBankCode: String?,
|
||||
val otherPartyAccountId: String?,
|
||||
val bookingText: String?,
|
||||
val valueDate: Date,
|
||||
val valueDate: LocalDate,
|
||||
val statementNumber: Int,
|
||||
val sequenceNumber: Int?,
|
||||
val openingBalance: Money?,
|
||||
|
@ -44,9 +45,9 @@ open class AccountTransaction(
|
|||
) {
|
||||
|
||||
// for object deserializers
|
||||
internal constructor() : this(AccountData(), Money(Amount.Zero, ""), "", Date(0), null, null, null, null, Date(0))
|
||||
internal constructor() : this(AccountData(), Money(Amount.Zero, ""), "", LocalDate.atUnixEpochStart, null, null, null, null, LocalDate.atUnixEpochStart)
|
||||
|
||||
constructor(account: AccountData, amount: Money, unparsedReference: String, bookingDate: Date, otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?, bookingText: String?, valueDate: Date)
|
||||
constructor(account: AccountData, amount: Money, unparsedReference: String, bookingDate: LocalDate, otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?, bookingText: String?, valueDate: LocalDate)
|
||||
: this(account, amount, false, unparsedReference, bookingDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate,
|
||||
0, null, null, null,
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
package net.dankito.banking.fints.model
|
||||
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import net.dankito.utils.multiplatform.format
|
||||
import kotlinx.datetime.LocalDate
|
||||
import net.dankito.utils.multiplatform.extensions.format
|
||||
|
||||
|
||||
open class CreditCardTransaction(
|
||||
open val amount: Money,
|
||||
open val transactionDescriptionBase: String?,
|
||||
open val transactionDescriptionSupplement: String?,
|
||||
open val bookingDate: Date,
|
||||
open val valueDate: Date,
|
||||
open val bookingDate: LocalDate,
|
||||
open val valueDate: LocalDate,
|
||||
open val isCleared: Boolean
|
||||
) {
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package net.dankito.banking.fints.model
|
||||
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
|
||||
|
@ -8,8 +8,8 @@ open class GetAccountTransactionsParameter @JvmOverloads constructor(
|
|||
bank: BankData,
|
||||
account: AccountData,
|
||||
alsoRetrieveBalance: Boolean = true,
|
||||
fromDate: Date? = null,
|
||||
toDate: Date? = null,
|
||||
fromDate: LocalDate? = null,
|
||||
toDate: LocalDate? = null,
|
||||
|
||||
/**
|
||||
* Be aware this is by far not supported by all banks.
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package net.dankito.banking.fints.model
|
||||
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlin.jvm.JvmOverloads
|
||||
|
||||
|
||||
open class GetTransactionsParameter @JvmOverloads constructor(
|
||||
open val bank: BankData,
|
||||
open val alsoRetrieveBalance: Boolean = true,
|
||||
open val fromDate: Date? = null,
|
||||
open val toDate: Date? = null,
|
||||
open val fromDate: LocalDate? = null,
|
||||
open val toDate: LocalDate? = null,
|
||||
|
||||
/**
|
||||
* Be aware this is by far not supported by all banks.
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
package net.dankito.banking.fints.model
|
||||
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import net.dankito.banking.fints.log.MessageContext
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
|
||||
|
||||
open class MessageLogEntry(
|
||||
open val type: MessageLogEntryType,
|
||||
open val message: String,
|
||||
open val context: MessageContext,
|
||||
open val time: Date = Date()
|
||||
open val time: Instant = Clock.System.now()
|
||||
) {
|
||||
|
||||
override fun toString(): String {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package net.dankito.banking.fints.model
|
||||
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import kotlinx.datetime.LocalDate
|
||||
|
||||
|
||||
open class RetrievedAccountData(
|
||||
|
@ -9,8 +9,8 @@ open class RetrievedAccountData(
|
|||
open val balance: Money?,
|
||||
open var bookedTransactions: Collection<AccountTransaction>,
|
||||
open var unbookedTransactions: Collection<Any>,
|
||||
open val retrievedTransactionsFrom: Date?,
|
||||
open val retrievedTransactionsTo: Date?,
|
||||
open val retrievedTransactionsFrom: LocalDate?,
|
||||
open val retrievedTransactionsTo: LocalDate?,
|
||||
open val errorMessage: String? = null
|
||||
) {
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package net.dankito.banking.fints.response
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import kotlinx.datetime.atTime
|
||||
import net.dankito.banking.fints.log.IMessageLogAppender
|
||||
import net.dankito.banking.fints.messages.Separators
|
||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Datum
|
||||
|
@ -20,7 +23,6 @@ import net.dankito.banking.fints.model.CreditCardTransaction
|
|||
import net.dankito.banking.fints.model.Money
|
||||
import net.dankito.banking.fints.response.segments.*
|
||||
import net.dankito.banking.fints.util.MessageUtils
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import net.dankito.utils.multiplatform.extensions.getAllExceptionMessagesJoined
|
||||
import net.dankito.utils.multiplatform.log.LoggerFactory
|
||||
|
||||
|
@ -674,13 +676,13 @@ open class ResponseParser(
|
|||
var currency: String? = null
|
||||
|
||||
var dateIndex = 2
|
||||
var date: Date? = parseNullableDate(dataElements[dateIndex]) // in older versions dateElements[2] was the currency
|
||||
var date: LocalDate? = parseNullableDate(dataElements[dateIndex]) // in older versions dateElements[2] was the currency
|
||||
if (date == null) {
|
||||
currency = parseString(dataElements[dateIndex])
|
||||
date = parseDate(dataElements[++dateIndex])
|
||||
}
|
||||
|
||||
var time: Date? = null
|
||||
var time: LocalDateTime? = null
|
||||
if (dataElements.size > dateIndex + 1) {
|
||||
try {
|
||||
time = parseTime(dataElements[dateIndex + 1])
|
||||
|
@ -969,13 +971,13 @@ open class ResponseParser(
|
|||
return Amount(adjustedAmountString)
|
||||
}
|
||||
|
||||
protected open fun parseNullableDateTime(dataElementGroup: String): Date? {
|
||||
protected open fun parseNullableDateTime(dataElementGroup: String): LocalDateTime? {
|
||||
val dataElements = getDataElements(dataElementGroup)
|
||||
|
||||
if (dataElements.size >= 2) {
|
||||
parseNullableDate(dataElements[0])?.let { date ->
|
||||
parseNullableTime(dataElements[1])?.let { time ->
|
||||
return Date(date.millisSinceEpoch + time.millisSinceEpoch) // TODO: is this correct?
|
||||
return date.atTime(time.hour, time.minute, time.second) // TODO: is this correct?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -983,11 +985,11 @@ open class ResponseParser(
|
|||
return null
|
||||
}
|
||||
|
||||
protected open fun parseDate(dateString: String): Date {
|
||||
protected open fun parseDate(dateString: String): LocalDate {
|
||||
return Datum.parse(dateString)
|
||||
}
|
||||
|
||||
protected open fun parseNullableDate(dateString: String): Date? {
|
||||
protected open fun parseNullableDate(dateString: String): LocalDate? {
|
||||
try {
|
||||
return parseDate(dateString)
|
||||
} catch (ignored: Exception) { }
|
||||
|
@ -995,11 +997,11 @@ open class ResponseParser(
|
|||
return null
|
||||
}
|
||||
|
||||
protected open fun parseTime(timeString: String): Date {
|
||||
protected open fun parseTime(timeString: String): LocalDateTime {
|
||||
return Uhrzeit.parse(timeString)
|
||||
}
|
||||
|
||||
protected open fun parseNullableTime(timeString: String): Date? {
|
||||
protected open fun parseNullableTime(timeString: String): LocalDateTime? {
|
||||
try {
|
||||
return parseTime(timeString)
|
||||
} catch (ignored: Exception) { }
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
package net.dankito.banking.fints.response.segments
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import net.dankito.banking.fints.model.Amount
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
|
||||
|
||||
open class Balance(
|
||||
val amount: Amount,
|
||||
val currency: String?,
|
||||
val date: Date,
|
||||
val time: Date?
|
||||
val date: LocalDate,
|
||||
val time: LocalDateTime?
|
||||
) {
|
||||
|
||||
override fun toString(): String {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package net.dankito.banking.fints.response.segments
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
import net.dankito.banking.fints.model.Amount
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
|
||||
|
||||
open class BalanceSegment(
|
||||
val balance: Amount,
|
||||
val currency: String,
|
||||
val date: Date,
|
||||
val date: LocalDate,
|
||||
val accountProductName: String,
|
||||
val balanceOfPreBookedTransactions: Amount?,
|
||||
segmentString: String
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package net.dankito.banking.fints.response.segments
|
||||
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanProcess
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
|
||||
|
||||
open class TanResponse(
|
||||
|
@ -31,7 +31,7 @@ open class TanResponse(
|
|||
val challenge: String?, // M: bei TAN-Prozess=1, 3, 4. O: bei TAN-Prozess=2
|
||||
|
||||
val challengeHHD_UC: String?,
|
||||
val validityDateTimeForChallenge: Date?,
|
||||
val validityDateTimeForChallenge: LocalDateTime?,
|
||||
val tanMediaIdentifier: String? = null, // M: bei TAN-Prozess=1, 3, 4 und „Anzahl unterstützter aktiver TAN-Medien“ nicht vorhanden. O: sonst
|
||||
|
||||
segmentString: String
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package net.dankito.banking.fints.transactions.mt940
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.Month
|
||||
import net.dankito.utils.multiplatform.extensions.todayAtEuropeBerlin
|
||||
import net.dankito.banking.fints.log.IMessageLogAppender
|
||||
import net.dankito.banking.fints.model.Amount
|
||||
import net.dankito.banking.fints.transactions.mt940.model.*
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import net.dankito.utils.multiplatform.DateFormatter
|
||||
import net.dankito.utils.multiplatform.Month
|
||||
import net.dankito.utils.multiplatform.extensions.isUpperCase
|
||||
import net.dankito.utils.multiplatform.log.LoggerFactory
|
||||
|
||||
|
@ -53,7 +54,7 @@ open class Mt940Parser(
|
|||
|
||||
val DateFormatter = DateFormatter("yyMMdd")
|
||||
|
||||
val CurrentYearTwoDigit = Date().year() - 2000
|
||||
val CurrentYearTwoDigit = LocalDate.todayAtEuropeBerlin().year - 2000
|
||||
|
||||
val CreditDebitCancellationRegex = Regex("C|D|RC|RD")
|
||||
|
||||
|
@ -449,7 +450,7 @@ open class Mt940Parser(
|
|||
}
|
||||
|
||||
|
||||
protected open fun parseMt940Date(dateString: String): Date {
|
||||
protected 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
|
||||
|
@ -464,25 +465,25 @@ open class Mt940Parser(
|
|||
year -= 100
|
||||
}
|
||||
|
||||
return Date(year + 2000, month, day) // java.util.Date years start at 1900 at month at 0 not at 1
|
||||
return LocalDate(year + 2000, month, day) // java.util.Date years start at 1900 at month at 0 not at 1
|
||||
} catch (e: Exception) {
|
||||
logError("Could not parse dateString '$dateString'", e)
|
||||
}
|
||||
}
|
||||
|
||||
return DateFormatter.parse(dateString)!! // fallback to not thread-safe SimpleDateFormat. Works in most cases but not all
|
||||
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: Date): Date {
|
||||
protected open fun parseMt940BookingDate(bookingDateString: String, valueDateString: String, valueDate: LocalDate): LocalDate {
|
||||
val bookingDate = parseMt940Date(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)
|
||||
val bookingDateMonth = bookingDate.month
|
||||
if (bookingDateMonth != valueDate.month && bookingDateMonth == Month.DECEMBER) {
|
||||
return parseMt940Date("" + (valueDate.year - 1 - 2000) + bookingDateString)
|
||||
}
|
||||
|
||||
return bookingDate
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package net.dankito.banking.fints.transactions.mt940.model
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
import net.dankito.utils.multiplatform.extensions.atUnixEpochStart
|
||||
import net.dankito.banking.fints.model.Amount
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
|
||||
|
||||
open class Balance(
|
||||
|
@ -23,7 +24,7 @@ open class Balance(
|
|||
*
|
||||
* Max length = 6
|
||||
*/
|
||||
val bookingDate: Date,
|
||||
val bookingDate: LocalDate,
|
||||
|
||||
/**
|
||||
* Währungsschlüssel gem. ISO 4217
|
||||
|
@ -41,7 +42,7 @@ open class Balance(
|
|||
|
||||
) {
|
||||
|
||||
internal constructor() : this(false, false, Date(0), "", Amount.Zero) // for object deserializers
|
||||
internal constructor() : this(false, false, LocalDate.atUnixEpochStart, "", Amount.Zero) // for object deserializers
|
||||
|
||||
|
||||
override fun toString(): String {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package net.dankito.banking.fints.transactions.mt940.model
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
import net.dankito.banking.fints.model.Amount
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
|
||||
|
||||
open class StatementLine(
|
||||
|
@ -25,14 +25,14 @@ open class StatementLine(
|
|||
*
|
||||
* Length = 6
|
||||
*/
|
||||
val valueDate: Date,
|
||||
val valueDate: LocalDate,
|
||||
|
||||
/**
|
||||
* MMTT
|
||||
*
|
||||
* Length = 4
|
||||
*/
|
||||
val bookingDate: Date?,
|
||||
val bookingDate: LocalDate?,
|
||||
|
||||
/**
|
||||
* dritte Stelle der Währungsbezeichnung, falls sie zur Unterscheidung notwendig ist
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
package net.dankito.banking.fints.util
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import net.dankito.utils.multiplatform.extensions.nowAtEuropeBerlin
|
||||
import net.dankito.utils.multiplatform.extensions.todayAtEuropeBerlin
|
||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Datum
|
||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Uhrzeit
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
|
||||
|
||||
open class FinTsUtils {
|
||||
|
||||
|
||||
open fun formatDateToday(): String {
|
||||
return formatDate(Date())
|
||||
return formatDate(LocalDate.todayAtEuropeBerlin())
|
||||
}
|
||||
|
||||
open fun formatDate(date: Date): String {
|
||||
open fun formatDate(date: LocalDate): String {
|
||||
return Datum.format(date)
|
||||
}
|
||||
|
||||
|
@ -20,16 +23,16 @@ open class FinTsUtils {
|
|||
return convertToInt(formatDateToday())
|
||||
}
|
||||
|
||||
open fun formatDateAsInt(date: Date): Int {
|
||||
open fun formatDateAsInt(date: LocalDate): Int {
|
||||
return convertToInt(formatDate(date))
|
||||
}
|
||||
|
||||
|
||||
open fun formatTimeNow(): String {
|
||||
return formatTime(Date())
|
||||
return formatTime(LocalDateTime.nowAtEuropeBerlin())
|
||||
}
|
||||
|
||||
open fun formatTime(time: Date): String {
|
||||
open fun formatTime(time: LocalDateTime): String {
|
||||
return Uhrzeit.format(time)
|
||||
}
|
||||
|
||||
|
@ -37,7 +40,7 @@ open class FinTsUtils {
|
|||
return convertToInt(formatTimeNow())
|
||||
}
|
||||
|
||||
open fun formatTimeAsInt(time: Date): Int {
|
||||
open fun formatTimeAsInt(time: LocalDateTime): Int {
|
||||
return convertToInt(formatTime(time))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package net.dankito.banking.fints
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
import net.dankito.banking.fints.callback.SimpleFinTsClientCallback
|
||||
import net.dankito.utils.multiplatform.extensions.randomWithSeed
|
||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Datum
|
||||
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen
|
||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.Dialogsprache
|
||||
|
@ -9,8 +11,6 @@ import net.dankito.banking.fints.model.*
|
|||
import net.dankito.banking.fints.response.segments.AccountType
|
||||
import net.dankito.banking.fints.response.segments.ChangeTanMediaParameters
|
||||
import net.dankito.banking.fints.response.segments.JobParameters
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
abstract class FinTsTestBase {
|
||||
|
@ -70,10 +70,10 @@ abstract class FinTsTestBase {
|
|||
}
|
||||
|
||||
protected open fun createDialogId(): String {
|
||||
return Random(net.dankito.utils.multiplatform.Date.nanoSecondsSinceEpoch).nextInt(1000000, 9999999).toString()
|
||||
return randomWithSeed().nextInt(1000000, 9999999).toString()
|
||||
}
|
||||
|
||||
protected open fun convertDate(date: Date): String {
|
||||
protected open fun convertDate(date: LocalDate): String {
|
||||
return Datum.format(date)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@ package net.dankito.banking.fints.messages
|
|||
import ch.tutteli.atrium.api.fluent.en_GB.notToBeNull
|
||||
import ch.tutteli.atrium.api.fluent.en_GB.toBe
|
||||
import ch.tutteli.atrium.api.verbs.expect
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import kotlinx.datetime.Month
|
||||
import net.dankito.banking.fints.FinTsTestBase
|
||||
import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.JobTanConfiguration
|
||||
import net.dankito.banking.fints.messages.segmente.id.CustomerSegmentId
|
||||
|
@ -12,8 +15,6 @@ import net.dankito.banking.fints.response.segments.JobParameters
|
|||
import net.dankito.banking.fints.response.segments.PinInfo
|
||||
import net.dankito.banking.fints.response.segments.RetrieveAccountTransactionsParameters
|
||||
import net.dankito.banking.fints.util.FinTsUtils
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import net.dankito.utils.multiplatform.Month
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.Ignore
|
||||
import kotlin.test.Test
|
||||
|
@ -22,11 +23,11 @@ import kotlin.test.Test
|
|||
class MessageBuilderTest : FinTsTestBase() {
|
||||
|
||||
private val underTest = object : MessageBuilder(utils = object : FinTsUtils() {
|
||||
override fun formatDate(date: Date): String {
|
||||
override fun formatDate(date: LocalDate): String {
|
||||
return Date.toString()
|
||||
}
|
||||
|
||||
override fun formatTime(time: Date): String {
|
||||
override fun formatTime(time: LocalDateTime): String {
|
||||
return Time.toString()
|
||||
}
|
||||
}) {
|
||||
|
@ -171,8 +172,8 @@ class MessageBuilderTest : FinTsTestBase() {
|
|||
|
||||
val context = createContext()
|
||||
|
||||
val fromDate = Date(2019, Month.August, 6)
|
||||
val toDate = Date(2019, Month.October, 21)
|
||||
val fromDate = LocalDate(2019, Month.AUGUST, 6)
|
||||
val toDate = LocalDate(2019, Month.OCTOBER, 21)
|
||||
val maxCountEntries = 99
|
||||
|
||||
// when
|
||||
|
@ -205,8 +206,8 @@ class MessageBuilderTest : FinTsTestBase() {
|
|||
|
||||
val context = createContext()
|
||||
|
||||
val fromDate = Date(2019, Month.August, 6)
|
||||
val toDate = Date(2019, Month.October, 21)
|
||||
val fromDate = LocalDate(2019, Month.AUGUST, 6)
|
||||
val toDate = LocalDate(2019, Month.OCTOBER, 21)
|
||||
val maxCountEntries = 99
|
||||
val continuationId = "9345-10-26-11.52.15.693455"
|
||||
|
||||
|
|
|
@ -13,10 +13,10 @@ import net.dankito.banking.fints.messages.segmente.id.ISegmentId
|
|||
import net.dankito.banking.fints.messages.segmente.id.MessageSegmentId
|
||||
import net.dankito.banking.fints.response.segments.*
|
||||
import ch.tutteli.atrium.api.verbs.expect
|
||||
import kotlinx.datetime.LocalDate
|
||||
import net.dankito.banking.fints.extensions.isFalse
|
||||
import net.dankito.banking.fints.extensions.isTrue
|
||||
import net.dankito.banking.fints.model.Amount
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import kotlin.test.Ignore
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.fail
|
||||
|
@ -1094,7 +1094,7 @@ class ResponseParserTest : FinTsTestBase() {
|
|||
|
||||
// given
|
||||
val balance = "1234,56"
|
||||
val date = Date(1988, 3, 27)
|
||||
val date = LocalDate(1988, 3, 27)
|
||||
val bankCode = "12345678"
|
||||
val accountId = "0987654321"
|
||||
val accountProductName = "Sichteinlagen"
|
||||
|
@ -1121,7 +1121,7 @@ class ResponseParserTest : FinTsTestBase() {
|
|||
|
||||
// given
|
||||
val balance = Amount.Zero
|
||||
val date = Date(2020, 6, 11)
|
||||
val date = LocalDate(2020, 6, 11)
|
||||
val bankCode = "12345678"
|
||||
val accountId = "0987654321"
|
||||
val accountProductName = "Girokonto"
|
||||
|
@ -1148,7 +1148,7 @@ class ResponseParserTest : FinTsTestBase() {
|
|||
|
||||
// given
|
||||
val balance = Amount.Zero
|
||||
val date = Date(2020, 6, 11)
|
||||
val date = LocalDate(2020, 6, 11)
|
||||
val bankCode = "12345678"
|
||||
val accountId = "0987654321"
|
||||
val accountProductName = "Girokonto"
|
||||
|
@ -1231,14 +1231,14 @@ class ResponseParserTest : FinTsTestBase() {
|
|||
|
||||
result.getFirstSegmentById<ReceivedCreditCardTransactionsAndBalance>(InstituteSegmentId.CreditCardTransactions)?.let { segment ->
|
||||
expect(segment.balance.amount.string).toBe(balance)
|
||||
expect(segment.balance.date).toBe(Date(2020, 9, 23))
|
||||
expect(segment.balance.date).toBe(LocalDate(2020, 9, 23))
|
||||
expect(segment.balance.time).notToBeNull()
|
||||
expect(segment.transactions.size).toBe(2)
|
||||
|
||||
segment.transactions.forEach { transaction ->
|
||||
expect(transaction.transactionDescriptionBase).toBe(otherPartyName)
|
||||
expect(transaction.bookingDate).toBe(Date(2020, 8, 19))
|
||||
expect(transaction.valueDate).toBe(Date(2020, 8, 20))
|
||||
expect(transaction.bookingDate).toBe(LocalDate(2020, 8, 19))
|
||||
expect(transaction.valueDate).toBe(LocalDate(2020, 8, 20))
|
||||
expect(transaction.amount.amount.string).toBe("-" + amount)
|
||||
expect(transaction.amount.currency.code).toBe("EUR")
|
||||
expect(transaction.isCleared).isTrue()
|
||||
|
@ -1264,7 +1264,7 @@ class ResponseParserTest : FinTsTestBase() {
|
|||
|
||||
result.getFirstSegmentById<ReceivedCreditCardTransactionsAndBalance>(InstituteSegmentId.CreditCardTransactions)?.let { segment ->
|
||||
expect(segment.balance.amount.string).toBe(balance)
|
||||
expect(segment.balance.date).toBe(Date(2020, 9, 23))
|
||||
expect(segment.balance.date).toBe(LocalDate(2020, 9, 23))
|
||||
expect(segment.balance.time).notToBeNull()
|
||||
|
||||
expect(segment.transactions).isEmpty()
|
||||
|
|
|
@ -9,12 +9,13 @@ import net.dankito.banking.fints.transactions.mt940.model.Balance
|
|||
import net.dankito.banking.fints.transactions.mt940.model.InformationToAccountOwner
|
||||
import net.dankito.banking.fints.transactions.mt940.model.StatementLine
|
||||
import ch.tutteli.atrium.api.verbs.expect
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlin.test.Test
|
||||
import net.dankito.banking.fints.extensions.isFalse
|
||||
import net.dankito.banking.fints.extensions.isTrue
|
||||
import net.dankito.banking.fints.model.Amount
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import net.dankito.utils.multiplatform.DateFormatter
|
||||
import net.dankito.utils.multiplatform.extensions.format
|
||||
|
||||
|
||||
class Mt940ParserTest : FinTsTestBase() {
|
||||
|
@ -23,8 +24,8 @@ class Mt940ParserTest : FinTsTestBase() {
|
|||
|
||||
const val Currency = "EUR"
|
||||
|
||||
val AccountStatement1PreviousStatementBookingDate = Date(1988, 2, 26)
|
||||
val AccountStatement1BookingDate = Date(1988, 2, 27)
|
||||
val AccountStatement1PreviousStatementBookingDate = LocalDate(1988, 2, 26)
|
||||
val AccountStatement1BookingDate = LocalDate(1988, 2, 27)
|
||||
|
||||
val AccountStatement1OpeningBalanceAmount = Amount("12345,67")
|
||||
|
||||
|
@ -79,7 +80,7 @@ class Mt940ParserTest : FinTsTestBase() {
|
|||
// given
|
||||
val amount = Amount("15,00")
|
||||
val isCredit = false
|
||||
val bookingDate = Date(2020, 5, 11)
|
||||
val bookingDate = LocalDate(2020, 5, 11)
|
||||
|
||||
// when
|
||||
val result = underTest.parseMt940String(AccountStatementWithSingleTransaction_SheetNumberOmitted)
|
||||
|
@ -160,10 +161,10 @@ class Mt940ParserTest : FinTsTestBase() {
|
|||
expect(result).hasSize(1)
|
||||
expect(result.first().transactions).hasSize(2)
|
||||
|
||||
expect(result.first().transactions[0].statementLine.bookingDate).toBe(Date(2019, 12, 30))
|
||||
expect(result.first().transactions[0].statementLine.valueDate).toBe(Date(2020, 1, 1))
|
||||
expect(result.first().transactions[1].statementLine.bookingDate).toBe(Date(2019, 12, 30))
|
||||
expect(result.first().transactions[1].statementLine.valueDate).toBe(Date(2020, 1, 1))
|
||||
expect(result.first().transactions[0].statementLine.bookingDate).toBe(LocalDate(2019, 12, 30))
|
||||
expect(result.first().transactions[0].statementLine.valueDate).toBe(LocalDate(2020, 1, 1))
|
||||
expect(result.first().transactions[1].statementLine.bookingDate).toBe(LocalDate(2019, 12, 30))
|
||||
expect(result.first().transactions[1].statementLine.valueDate).toBe(LocalDate(2020, 1, 1))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -313,15 +314,15 @@ class Mt940ParserTest : FinTsTestBase() {
|
|||
}
|
||||
|
||||
|
||||
private fun assertBalance(balance: Balance, isCredit: Boolean, bookingDate: Date, amount: Amount) {
|
||||
private fun assertBalance(balance: Balance, isCredit: Boolean, bookingDate: LocalDate, amount: Amount) {
|
||||
expect(balance.isCredit).toBe(isCredit)
|
||||
expect(balance.bookingDate).toBe(bookingDate)
|
||||
expect(balance.amount).toBe(amount)
|
||||
expect(balance.currency).toBe(Currency)
|
||||
}
|
||||
|
||||
private fun assertTurnover(statementLine: StatementLine, valueDate: Date, amount: Amount, isCredit: Boolean = true,
|
||||
bookingDate: Date? = valueDate) {
|
||||
private fun assertTurnover(statementLine: StatementLine, valueDate: LocalDate, amount: Amount, isCredit: Boolean = true,
|
||||
bookingDate: LocalDate? = valueDate) {
|
||||
|
||||
expect(statementLine.isCredit).toBe(isCredit)
|
||||
expect(statementLine.isReversal).isFalse()
|
||||
|
@ -385,12 +386,12 @@ class Mt940ParserTest : FinTsTestBase() {
|
|||
""".trimIndent()
|
||||
|
||||
|
||||
private fun convertMt940Date(date: Date): String {
|
||||
return Mt940DateFormatter.format(date)
|
||||
private fun convertMt940Date(date: LocalDate): String {
|
||||
return date.format(Mt940DateFormatter)
|
||||
}
|
||||
|
||||
private fun convertToShortBookingDate(date: Date): String {
|
||||
return BookingDateFormatter.format(date)
|
||||
private fun convertToShortBookingDate(date: LocalDate): String {
|
||||
return date.format(BookingDateFormatter)
|
||||
}
|
||||
|
||||
}
|
|
@ -4,7 +4,7 @@ import ch.tutteli.atrium.api.fluent.en_GB.ExperimentalWithOptions
|
|||
import ch.tutteli.atrium.api.fluent.en_GB.toBe
|
||||
import ch.tutteli.atrium.api.fluent.en_GB.withRepresentation
|
||||
import ch.tutteli.atrium.api.verbs.expect
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import net.dankito.utils.multiplatform.extensions.randomWithSeed
|
||||
import kotlin.random.Random
|
||||
import kotlin.test.Test
|
||||
|
||||
|
@ -60,7 +60,7 @@ class Base64Test {
|
|||
@ExperimentalWithOptions
|
||||
fun testRandomStrings() {
|
||||
val steps = 1000000
|
||||
val random = Random(Date().millisSinceEpoch)
|
||||
val random = randomWithSeed()
|
||||
|
||||
for (count in 0 until steps) {
|
||||
// given
|
||||
|
|
|
@ -2,7 +2,9 @@ package net.dankito.banking.fints.util
|
|||
|
||||
import ch.tutteli.atrium.api.fluent.en_GB.toBe
|
||||
import ch.tutteli.atrium.api.verbs.expect
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import net.dankito.utils.multiplatform.extensions.of
|
||||
import kotlin.test.Test
|
||||
|
||||
class FinTsUtilsTest {
|
||||
|
@ -14,7 +16,7 @@ class FinTsUtilsTest {
|
|||
fun formatDate() {
|
||||
|
||||
// given
|
||||
val date = Date(1988, 3, 27)
|
||||
val date = LocalDate(1988, 3, 27)
|
||||
|
||||
// when
|
||||
val result = underTest.formatDate(date)
|
||||
|
@ -27,7 +29,7 @@ class FinTsUtilsTest {
|
|||
fun formatDateAsInt() {
|
||||
|
||||
// given
|
||||
val date = Date(1988, 3, 27)
|
||||
val date = LocalDate(1988, 3, 27)
|
||||
|
||||
// when
|
||||
val result = underTest.formatDateAsInt(date)
|
||||
|
@ -41,7 +43,7 @@ class FinTsUtilsTest {
|
|||
fun formatTime_AM() {
|
||||
|
||||
// given
|
||||
val time = Date(0, 0, 0, 8, 2, 1)
|
||||
val time = LocalDateTime.of(8, 2, 1)
|
||||
|
||||
// when
|
||||
val result = underTest.formatTime(time)
|
||||
|
@ -54,7 +56,7 @@ class FinTsUtilsTest {
|
|||
fun formatTime_PM() {
|
||||
|
||||
// given
|
||||
val time = Date(0, 0, 0, 18, 22, 51)
|
||||
val time = LocalDateTime.of(18, 22, 51)
|
||||
|
||||
// when
|
||||
val result = underTest.formatTime(time)
|
||||
|
@ -67,7 +69,7 @@ class FinTsUtilsTest {
|
|||
fun formatTimeAsInt_AM() {
|
||||
|
||||
// given
|
||||
val time = Date(0, 0, 0, 8, 2, 1)
|
||||
val time = LocalDateTime.of(8, 2, 1)
|
||||
|
||||
// when
|
||||
val result = underTest.formatTimeAsInt(time)
|
||||
|
@ -80,7 +82,7 @@ class FinTsUtilsTest {
|
|||
fun formatTimeAsInt_PM() {
|
||||
|
||||
// given
|
||||
val time = Date(0, 0, 0, 18, 22, 51)
|
||||
val time = LocalDateTime.of(18, 22, 51)
|
||||
|
||||
// when
|
||||
val result = underTest.formatTimeAsInt(time)
|
||||
|
|
|
@ -46,7 +46,7 @@ kotlin {
|
|||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-datetime:0.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,6 +87,21 @@ kotlin {
|
|||
}
|
||||
|
||||
|
||||
// jsMain {
|
||||
// dependencies {
|
||||
// implementation npm("@js-joda/timezone", "2.3.0")
|
||||
// implementation "io.ktor:ktor-client-js:$ktorVersion"
|
||||
// }
|
||||
// }
|
||||
/* Plus:
|
||||
@JsModule("@js-joda/timezone")
|
||||
@JsNonModule
|
||||
external object JsJodaTimeZoneModule
|
||||
|
||||
private val jsJodaTz = JsJodaTimeZoneModule
|
||||
*/
|
||||
|
||||
|
||||
iosMain {
|
||||
dependencies {
|
||||
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
package net.dankito.utils.multiplatform
|
||||
|
||||
|
||||
|
||||
fun Date.isBefore(other: Date): Boolean {
|
||||
return compareTo(other) < 0
|
||||
}
|
||||
|
||||
fun Date.isBeforeOrEquals(other: Date): Boolean {
|
||||
return compareTo(other) <= 0
|
||||
}
|
||||
|
||||
|
||||
fun Date.format(format: DateFormatter): String {
|
||||
return format.format(this)
|
||||
}
|
||||
|
||||
fun Date.format(pattern: String): String {
|
||||
return this.format(DateFormatter(pattern))
|
||||
}
|
||||
|
||||
|
||||
expect class Date(millisSinceEpoch: Long) {
|
||||
|
||||
companion object {
|
||||
|
||||
val today: Date
|
||||
|
||||
val nanoSecondsSinceEpoch: Long
|
||||
|
||||
}
|
||||
|
||||
|
||||
constructor()
|
||||
|
||||
constructor(year: Int, month: Int, day: Int, hour: Int = 0, minute: Int = 0, second: Int = 0)
|
||||
|
||||
constructor(year: Int, month: Month, day: Int, hour: Int = 0, minute: Int = 0, second: Int = 0)
|
||||
|
||||
|
||||
val millisSinceEpoch: Long
|
||||
|
||||
fun year(): Int
|
||||
|
||||
fun month(): Month
|
||||
|
||||
fun monthInt(): Int
|
||||
|
||||
fun day(): Int
|
||||
|
||||
fun addDays(days: Int): Date
|
||||
|
||||
|
||||
fun compareTo(other: Date): Int
|
||||
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
package net.dankito.utils.multiplatform
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
|
||||
/**
|
||||
* Be aware that Java DateFormat is not thread safe!
|
||||
*/
|
||||
|
@ -10,8 +13,10 @@ expect class DateFormatter constructor(pattern: String) {
|
|||
constructor(dateStyle: DateFormatStyle, timeStyle: DateFormatStyle)
|
||||
|
||||
|
||||
fun format(date: Date): String
|
||||
fun format(date: LocalDateTime): String
|
||||
|
||||
fun parse(dateString: String): Date?
|
||||
fun parseDate(dateString: String): LocalDate?
|
||||
|
||||
fun parse(dateString: String): LocalDateTime?
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package net.dankito.utils.multiplatform.extensions
|
||||
|
||||
import kotlinx.datetime.*
|
||||
import net.dankito.utils.multiplatform.DateFormatter
|
||||
|
||||
|
||||
val LocalDate.Companion.atUnixEpochStart: LocalDate
|
||||
get() = fromEpochMillisecondsAtUtc(0)
|
||||
|
||||
fun LocalDate.Companion.fromEpochMillisecondsAtUtc(epochMilliseconds: Long): LocalDate {
|
||||
return fromEpochMilliseconds(epochMilliseconds, TimeZone.UTC)
|
||||
}
|
||||
|
||||
fun LocalDate.Companion.fromEpochMillisecondsAtSystemDefaultTimeZone(epochMilliseconds: Long): LocalDate {
|
||||
return fromEpochMilliseconds(epochMilliseconds, TimeZone.currentSystemDefault())
|
||||
}
|
||||
|
||||
fun LocalDate.Companion.fromEpochMillisecondsAtEuropeBerlin(epochMilliseconds: Long): LocalDate {
|
||||
return fromEpochMilliseconds(epochMilliseconds, TimeZone.europeBerlin)
|
||||
}
|
||||
|
||||
fun LocalDate.Companion.fromEpochMilliseconds(epochMilliseconds: Long, timeZone: TimeZone): LocalDate {
|
||||
return Instant.fromEpochMilliseconds(epochMilliseconds).toLocalDateTime(timeZone).date
|
||||
}
|
||||
|
||||
|
||||
fun LocalDate.Companion.todayAtUtc(): LocalDate {
|
||||
return nowAt(TimeZone.UTC)
|
||||
}
|
||||
|
||||
fun LocalDate.Companion.todayAtSystemDefaultTimeZone(): LocalDate {
|
||||
return nowAt(TimeZone.currentSystemDefault())
|
||||
}
|
||||
|
||||
fun LocalDate.Companion.todayAtEuropeBerlin(): LocalDate {
|
||||
return nowAt(TimeZone.europeBerlin)
|
||||
}
|
||||
|
||||
fun LocalDate.Companion.nowAt(timeZone: String): LocalDate {
|
||||
return nowAt(TimeZone.of(timeZone))
|
||||
}
|
||||
|
||||
fun LocalDate.Companion.nowAt(timeZone: TimeZone): LocalDate {
|
||||
return Clock.System.todayAt(timeZone)
|
||||
}
|
||||
|
||||
|
||||
val LocalDate.millisSinceEpochAtUtc: Long
|
||||
get() = this.toEpochMillisecondsAt(TimeZone.UTC)
|
||||
|
||||
val LocalDate.millisSinceEpochAtSystemDefaultTimeZone: Long
|
||||
get() = this.toEpochMillisecondsAt(TimeZone.currentSystemDefault())
|
||||
|
||||
val LocalDate.millisSinceEpochAtEuropeBerlin: Long
|
||||
get() = this.toEpochMillisecondsAt(TimeZone.europeBerlin)
|
||||
|
||||
fun LocalDate.toEpochMillisecondsAt(timeZone: TimeZone): Long {
|
||||
return this.atTime(0, 0).toInstant(timeZone).toEpochMilliseconds()
|
||||
}
|
||||
|
||||
|
||||
fun LocalDate.addDays(days: Int): LocalDate {
|
||||
return this.plus(days, DateTimeUnit.DAY)
|
||||
}
|
||||
|
||||
fun LocalDate.minusDays(days: Int): LocalDate {
|
||||
return this.minus(days, DateTimeUnit.DAY)
|
||||
}
|
||||
|
||||
|
||||
fun LocalDate.format(formatter: DateFormatter): String {
|
||||
return this.atTime(0, 0).format(formatter)
|
||||
}
|
||||
|
||||
fun LocalDate.format(pattern: String): String {
|
||||
return this.format(DateFormatter(pattern))
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package net.dankito.utils.multiplatform.extensions
|
||||
|
||||
import kotlinx.datetime.*
|
||||
import net.dankito.utils.multiplatform.DateFormatter
|
||||
|
||||
|
||||
fun LocalDateTime.Companion.of(hour: Int, minute: Int, second: Int = 0, nanosecond: Int = 0): LocalDateTime {
|
||||
return LocalDateTime(0, 1, 1, hour, minute, second, nanosecond) // minimum values for month and day are 1
|
||||
}
|
||||
|
||||
fun LocalDateTime.Companion.fromEpochMillisecondsAtUtc(epochMilliseconds: Long): LocalDateTime {
|
||||
return fromEpochMilliseconds(epochMilliseconds, TimeZone.UTC)
|
||||
}
|
||||
|
||||
fun LocalDateTime.Companion.fromEpochMillisecondsAtSystemDefaultTimeZone(epochMilliseconds: Long): LocalDateTime {
|
||||
return fromEpochMilliseconds(epochMilliseconds, TimeZone.currentSystemDefault())
|
||||
}
|
||||
|
||||
fun LocalDateTime.Companion.fromEpochMillisecondsAtEuropeBerlin(epochMilliseconds: Long): LocalDateTime {
|
||||
return fromEpochMilliseconds(epochMilliseconds, TimeZone.europeBerlin)
|
||||
}
|
||||
|
||||
fun LocalDateTime.Companion.fromEpochMilliseconds(epochMilliseconds: Long, timeZone: TimeZone): LocalDateTime {
|
||||
return Instant.fromEpochMilliseconds(epochMilliseconds).toLocalDateTime(timeZone)
|
||||
}
|
||||
|
||||
|
||||
fun LocalDateTime.Companion.nowAtUtc(): LocalDateTime {
|
||||
return nowAt(TimeZone.UTC)
|
||||
}
|
||||
|
||||
fun LocalDateTime.Companion.nowAtSystemDefaultTimeZone(): LocalDateTime {
|
||||
return nowAt(TimeZone.currentSystemDefault())
|
||||
}
|
||||
|
||||
fun LocalDateTime.Companion.nowAtEuropeBerlin(): LocalDateTime {
|
||||
return nowAt(TimeZone.europeBerlin)
|
||||
}
|
||||
|
||||
fun LocalDateTime.Companion.nowAt(timeZone: String): LocalDateTime {
|
||||
return nowAt(TimeZone.of(timeZone))
|
||||
}
|
||||
|
||||
fun LocalDateTime.Companion.nowAt(timeZone: TimeZone): LocalDateTime {
|
||||
return Clock.System.now().toLocalDateTime(timeZone)
|
||||
}
|
||||
|
||||
|
||||
val LocalDateTime.millisSinceEpochAtUtc: Long
|
||||
get() = this.toEpochMillisecondsAt(TimeZone.UTC)
|
||||
|
||||
val LocalDateTime.millisSinceEpochAtSystemDefaultTimeZone: Long
|
||||
get() = this.toEpochMillisecondsAt(TimeZone.currentSystemDefault())
|
||||
|
||||
val LocalDateTime.millisSinceEpochAtEuropeBerlin: Long
|
||||
get() = this.toEpochMillisecondsAt(TimeZone.europeBerlin)
|
||||
|
||||
fun LocalDateTime.toEpochMillisecondsAt(timeZone: TimeZone): Long {
|
||||
return this.toInstant(timeZone).toEpochMilliseconds()
|
||||
}
|
||||
|
||||
|
||||
fun LocalDateTime.format(formatter: DateFormatter): String {
|
||||
return formatter.format(this)
|
||||
}
|
||||
|
||||
fun LocalDateTime.format(pattern: String): String {
|
||||
return this.format(DateFormatter(pattern))
|
||||
}
|
|
@ -3,6 +3,14 @@ package net.dankito.utils.multiplatform.extensions
|
|||
import net.dankito.utils.multiplatform.StringHelper
|
||||
|
||||
|
||||
fun Int.toStringWithTwoDigits(): String {
|
||||
return toStringWithMinDigits(2)
|
||||
}
|
||||
|
||||
fun Int.toStringWithMinDigits(countDigits: Int): String {
|
||||
return format("%0${countDigits}d")
|
||||
}
|
||||
|
||||
fun Int.format(format: String): String {
|
||||
return StringHelper.format(format, this)
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package net.dankito.utils.multiplatform.extensions
|
||||
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
fun randomWithSeed(): Random = Random(randomSeed())
|
||||
|
||||
fun randomSeed(): Long {
|
||||
return Clock.System.now().nanosecondsOfSecond.toLong() + Clock.System.now().toEpochMilliseconds()
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package net.dankito.utils.multiplatform.extensions
|
||||
|
||||
import kotlinx.datetime.TimeZone
|
||||
|
||||
|
||||
val TimeZone.Companion.europeBerlin: TimeZone
|
||||
get() = TimeZone.of("Europe/Berlin")
|
|
@ -1,8 +1,9 @@
|
|||
package net.dankito.utils.multiplatform.log
|
||||
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import net.dankito.utils.multiplatform.DateFormatter
|
||||
import net.dankito.utils.multiplatform.Thread
|
||||
import net.dankito.utils.multiplatform.extensions.nowAtSystemDefaultTimeZone
|
||||
|
||||
|
||||
abstract class LoggerBase(
|
||||
|
@ -99,7 +100,7 @@ abstract class LoggerBase(
|
|||
|
||||
|
||||
protected open fun createLogOutput(level: LogLevel, message: String): String {
|
||||
return "${DateFormatter.format(Date())} [$level] ${Thread.current.threadName} $name - $message"
|
||||
return "${DateFormatter.format(LocalDateTime.nowAtSystemDefaultTimeZone())} [$level] ${Thread.current.threadName} $name - $message"
|
||||
}
|
||||
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
package net.dankito.utils.multiplatform
|
||||
|
||||
import platform.CoreFoundation.*
|
||||
import platform.Foundation.*
|
||||
|
||||
|
||||
fun NSTimeInterval.toMillis(): Long {
|
||||
return this.toLong() * 1000
|
||||
}
|
||||
|
||||
fun NSDate?.toDate(): Date? {
|
||||
return this?.toDate()
|
||||
}
|
||||
|
||||
fun NSDate.toDate(): Date {
|
||||
return Date(this)
|
||||
}
|
||||
|
||||
|
||||
actual class Date(val date: NSDate) { // cannot subclass NSDate as it's a class cluster
|
||||
|
||||
actual companion object {
|
||||
|
||||
val DiffBetweenEpochTimeAndReferenceDate = (NSDate.timeIntervalSinceReferenceDate - NSTimeIntervalSince1970).toMillis()
|
||||
|
||||
fun from(year: Int, month: Int, day: Int, hour: Int = 0, minute: Int = 0, second: Int = 0): NSDate {
|
||||
val dateComponents = NSDateComponents()
|
||||
|
||||
dateComponents.year = year.toLong()
|
||||
dateComponents.month = month.toLong()
|
||||
dateComponents.day = day.toLong()
|
||||
|
||||
dateComponents.hour = hour.toLong()
|
||||
dateComponents.minute = minute.toLong()
|
||||
dateComponents.second = second.toLong()
|
||||
|
||||
val calendar = NSCalendar.currentCalendar
|
||||
val todayInUtc = calendar.dateFromComponents(dateComponents) !!
|
||||
|
||||
return calendar.dateByAddingUnit(NSCalendarUnitSecond, NSTimeZone.defaultTimeZone.secondsFromGMT, todayInUtc, 0)!!
|
||||
}
|
||||
|
||||
|
||||
actual val today: Date
|
||||
get() {
|
||||
val now = Date()
|
||||
|
||||
return Date(from(now.year(), now.monthInt(), now.day()))
|
||||
}
|
||||
|
||||
actual val nanoSecondsSinceEpoch: Long
|
||||
get() = CFAbsoluteTimeGetCurrent().toLong() * 1000
|
||||
|
||||
}
|
||||
|
||||
|
||||
actual constructor(millisSinceEpoch: Long) : this(NSDate(timeIntervalSinceReferenceDate = ((millisSinceEpoch / 1000).toDouble() - NSTimeIntervalSince1970)))
|
||||
|
||||
actual constructor() : this(NSDate())
|
||||
|
||||
actual constructor(year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int) : this(from(year, month, day, hour, minute, second))
|
||||
|
||||
actual constructor(year: Int, month: Month, day: Int, hour: Int, minute: Int, second: Int) : this(year, month.month, day, hour, minute, second)
|
||||
|
||||
|
||||
actual val millisSinceEpoch: Long
|
||||
get() = date.timeIntervalSince1970.toMillis()
|
||||
|
||||
|
||||
actual fun year(): Int {
|
||||
val components = NSCalendar.currentCalendar.components(NSCalendarUnitYear, date)
|
||||
return components.year.toInt()
|
||||
}
|
||||
|
||||
actual fun month(): Month {
|
||||
return Month.fromInt(monthInt())
|
||||
}
|
||||
|
||||
actual fun monthInt(): Int {
|
||||
val components = NSCalendar.currentCalendar.components(NSCalendarUnitMonth, date)
|
||||
return components.month.toInt()
|
||||
}
|
||||
|
||||
actual fun day(): Int {
|
||||
val components = NSCalendar.currentCalendar.components(NSCalendarUnitDay, date)
|
||||
return components.day.toInt()
|
||||
}
|
||||
|
||||
actual fun addDays(days: Int): Date {
|
||||
val calendar = NSCalendar.currentCalendar
|
||||
|
||||
return calendar.dateByAddingUnit(NSCalendarUnitDay, days, this.date, 0)!!
|
||||
}
|
||||
|
||||
|
||||
|
||||
actual fun compareTo(other: Date): Int {
|
||||
return date.compare(other.date).toCompareToResult()
|
||||
}
|
||||
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Date) return false
|
||||
|
||||
if (date != other.date) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return date.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return date.description ?: "Date(date=$date)"
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package net.dankito.utils.multiplatform
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import platform.Foundation.*
|
||||
|
||||
|
||||
|
@ -33,13 +35,21 @@ actual class DateFormatter actual constructor(val pattern: String): NSDateFormat
|
|||
}
|
||||
|
||||
|
||||
actual fun format(date: Date): String {
|
||||
return stringFromDate(date.date)
|
||||
actual fun format(date: LocalDateTime): String {
|
||||
return stringFromDate(date) // TODO: convert to NSDate when back on Mac
|
||||
}
|
||||
|
||||
actual fun parse(dateString: String): Date? {
|
||||
actual fun parseDate(dateString: String): LocalDate? {
|
||||
super.dateFromString(dateString)?.let { nsDate ->
|
||||
return Date(nsDate)
|
||||
return LocalDate(nsDate) // TODO: convert to NSDate when back on Mac
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
actual fun parse(dateString: String): LocalDateTime? {
|
||||
super.dateFromString(dateString)?.let { nsDate ->
|
||||
return LocalDateTime(nsDate) // TODO: convert to NSDate when back on Mac
|
||||
}
|
||||
|
||||
return null
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
package net.dankito.utils.multiplatform
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||
import net.dankito.utils.multiplatform.serialization.DateDeserializer
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
|
||||
|
||||
@JsonDeserialize(using = DateDeserializer::class)
|
||||
actual class Date actual constructor(millisSinceEpoch: Long) : java.util.Date(millisSinceEpoch) {
|
||||
|
||||
actual companion object {
|
||||
|
||||
actual val today: Date
|
||||
get() {
|
||||
val today = Calendar.getInstance()
|
||||
|
||||
today.set(Calendar.HOUR_OF_DAY, 0)
|
||||
today.set(Calendar.MINUTE, 0)
|
||||
today.set(Calendar.SECOND, 0)
|
||||
today.set(Calendar.MILLISECOND, 0)
|
||||
|
||||
return Date(today.time.time)
|
||||
}
|
||||
|
||||
actual val nanoSecondsSinceEpoch: Long
|
||||
get() = System.nanoTime()
|
||||
|
||||
}
|
||||
|
||||
|
||||
actual constructor() : this(System.currentTimeMillis())
|
||||
|
||||
actual constructor(year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int) : this(java.util.Date(year - 1900, month - 1, day, hour, minute, second).time)
|
||||
|
||||
actual constructor(year: Int, month: Month, day: Int, hour: Int, minute: Int, second: Int) : this(year, month.month, day, hour, minute, second)
|
||||
|
||||
|
||||
actual val millisSinceEpoch: Long
|
||||
get() = time
|
||||
|
||||
|
||||
actual fun year(): Int {
|
||||
return super.getYear() + 1900
|
||||
}
|
||||
|
||||
actual fun month(): Month {
|
||||
return Month.fromInt(monthInt())
|
||||
}
|
||||
|
||||
actual fun monthInt(): Int {
|
||||
return super.getMonth() + 1
|
||||
}
|
||||
|
||||
actual fun day(): Int {
|
||||
return super.getDate()
|
||||
}
|
||||
|
||||
actual fun addDays(days: Int): Date {
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.time = this
|
||||
calendar.add(Calendar.DATE, days)
|
||||
|
||||
return Date(calendar.time.time)
|
||||
}
|
||||
|
||||
|
||||
private fun formatDate(dateFormatStyle: Int): Int {
|
||||
val dateStringString = DateFormat.getDateInstance(dateFormatStyle).format(this)
|
||||
|
||||
return dateStringString.toInt()
|
||||
}
|
||||
|
||||
|
||||
actual fun compareTo(other: Date): Int {
|
||||
return super.compareTo(other)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
package net.dankito.utils.multiplatform
|
||||
|
||||
import kotlinx.datetime.*
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
|
||||
fun DateFormatStyle.convert(): Int {
|
||||
|
@ -14,7 +16,10 @@ fun DateFormatStyle.convert(): Int {
|
|||
}
|
||||
|
||||
|
||||
actual class DateFormatter actual constructor(val pattern: String): SimpleDateFormat(pattern) {
|
||||
actual class DateFormatter actual constructor(pattern: String) {
|
||||
|
||||
private val formatter = DateTimeFormatter.ofPattern(pattern)
|
||||
|
||||
|
||||
actual constructor(dateStyle: DateFormatStyle)
|
||||
: this((DateFormat.getDateInstance(dateStyle.convert()) as? SimpleDateFormat)?.toPattern() ?: "")
|
||||
|
@ -23,16 +28,16 @@ actual class DateFormatter actual constructor(val pattern: String): SimpleDateFo
|
|||
: this((DateFormat.getDateTimeInstance(dateStyle.convert(), timeStyle.convert()) as? SimpleDateFormat)?.toPattern() ?: "")
|
||||
|
||||
|
||||
actual fun format(date: Date): String {
|
||||
return super.format(date)
|
||||
actual fun format(date: LocalDateTime): String {
|
||||
return formatter.format(date.toJavaLocalDateTime())
|
||||
}
|
||||
|
||||
actual override fun parse(dateString: String): Date? {
|
||||
super.parse(dateString)?.let { javaDate ->
|
||||
return Date(javaDate.time)
|
||||
}
|
||||
actual fun parseDate(dateString: String): LocalDate? {
|
||||
return java.time.LocalDate.parse(dateString, formatter)?.toKotlinLocalDate()
|
||||
}
|
||||
|
||||
return null
|
||||
actual fun parse(dateString: String): LocalDateTime? {
|
||||
return java.time.LocalDateTime.parse(dateString, formatter)?.toKotlinLocalDateTime()
|
||||
}
|
||||
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package net.dankito.utils.multiplatform.serialization
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser
|
||||
import com.fasterxml.jackson.databind.DeserializationContext
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
|
||||
import net.dankito.utils.multiplatform.Date
|
||||
|
||||
|
||||
open class DateDeserializer : StdDeserializer<Date>(Date::class.java) {
|
||||
|
||||
override fun deserialize(parser: JsonParser, context: DeserializationContext): Date? {
|
||||
val millisSinceEpoch = parser.readValueAs(Long::class.java)
|
||||
|
||||
if (millisSinceEpoch == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return Date(millisSinceEpoch)
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue