Preserve event-local timestamp timezone

This commit is contained in:
ajp_anton
2026-07-23 10:51:51 +00:00
parent dd1c9e5a01
commit 0091eb5d49
5 changed files with 21 additions and 6 deletions
@@ -77,7 +77,7 @@ class ViewLogsFragment : Fragment(R.layout.fragment_view_logs) {
entries.forEach { entry ->
view.logRows.addView(TextView(requireContext()).apply {
text = settings.order.filter { it in settings.visibleFields }.mapNotNull { field -> when (field) {
LogField.TIMESTAMP -> timestamp(entry.recordedAtEpochMillis, settings.timestampZone)
LogField.TIMESTAMP -> timestamp(entry.recordedAtEpochMillis, settings.timestampZone, entry.eventTimeZoneId)
LogField.APP_NAME -> entry.appName
LogField.PACKAGE_NAME -> entry.packageName
LogField.ACTION -> entry.action.name.lowercase().replace('_', ' ')
@@ -95,9 +95,13 @@ class ViewLogsFragment : Fragment(R.layout.fragment_view_logs) {
}
}
private fun timestamp(value: Long, zone: TimestampZone): String {
private fun timestamp(value: Long, zone: TimestampZone, eventTimeZoneId: String?): String {
val format = DateFormat.getDateTimeInstance()
if (zone == TimestampZone.UTC) format.timeZone = java.util.TimeZone.getTimeZone("UTC")
format.timeZone = when (zone) {
TimestampZone.UTC -> java.util.TimeZone.getTimeZone("UTC")
TimestampZone.LOCAL_NOW -> java.util.TimeZone.getDefault()
TimestampZone.EVENT_LOCAL -> eventTimeZoneId?.let(java.util.TimeZone::getTimeZone) ?: java.util.TimeZone.getDefault()
}
return format.format(Date(value))
}
@@ -81,6 +81,7 @@ class NotificationCaptureService : NotificationListenerService() {
NotificationRuleEvaluator.allows(ruleStore.ruleFor(LoggingType.IMAGE_CONTENT), snapshot.packageName)
val entry = NotificationLogEntry(
recordedAtEpochMillis = System.currentTimeMillis(),
eventTimeZoneId = java.util.TimeZone.getDefault().id,
packageName = snapshot.packageName,
appName = appName,
action = action,
@@ -13,6 +13,7 @@ object NotificationLogEntryJson {
JSONObject()
.put("id", entry.id)
.put("recordedAtEpochMillis", entry.recordedAtEpochMillis)
.put("eventTimeZoneId", entry.eventTimeZoneId)
.put("packageName", entry.packageName)
.put("appName", entry.appName)
.put("action", entry.action.name)
@@ -29,6 +30,7 @@ object NotificationLogEntryJson {
NotificationLogEntry(
id = entry.getString("id"),
recordedAtEpochMillis = entry.getLong("recordedAtEpochMillis"),
eventTimeZoneId = entry.optString("eventTimeZoneId").takeIf { it.isNotEmpty() },
packageName = entry.getString("packageName"),
appName = entry.getString("appName"),
action = NotificationAction.valueOf(entry.getString("action")),
@@ -40,7 +40,7 @@ object LogExporter {
settings.order.filter { it in settings.visibleFields }.map(::header),
) + entries.sortedByDescending { it.recordedAtEpochMillis }.map { entry ->
settings.order.filter { it in settings.visibleFields }.map { field -> when (field) {
LogField.TIMESTAMP -> timestamp(entry.recordedAtEpochMillis, settings.timestampZone)
LogField.TIMESTAMP -> timestamp(entry.recordedAtEpochMillis, settings.timestampZone, entry.eventTimeZoneId)
LogField.APP_NAME -> entry.appName; LogField.PACKAGE_NAME -> entry.packageName
LogField.ACTION -> entry.action.name.lowercase().replace('_', ' ')
LogField.CONTENTS -> entry.contents ?: ""
@@ -49,7 +49,7 @@ object LogExporter {
private fun header(field: LogField) = field.name.lowercase().replace('_', ' ')
private fun htmlValue(field: LogField, entry: NotificationLogEntry, settings: LogViewSettings, imagePath: String?): String {
val value = when (field) {
LogField.TIMESTAMP -> timestamp(entry.recordedAtEpochMillis, settings.timestampZone)
LogField.TIMESTAMP -> timestamp(entry.recordedAtEpochMillis, settings.timestampZone, entry.eventTimeZoneId)
LogField.APP_NAME -> entry.appName
LogField.PACKAGE_NAME -> entry.packageName
LogField.ACTION -> entry.action.name.lowercase().replace('_', ' ')
@@ -62,7 +62,13 @@ object LogExporter {
escaped
}
}
private fun timestamp(value: Long, zone: TimestampZone): String = DateFormat.getDateTimeInstance().apply { if (zone == TimestampZone.UTC) timeZone = TimeZone.getTimeZone("UTC") }.format(Date(value))
private fun timestamp(value: Long, zone: TimestampZone, eventTimeZoneId: String?): String = DateFormat.getDateTimeInstance().apply {
timeZone = when (zone) {
TimestampZone.UTC -> TimeZone.getTimeZone("UTC")
TimestampZone.LOCAL_NOW -> TimeZone.getDefault()
TimestampZone.EVENT_LOCAL -> eventTimeZoneId?.let(TimeZone::getTimeZone) ?: TimeZone.getDefault()
}
}.format(Date(value))
private fun csvValue(value: String) = "\"${value.replace("\"", "\"\"")}\""
private fun escape(value: String) = value.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;")
}
@@ -6,6 +6,8 @@ import java.util.UUID
data class NotificationLogEntry(
val id: String = UUID.randomUUID().toString(),
val recordedAtEpochMillis: Long,
/** The device timezone at capture time, retained for event-local display/export. */
val eventTimeZoneId: String? = null,
val packageName: String,
val appName: String,
val action: NotificationAction,