Move SSD truth timestamp selection to typed Kotlin

ober

506080e9384545c5293e4f13867a0e97154790a4

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index cb0b131..a028221 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -5778,13 +5778,21 @@
     (typed-kotlin-file "com/sfb/ssdreview/TruthSessionJson.kt"
       (kotlin-imports (org json JSONObject))
       (typed-library (com sfb ssdreview)
-        (export truthForSession)
+        (export truthForSession truthTimestampValue)
+        (type Any)
         (type JSONObject)
+        (extern (jsonObjectOptAny (json : JSONObject) (key : String)) : (Nullable Any)
+          (kotlin-member-call opt))
         (def (truthSessionSourceObject (truth : JSONObject)) : JSONObject
           (let ((source (json-object-opt-json-object truth "source")))
             (if (nullable-null? source)
                 (json-object-empty)
                 (nullable-get source))))
+        (def (truthTimestampValue (truth : JSONObject)) : (Nullable Any)
+          (let ((generated (jsonObjectOptAny truth "generated_at")))
+            (if (nullable-null? generated)
+                (jsonObjectOptAny truth "created_at")
+                generated)))
         (def (truthForSession (session : SsdSession) (truth : JSONObject)) : JSONObject
           (let ((cached (json-object-copy truth)))
             (begin
@@ -9475,7 +9483,7 @@
        "    }"
        ""
        "    private fun truthTime(truth: JSONObject): Long? ="
-       "        jsonValueTime(truth.opt(\"generated_at\") ?: truth.opt(\"created_at\"))"
+       "        jsonValueTime(truthTimestampValue(truth))"
        ""
        "    private fun jsonTime(text: String): Long? = try {"
        "        val json = jsonObjectFromText(text)"