diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/util/format/TextFormatter.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/util/format/TextFormatter.kt
index 0278fc4f9c38507de68b08d3668638e95b148cee..3d605faa9253633e69852f8b60b85ebec3cf0384 100644
--- a/app/src/main/kotlin/de/justjanne/quasseldroid/util/format/TextFormatter.kt
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/util/format/TextFormatter.kt
@@ -21,6 +21,8 @@ import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.buildAnnotatedString
 import androidx.core.text.buildSpannedString
 import de.justjanne.quasseldroid.util.AnnotatedStringAppender
+import de.justjanne.quasseldroid.util.extensions.component6
+import de.justjanne.quasseldroid.util.extensions.component7
 import java.util.*
 import java.util.regex.Pattern
 
@@ -66,7 +68,7 @@ object TextFormatter {
         is FormatString.FixedValue -> target.append(block.content)
         is FormatString.FormatSpecifier -> {
           val arg = when {
-              block.index != null -> args[block.index - 1]
+              block.argumentIndex != null -> args[block.argumentIndex - 1]
               block.flags.orEmpty().contains(FLAG_REUSE_ARGUMENT) -> args[argIndex]
               else -> args[argIndex++]
           }
@@ -120,23 +122,23 @@ object TextFormatter {
       }
       index = match.range.last + 1
 
-      val conversionGroup = match.groups["conversion"]?.value
-      require(conversionGroup != null) {
-        "Invalid format string '$match', missing conversion"
+      val groupValues = match.groupValues
+      require(groupValues.size == 7) {
+        "Invalid match '$match', should return 6 groups, returned ${groupValues.size}"
       }
-      require(conversionGroup.length == 1) {
+      val (_, argumentIndex, flags, width, precision, time, conversion) = groupValues
+      require(conversion.length == 1) {
         "Invalid format string '$match', conversion too long"
       }
-      val conversion = conversionGroup.first()
 
       yield(
         FormatString.FormatSpecifier(
-          index = match.groups["index"]?.value?.toIntOrNull(),
-          flags = match.groups["flags"]?.value,
-          width = match.groups["width"]?.value?.toIntOrNull(),
-          precision = match.groups["precision"]?.value?.toIntOrNull(),
-          time = match.groups["time"] != null,
-          conversion = conversion
+          argumentIndex = argumentIndex.takeIf(String::isNotEmpty)?.toIntOrNull(),
+          flags = flags.takeIf(String::isNotEmpty),
+          width = width.takeIf(String::isNotEmpty)?.toIntOrNull(),
+          precision = precision.takeIf(String::isNotEmpty)?.toIntOrNull(),
+          time = time.takeIf(String::isNotEmpty) != null,
+          conversion = conversion.first()
         )
       )
     }
@@ -148,5 +150,5 @@ object TextFormatter {
   private const val FLAG_REUSE_ARGUMENT = '<'
 
   private val FORMAT_SEQUENCE =
-    Pattern.compile("%(?:(?<index>[0-9]+)\\$)?(?<flags>[,\\-(+# 0<]+)?(?<width>[0-9]+)?(?:\\.(?<precision>[0-9]+))?(?<time>[tT])?(?<conversion>[a-zA-Z])")
+    Pattern.compile("%(?:([0-9]+)\\\$)?([,\\-(+# 0<]*)([0-9]*)(?:\\.([0-9]*))?([tT]?)([a-zA-Z])")
 }
diff --git a/app/src/test/kotlin/de/kuschku/justjanne/quasseldroid/util/irc/TextFormatterTest.kt b/app/src/test/kotlin/de/kuschku/justjanne/quasseldroid/util/irc/TextFormatterTest.kt
index 3a844e9dec080cbdb18ab4838b2d4b7423a481cf..23fcf9e53803036c341d09d5dbb94dc59619d202 100644
--- a/app/src/test/kotlin/de/kuschku/justjanne/quasseldroid/util/irc/TextFormatterTest.kt
+++ b/app/src/test/kotlin/de/kuschku/justjanne/quasseldroid/util/irc/TextFormatterTest.kt
@@ -1,21 +1,31 @@
 package de.kuschku.justjanne.quasseldroid.util.irc
 
-import android.text.Spanned
-import android.text.style.ForegroundColorSpan
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.buildAnnotatedString
-import androidx.core.text.buildSpannedString
+import de.justjanne.quasseldroid.util.format.FormatString
 import de.justjanne.quasseldroid.util.format.TextFormatter
 import org.junit.jupiter.api.Test
 import java.util.*
 import kotlin.test.assertEquals
 
 class TextFormatterTest {
-  @Test
-  fun testBasicFormatting() {
-    val calendar = GregorianCalendar(1995, Calendar.MAY, 23, 13, 34, 18)
+  private val calendar = GregorianCalendar(1995, Calendar.MAY, 23, 13, 34, 18)
 
+  @Test
+  fun testNumberedArgumentIndex() {
+    assertEquals(
+      listOf(
+        FormatString.FormatSpecifier(argumentIndex = 4, width = 2, conversion = 's'),
+        FormatString.FixedValue(" "),
+        FormatString.FormatSpecifier(argumentIndex = 3, width = 2, conversion = 's'),
+        FormatString.FixedValue(" "),
+        FormatString.FormatSpecifier(argumentIndex = 2, width = 2, conversion = 's'),
+        FormatString.FixedValue(" "),
+        FormatString.FormatSpecifier(argumentIndex = 1, width = 2, conversion = 's')
+      ),
+      TextFormatter.parseBlocks("%4\$2s %3\$2s %2\$2s %1\$2s").toList()
+    )
     assertEquals(
       " d  c  b  a",
       TextFormatter.format(
@@ -23,10 +33,55 @@ class TextFormatterTest {
         locale = Locale.ENGLISH
       )
     )
+  }
+
+  @Test
+  fun testReuseFlag() {
+    assertEquals(
+      listOf(
+        FormatString.FixedValue("Duke's Birthday: "),
+        FormatString.FormatSpecifier(argumentIndex = 1, time = true, conversion = 'b'),
+        FormatString.FixedValue(" "),
+        FormatString.FormatSpecifier(flags = "<", time = true, conversion = 'e'),
+        FormatString.FixedValue(", "),
+        FormatString.FormatSpecifier(flags = "<", time = true, conversion = 'Y'),
+      ),
+      TextFormatter.parseBlocks("Duke's Birthday: %1\$tb %<te, %<tY").toList()
+    )
+    assertEquals(
+      "Duke's Birthday: May 23, 1995",
+      TextFormatter.format(
+        "Duke's Birthday: %1\$tb %<te, %<tY",
+        calendar,
+        locale = Locale.ENGLISH,
+      )
+    )
+  }
+
+  @Test
+  fun testFloatFormatting() {
+    assertEquals(
+      listOf(
+        FormatString.FixedValue("e = "),
+        FormatString.FormatSpecifier(flags = "+", width = 10, precision = 4, conversion = 'f'),
+      ),
+      TextFormatter.parseBlocks("e = %+10.4f").toList()
+    )
     assertEquals(
       "e =    +2,7183",
       TextFormatter.format("e = %+10.4f", Math.E, locale = Locale.FRANCE)
     )
+  }
+
+  @Test
+  fun testAccountingFormatting() {
+    assertEquals(
+      listOf(
+        FormatString.FixedValue("Amount gained or lost since last statement: \$ "),
+        FormatString.FormatSpecifier(flags = "(,", precision = 2, conversion = 'f'),
+      ),
+      TextFormatter.parseBlocks("Amount gained or lost since last statement: \$ %(,.2f").toList()
+    )
     assertEquals(
       "Amount gained or lost since last statement: $ (6,217.58)",
       TextFormatter.format(
@@ -34,6 +89,17 @@ class TextFormatterTest {
         locale = Locale.ENGLISH
       )
     )
+  }
+
+  @Test
+  fun testDateTimeFormatting() {
+    assertEquals(
+      listOf(
+        FormatString.FixedValue("Local time: "),
+        FormatString.FormatSpecifier(time = true, conversion = 'T'),
+      ),
+      TextFormatter.parseBlocks("Local time: %tT").toList()
+    )
     assertEquals(
       "Local time: 13:34:18",
       TextFormatter.format(
@@ -42,14 +108,17 @@ class TextFormatterTest {
         locale = Locale.ENGLISH
       )
     )
+
     assertEquals(
-      "Unable to open file 'food': No such file or directory",
-      TextFormatter.format(
-        "Unable to open file '%1\$s': %2\$s",
-        "food",
-        "No such file or directory",
-        locale = Locale.ENGLISH
-      )
+      listOf(
+        FormatString.FixedValue("Duke's Birthday: "),
+        FormatString.FormatSpecifier(argumentIndex = 1, time = true, conversion = 'b'),
+        FormatString.FixedValue(" "),
+        FormatString.FormatSpecifier(argumentIndex = 1, time = true, conversion = 'e'),
+        FormatString.FixedValue(", "),
+        FormatString.FormatSpecifier(argumentIndex = 1, time = true, conversion = 'Y'),
+      ),
+      TextFormatter.parseBlocks("Duke's Birthday: %1\$tb %1\$te, %1\$tY").toList()
     )
     assertEquals(
       "Duke's Birthday: May 23, 1995",
@@ -59,14 +128,6 @@ class TextFormatterTest {
         locale = Locale.ENGLISH
       )
     )
-    assertEquals(
-      "Duke's Birthday: May 23, 1995",
-      TextFormatter.format(
-        "Duke's Birthday: %1\$tb %<te, %<tY",
-        calendar,
-        locale = Locale.ENGLISH,
-      )
-    )
   }
 
   @Test