From da9d5c018b40b205c246fdd524dcf87c5aa89ca1 Mon Sep 17 00:00:00 2001 From: dankito Date: Sat, 12 Feb 2022 16:31:46 +0100 Subject: [PATCH] Replaced own Date implementation with kotlinx-datetime --- fints4k/build.gradle | 10 ++ .../net/dankito/banking/fints/FinTsClient.kt | 7 +- .../dankito/banking/fints/FinTsJobExecutor.kt | 12 +- .../banking/fints/log/MessageLogCollector.kt | 4 +- .../banking/fints/messages/MessageBuilder.kt | 5 +- .../datenelemente/abgeleiteteformate/Datum.kt | 17 ++- .../abgeleiteteformate/Uhrzeit.kt | 19 ++- .../tan/TanGeneratorTanMedium.kt | 6 +- .../implementierte/sepa/SepaMessageCreator.kt | 7 +- .../banking/fints/model/AccountTransaction.kt | 11 +- .../fints/model/CreditCardTransaction.kt | 8 +- .../model/GetAccountTransactionsParameter.kt | 6 +- .../fints/model/GetTransactionsParameter.kt | 6 +- .../banking/fints/model/MessageLogEntry.kt | 5 +- .../fints/model/RetrievedAccountData.kt | 6 +- .../banking/fints/response/ResponseParser.kt | 20 +-- .../fints/response/segments/Balance.kt | 7 +- .../fints/response/segments/BalanceSegment.kt | 4 +- .../fints/response/segments/TanResponse.kt | 4 +- .../fints/transactions/mt940/Mt940Parser.kt | 21 +-- .../fints/transactions/mt940/model/Balance.kt | 7 +- .../transactions/mt940/model/StatementLine.kt | 6 +- .../dankito/banking/fints/util/FinTsUtils.kt | 17 ++- .../dankito/banking/fints/FinTsTestBase.kt | 8 +- .../fints/messages/MessageBuilderTest.kt | 17 +-- .../fints/response/ResponseParserTest.kt | 16 +-- .../fints/transactions/Mt940ParserTest.kt | 31 ++--- .../dankito/banking/fints/util/Base64Test.kt | 4 +- .../banking/fints/util/FinTsUtilsTest.kt | 16 ++- multiplatform-utils/build.gradle | 17 ++- .../net/dankito/utils/multiplatform/Date.kt | 56 -------- .../utils/multiplatform/DateFormatter.kt | 9 +- .../extensions/LocalDateExtensions.kt | 77 +++++++++++ .../extensions/LocalDateTimeExtensions.kt | 69 ++++++++++ .../extensions/NumberExtensions.kt | 8 ++ .../extensions/RandomExtensions.kt | 11 ++ .../extensions/TimeZoneExtensions.kt | 7 + .../utils/multiplatform/log/LoggerBase.kt | 5 +- .../net/dankito/utils/multiplatform/Date.kt | 120 ------------------ .../utils/multiplatform/DateFormatter.kt | 18 ++- .../net/dankito/utils/multiplatform/Date.kt | 79 ------------ .../utils/multiplatform/DateFormatter.kt | 21 +-- .../serialization/DateDeserializer.kt | 21 --- 43 files changed, 393 insertions(+), 432 deletions(-) delete mode 100644 multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/Date.kt create mode 100644 multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/LocalDateExtensions.kt create mode 100644 multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/LocalDateTimeExtensions.kt create mode 100644 multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/RandomExtensions.kt create mode 100644 multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/TimeZoneExtensions.kt delete mode 100644 multiplatform-utils/src/iosMain/kotlin/net/dankito/utils/multiplatform/Date.kt delete mode 100644 multiplatform-utils/src/jvmMain/kotlin/net/dankito/utils/multiplatform/Date.kt delete mode 100644 multiplatform-utils/src/jvmMain/kotlin/net/dankito/utils/multiplatform/serialization/DateDeserializer.kt diff --git a/fints4k/build.gradle b/fints4k/build.gradle index 167b32a0..c8416a91 100644 --- a/fints4k/build.gradle +++ b/fints4k/build.gradle @@ -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 { diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt index 2fae3443..7525259f 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt @@ -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) } diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsJobExecutor.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsJobExecutor.kt index 5936ecb4..5ed2efab 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsJobExecutor.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsJobExecutor.kt @@ -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)) diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/log/MessageLogCollector.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/log/MessageLogCollector.kt index f25b66cc..4308388a 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/log/MessageLogCollector.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/log/MessageLogCollector.kt @@ -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 { diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt index 4233cabd..f80dcb7c 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt @@ -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() } diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/abgeleiteteformate/Datum.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/abgeleiteteformate/Datum.kt index d3abecc7..e0eeca49 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/abgeleiteteformate/Datum.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/abgeleiteteformate/Datum.kt @@ -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) } \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/abgeleiteteformate/Uhrzeit.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/abgeleiteteformate/Uhrzeit.kt index afe8b0fe..a8df72fb 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/abgeleiteteformate/Uhrzeit.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/abgeleiteteformate/Uhrzeit.kt @@ -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) } \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/implementierte/tan/TanGeneratorTanMedium.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/implementierte/tan/TanGeneratorTanMedium.kt index 46532ca7..45498669 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/implementierte/tan/TanGeneratorTanMedium.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/implementierte/tan/TanGeneratorTanMedium.kt @@ -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) { diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/sepa/SepaMessageCreator.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/sepa/SepaMessageCreator.kt index f05b7466..aaebb4b3 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/sepa/SepaMessageCreator.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/sepa/SepaMessageCreator.kt @@ -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 { 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) diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/AccountTransaction.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/AccountTransaction.kt index a3eae0fc..25c09a14 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/AccountTransaction.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/AccountTransaction.kt @@ -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, diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/CreditCardTransaction.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/CreditCardTransaction.kt index 9f355d5b..7cd10120 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/CreditCardTransaction.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/CreditCardTransaction.kt @@ -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 ) { diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/GetAccountTransactionsParameter.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/GetAccountTransactionsParameter.kt index e0b9a875..70f3d2e7 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/GetAccountTransactionsParameter.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/GetAccountTransactionsParameter.kt @@ -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. diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/GetTransactionsParameter.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/GetTransactionsParameter.kt index e17dfc4f..972c61f3 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/GetTransactionsParameter.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/GetTransactionsParameter.kt @@ -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. diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/MessageLogEntry.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/MessageLogEntry.kt index 4e8076e5..8fb56621 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/MessageLogEntry.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/MessageLogEntry.kt @@ -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 { diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/RetrievedAccountData.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/RetrievedAccountData.kt index 125addab..52967614 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/RetrievedAccountData.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/RetrievedAccountData.kt @@ -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, open var unbookedTransactions: Collection, - open val retrievedTransactionsFrom: Date?, - open val retrievedTransactionsTo: Date?, + open val retrievedTransactionsFrom: LocalDate?, + open val retrievedTransactionsTo: LocalDate?, open val errorMessage: String? = null ) { diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/ResponseParser.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/ResponseParser.kt index a7648db6..7a97de27 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/ResponseParser.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/ResponseParser.kt @@ -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) { } diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/Balance.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/Balance.kt index 43ce6269..f2803a5c 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/Balance.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/Balance.kt @@ -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 { diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/BalanceSegment.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/BalanceSegment.kt index a97bc766..e7792919 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/BalanceSegment.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/BalanceSegment.kt @@ -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 diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TanResponse.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TanResponse.kt index ab1c0da8..bd537187 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TanResponse.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/TanResponse.kt @@ -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 diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/transactions/mt940/Mt940Parser.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/transactions/mt940/Mt940Parser.kt index 3023c613..0eeedb2d 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/transactions/mt940/Mt940Parser.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/transactions/mt940/Mt940Parser.kt @@ -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 diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/transactions/mt940/model/Balance.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/transactions/mt940/model/Balance.kt index 4415721b..4b67fbb6 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/transactions/mt940/model/Balance.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/transactions/mt940/model/Balance.kt @@ -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 { diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/transactions/mt940/model/StatementLine.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/transactions/mt940/model/StatementLine.kt index 78ec592c..de8ab706 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/transactions/mt940/model/StatementLine.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/transactions/mt940/model/StatementLine.kt @@ -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 diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/util/FinTsUtils.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/util/FinTsUtils.kt index 766a471e..74c6ba04 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/util/FinTsUtils.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/util/FinTsUtils.kt @@ -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)) } diff --git a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/FinTsTestBase.kt b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/FinTsTestBase.kt index 6df8b288..7f4b340d 100644 --- a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/FinTsTestBase.kt +++ b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/FinTsTestBase.kt @@ -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) } diff --git a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/messages/MessageBuilderTest.kt b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/messages/MessageBuilderTest.kt index 54e4caff..cd37ce40 100644 --- a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/messages/MessageBuilderTest.kt +++ b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/messages/MessageBuilderTest.kt @@ -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" diff --git a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/response/ResponseParserTest.kt b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/response/ResponseParserTest.kt index 79a9e288..7fa9919b 100644 --- a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/response/ResponseParserTest.kt +++ b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/response/ResponseParserTest.kt @@ -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(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(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() diff --git a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/transactions/Mt940ParserTest.kt b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/transactions/Mt940ParserTest.kt index 574d47bd..69b741d5 100644 --- a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/transactions/Mt940ParserTest.kt +++ b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/transactions/Mt940ParserTest.kt @@ -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) } } \ No newline at end of file diff --git a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/util/Base64Test.kt b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/util/Base64Test.kt index 816229aa..91779725 100644 --- a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/util/Base64Test.kt +++ b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/util/Base64Test.kt @@ -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 diff --git a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/util/FinTsUtilsTest.kt b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/util/FinTsUtilsTest.kt index 4425c1c3..18278551 100644 --- a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/util/FinTsUtilsTest.kt +++ b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/util/FinTsUtilsTest.kt @@ -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) diff --git a/multiplatform-utils/build.gradle b/multiplatform-utils/build.gradle index 2518b614..d0baede8 100644 --- a/multiplatform-utils/build.gradle +++ b/multiplatform-utils/build.gradle @@ -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 { diff --git a/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/Date.kt b/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/Date.kt deleted file mode 100644 index a045ab88..00000000 --- a/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/Date.kt +++ /dev/null @@ -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 - -} \ No newline at end of file diff --git a/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/DateFormatter.kt b/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/DateFormatter.kt index d89beaa3..f9729476 100644 --- a/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/DateFormatter.kt +++ b/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/DateFormatter.kt @@ -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? } \ No newline at end of file diff --git a/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/LocalDateExtensions.kt b/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/LocalDateExtensions.kt new file mode 100644 index 00000000..2098cbfa --- /dev/null +++ b/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/LocalDateExtensions.kt @@ -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)) +} \ No newline at end of file diff --git a/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/LocalDateTimeExtensions.kt b/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/LocalDateTimeExtensions.kt new file mode 100644 index 00000000..e1cf284b --- /dev/null +++ b/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/LocalDateTimeExtensions.kt @@ -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)) +} \ No newline at end of file diff --git a/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/NumberExtensions.kt b/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/NumberExtensions.kt index 71c8109a..b905bb03 100644 --- a/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/NumberExtensions.kt +++ b/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/NumberExtensions.kt @@ -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) } \ No newline at end of file diff --git a/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/RandomExtensions.kt b/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/RandomExtensions.kt new file mode 100644 index 00000000..599b5f78 --- /dev/null +++ b/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/RandomExtensions.kt @@ -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() +} \ No newline at end of file diff --git a/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/TimeZoneExtensions.kt b/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/TimeZoneExtensions.kt new file mode 100644 index 00000000..a10bf6fe --- /dev/null +++ b/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/extensions/TimeZoneExtensions.kt @@ -0,0 +1,7 @@ +package net.dankito.utils.multiplatform.extensions + +import kotlinx.datetime.TimeZone + + +val TimeZone.Companion.europeBerlin: TimeZone + get() = TimeZone.of("Europe/Berlin") \ No newline at end of file diff --git a/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/log/LoggerBase.kt b/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/log/LoggerBase.kt index e9292de7..f1440445 100644 --- a/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/log/LoggerBase.kt +++ b/multiplatform-utils/src/commonMain/kotlin/net/dankito/utils/multiplatform/log/LoggerBase.kt @@ -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" } } \ No newline at end of file diff --git a/multiplatform-utils/src/iosMain/kotlin/net/dankito/utils/multiplatform/Date.kt b/multiplatform-utils/src/iosMain/kotlin/net/dankito/utils/multiplatform/Date.kt deleted file mode 100644 index 935ceafb..00000000 --- a/multiplatform-utils/src/iosMain/kotlin/net/dankito/utils/multiplatform/Date.kt +++ /dev/null @@ -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)" - } - - -} \ No newline at end of file diff --git a/multiplatform-utils/src/iosMain/kotlin/net/dankito/utils/multiplatform/DateFormatter.kt b/multiplatform-utils/src/iosMain/kotlin/net/dankito/utils/multiplatform/DateFormatter.kt index 103c7d3b..b9ffed3a 100644 --- a/multiplatform-utils/src/iosMain/kotlin/net/dankito/utils/multiplatform/DateFormatter.kt +++ b/multiplatform-utils/src/iosMain/kotlin/net/dankito/utils/multiplatform/DateFormatter.kt @@ -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 diff --git a/multiplatform-utils/src/jvmMain/kotlin/net/dankito/utils/multiplatform/Date.kt b/multiplatform-utils/src/jvmMain/kotlin/net/dankito/utils/multiplatform/Date.kt deleted file mode 100644 index aef6fd28..00000000 --- a/multiplatform-utils/src/jvmMain/kotlin/net/dankito/utils/multiplatform/Date.kt +++ /dev/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) - } - -} \ No newline at end of file diff --git a/multiplatform-utils/src/jvmMain/kotlin/net/dankito/utils/multiplatform/DateFormatter.kt b/multiplatform-utils/src/jvmMain/kotlin/net/dankito/utils/multiplatform/DateFormatter.kt index 304537f4..f31173b9 100644 --- a/multiplatform-utils/src/jvmMain/kotlin/net/dankito/utils/multiplatform/DateFormatter.kt +++ b/multiplatform-utils/src/jvmMain/kotlin/net/dankito/utils/multiplatform/DateFormatter.kt @@ -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() } } \ No newline at end of file diff --git a/multiplatform-utils/src/jvmMain/kotlin/net/dankito/utils/multiplatform/serialization/DateDeserializer.kt b/multiplatform-utils/src/jvmMain/kotlin/net/dankito/utils/multiplatform/serialization/DateDeserializer.kt deleted file mode 100644 index 03a62a6a..00000000 --- a/multiplatform-utils/src/jvmMain/kotlin/net/dankito/utils/multiplatform/serialization/DateDeserializer.kt +++ /dev/null @@ -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::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) - } - -} \ No newline at end of file