Skip to content

Commit

Permalink
Add null safety on date formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
OlukaDenis committed Nov 22, 2024
2 parents af796f5 + 439430c commit 918de70
Show file tree
Hide file tree
Showing 7 changed files with 348 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class MainActivity : AppCompatActivity() {
private fun content() {
val date = "2024-09-08T14:54:00.85ZZ"
val view = findViewById<TextView>(R.id.mtView)
Log.d("MainActivity", "Formatted:::: " + SparkUtils.formatDateTime(date))
view.text = SparkUtils.formatDateTime(date, "hh:mm ss")
// Log.d("MainActivity", "Formatted:::: " + SparkUtils.formatDateTime(date))
// view.text = SparkUtils.formatDateTime(date, "hh:mm ss")
}
}
5 changes: 2 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
agp = "8.1.2"
agp = "8.1.4"
kotlin = "1.9.0"
coreKtx = "1.13.1"
junit = "4.13.2"
Expand All @@ -9,7 +9,6 @@ appcompat = "1.6.1"
material = "1.11.0"
activity = "1.8.0"
constraintlayout = "2.1.4"
jetbrainsKotlinJvm = "1.9.0"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
Expand All @@ -25,5 +24,5 @@ kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", versio
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
jetbrainsKotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrainsKotlinJvm" }
jetbrainsKotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }

158 changes: 158 additions & 0 deletions sparkutils/bin/main/com/mcdenny/sparkutils/SparkCryptUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package com.mcdenny.sparkutils

import java.util.Locale
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec

object SparkCryptUtils {

/**
* Convert hex string to bytes
*/
@JvmStatic
fun hexToBytes(hexStr: String): ByteArray? {
if (hexStr.isEmpty()) {
return null
}
val result = ByteArray(hexStr.length / 2)
for (i in 0 until hexStr.length / 2) {
val high = hexStr.substring(i * 2, i * 2 + 1).toInt(16)
val low = hexStr.substring(i * 2 + 1, i * 2 + 2).toInt(
16
)
result[i] = (high * 16 + low).toByte()
}
return result
}

/**
* Convert bytes to hex string
*/
@JvmStatic
fun bytesToHex(buf: ByteArray): String {
val sb = StringBuffer()
for (i in buf.indices) {
var hex = Integer.toHexString(buf[i].toInt() and 0xFF)
if (hex.length == 1) {
hex = "0$hex"
}
sb.append(hex.uppercase(Locale.getDefault()))
}
return sb.toString()
}

/**
* Convert hex value to ascii code
**/
@JvmStatic
fun hexToAsciiString(hex: String): String {
val sb = StringBuilder()
val temp = StringBuilder()

//49204c6f7665204a617661 split into two characters 49, 20, 4c...
var i = 0
while (i < hex.length - 1) {
//grab the hex in pairs
val output = hex.substring(i, i + 2)
//convert hex to decimal
val decimal = output.toInt(16)
//convert the decimal to character
sb.append(decimal.toChar())
temp.append(decimal)
i += 2
}
return sb.toString()
}

/**
* convert int to byte[]
* @param i integer to be converted to byte array
* @return byte array
*/
@JvmStatic
fun intToByteArray(i: Int): ByteArray {
val result = ByteArray(2)
result[0] = (i shr 8 and 0xFF).toByte()
result[1] = (i and 0xFF).toByte()
return result
}


@JvmStatic
private fun charToByte(c: Char): Byte {
return "0123456789ABCDEF".indexOf(c).toByte()
}

@JvmStatic
fun intToHexString(i: Int): String {
var string: String? = null
string = if (i in 0..9) {
"0$i"
} else {
Integer.toHexString(i)
}
if (string!!.length == 2) {
string = "00$string"
} else if (string.length == 1) {
string = "000$string"
} else if (string.length == 3) {
string = "0$string"
}
return string
}


/**
* convert hexadecimal byte to int
*
* @param b
* @return
*/
@JvmStatic
fun byteArrayToInt(b: ByteArray): Int {
var result = 0
for (i in b.indices) {
result = result shl 8
result = result or (b[i].toInt() and 0xff) //
}
return result
}

fun extractKCVFromKey(keyHex: String): String {
return try {
// Convert the key to a byte array
val keyByes = hexToBytes(keyHex) ?: return ""

// Define a block of 8 zero bytes
val zeroBlock = ByteArray(16)

// Encrypt the zeroBlock using the extended Key
val encryptedData: ByteArray = encrypt3DES(keyByes, zeroBlock)

// Take the first 3 bytes (6 hexadecimal digits) as the KCV
val result: String = bytesToHex(encryptedData)
val res = result.trim { it <= ' ' }.substring(0, 16)
res.trim { it <= ' ' }
} catch (e: Exception) {
""
}
}

@JvmStatic
@Throws(java.lang.Exception::class)
fun encrypt3DES(value: ByteArray, data: ByteArray?): ByteArray {
val cipher: Cipher
if (value.size == 8) { // Single-length (64-bit) TMK
cipher = Cipher.getInstance("DES/ECB/NoPadding")
val desKey = SecretKeySpec(value, "DES")
cipher.init(Cipher.ENCRYPT_MODE, desKey)
} else if (value.size == 16) { // Double-length (128-bit) TMK
cipher = Cipher.getInstance("DESede/ECB/NoPadding") // 3DES cipher
val desKey = SecretKeySpec(value, "DESede")
cipher.init(Cipher.ENCRYPT_MODE, desKey)
} else {
throw IllegalArgumentException("value must be either single (16 hex) or double-length (32 hex)")
}
return cipher.doFinal(data)
}
}
5 changes: 5 additions & 0 deletions sparkutils/bin/main/com/mcdenny/sparkutils/SparkPosUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.mcdenny.sparkutils

object SparkPosUtils {

}
151 changes: 150 additions & 1 deletion sparkutils/bin/main/com/mcdenny/sparkutils/SparkUtils.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package com.mcdenny.sparkutils

import java.security.SecureRandom
import java.text.DateFormat
import java.text.DecimalFormat
import java.text.NumberFormat
import java.text.SimpleDateFormat
import java.util.Currency
import java.util.Date
import java.util.Locale

object SparkUtils {
// @JvmStatic
@JvmStatic
fun formatNumber(number: Double): String {
val formatter = DecimalFormat("#,###,###")
return formatter.format(number)
Expand All @@ -21,4 +28,146 @@ object SparkUtils {
return "$currencyCode ${formatter.format(number)}"
}

@JvmStatic
@JvmOverloads
fun formatCurrency(value: Double, symbol: String? = "UGX", fractionDigits: Int = 0): String{
val format = NumberFormat.getCurrencyInstance()
format.maximumFractionDigits = fractionDigits
format.currency = Currency.getInstance(symbol)

return format.format(value)
}

@JvmStatic
fun getRandomNumber(): String {
val random = SecureRandom()
val randomNumber = random.nextInt(100000000 - 100) + 100
return randomNumber.toString()
}

@JvmStatic
fun isNullOrEmpty(value: String?): Boolean {
return value.isNullOrEmpty()
}

@JvmStatic
fun toKiloBytes(value: Long): Int {
return (value / 1024).toInt()
}

@JvmStatic
fun toMegaBytes(value: Long): Int {
return (value / 1024 / 1024).toInt()
}

@JvmStatic
fun maskText(text: String, startDigits: Int, endDigits: Int): String {
val textChars = text.toCharArray()
val end = text.length - endDigits
for (i in textChars.indices) {
if (i in startDigits..<end) {
textChars[i] = '*'
}
}
return String(textChars)
}

@JvmStatic
@JvmOverloads
fun todayFormatted(pattern: String = "yyyy-MM-dd HH:mm:ss a"): String {
val dtf: DateFormat = SimpleDateFormat(pattern, Locale.getDefault())
val today = Date()
return dtf.format(today)
}


@JvmStatic
@JvmOverloads
fun longDateToString(time: Long, pattern: String = "yyyy-MM-dd HH:mm:ss a"): String {
return try {
// Create a Date object from the milliseconds
val date = Date(time)

// Define the desired date format
val dateFormat = SimpleDateFormat(pattern, Locale.getDefault())

// Format the date to the specified format
dateFormat.format(date)
} catch (ex: Exception) {
ex.printStackTrace()
""
}
}

@JvmStatic
@JvmOverloads
fun formatDate(inputDate: String, pattern: String = "yyyy-MM-dd HH:mm:ss a"): String {
val formats = arrayOf(
"yyyy-MM-dd'T'HH:mm:ss.SSSS",
"yyyy-MM-dd'T'HH:mm:ss.SSSZ",
"yyyy-MM-dd'T'HH:mm:ss.SSS",
"yyyy-MM-dd'T'HH:mm:ssZ",
"yyyy-MM-dd'T'HH:mm:ss",
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd",
"yyyy/MM/dd'T'HH:mm:ss.SSSS",
"yyyy/MM/dd'T'HH:mm:ss.SSSZ",
"yyyy/MM/dd'T'HH:mm:ssZ",
"yyyy/MM/dd'T'HH:mm:ss",
"yyyy/MM/dd HH:mm:ss",
"yyyy/MM/dd",
"dd/MM/yyyy",
"dd-MM-yyyy",
"dd/MM/yyyy HH:mm:ss",
"dd-MM-yyyy HH:mm:ss",
"dd-MM-yyyy HH:mm:ss.SSS",
"dd-MM-yyyy HH:mm:ss.SSSZ",
"dd-MM-yyyy HH:mm:ss.SSSXXX",
"MM/dd/yyyy HH:mm:ss",
"MM/dd/yyyy",
"MM-dd-yyyy",
"MM-dd-yyyy HH:mm:ss",
)

return try {
val date = formats.asSequence()
.mapNotNull { format ->
runCatching {
SimpleDateFormat(format, Locale.getDefault()).parse(inputDate).also {
println("Trying format: $format, Result: $it")
}
}.getOrNull()
}
.firstOrNull() ?: throw IllegalArgumentException("Unparseable date: $inputDate")

val outputTime = SimpleDateFormat(pattern, Locale.getDefault())
outputTime.format(date)
} catch (ex: Exception) {
ex.printStackTrace()
inputDate
}
}

@JvmStatic
fun objectToString(`object`: Any): String {
if (`object` is Int) {
return `object`.toString()
}
return if (`object` is Double) {
`object`.toString()
} else if (`object` is Boolean) {
`object`.toString()
} else {
`object`.toString()
}
}

@JvmStatic
fun capitalize(value: String): String {
return value.replaceFirstChar {
if (it.isLowerCase()) it.titlecase(
Locale.getDefault()
) else it.toString()
}
}
}
2 changes: 1 addition & 1 deletion sparkutils/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ publishing {
from(components["java"])
groupId = "com.github.OlukaDenis"
artifactId = "spark-utils"
version = "0.1.3"
version = "0.1.5"

pom {
name.set("Spark Utils")
Expand Down
Loading

0 comments on commit 918de70

Please sign in to comment.