Skip to content

Commit 7e32e51

Browse files
committed
Cleanup
1 parent 032feef commit 7e32e51

File tree

2 files changed

+54
-17
lines changed

2 files changed

+54
-17
lines changed

lib/src/main/kotlin/at/bitfire/vcard4android/contactrow/EventHandler.kt

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,37 +24,53 @@ import java.time.temporal.Temporal
2424

2525
object EventHandler : DataRowHandler() {
2626

27-
// source: https://android.googlesource.com/platform/packages/apps/Contacts/+/c326c157541978c180be4e3432327eceb1e66637/src/com/android/contacts/util/CommonDateUtils.java#25
28-
private val acceptableFormats: List<Pair<DateTimeFormatter, (String, DateTimeFormatter) -> Temporal>> = listOf(
29-
// Formats provided by Android's CommonDateUtils
30-
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX") to OffsetDateTime::parse,
31-
DateTimeFormatter.ofPattern("yyyy-MM-dd") to LocalDate::parse,
32-
// Additional common formats
33-
DateTimeFormatter.ISO_OFFSET_DATE_TIME to OffsetDateTime::parse, // "yyyy-MM-dd'T'HH:mm:ssXXX"
27+
// CommonDateUtils: https://android.googlesource.com/platform/packages/apps/Contacts/+/c326c157541978c180be4e3432327eceb1e66637/src/com/android/contacts/util/CommonDateUtils.java#25
28+
29+
/**
30+
* Date formats for full date with time. Converts to [OffsetDateTime].
31+
*/
32+
private val fullDateTimeFormats = listOf(
33+
// Provided by Android's CommonDateUtils
34+
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"),
35+
// "yyyy-MM-dd'T'HH:mm:ssXXX"
36+
DateTimeFormatter.ISO_OFFSET_DATE_TIME,
3437
)
3538

39+
/**
40+
* Date format for full date without time. Converts to [LocalDate].
41+
*/
42+
private val fullDateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd")
43+
44+
3645
override fun forMimeType() = Event.CONTENT_ITEM_TYPE
3746

3847
/**
3948
* Tries to parse a date string into a [Temporal] object using multiple acceptable formats.
4049
* Returns the parsed [Temporal] if successful, or `null` if none of the formats match.
4150
* @param dateString The date string to parse.
4251
* @return If format is:
43-
* - `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'` or `yyyy-MM-dd'T'HH:mm:ssXXX` -> [OffsetDateTime]
44-
* - `yyyy-MM-dd` -> [LocalDate]
52+
* - `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'` or `yyyy-MM-dd'T'HH:mm:ssXXX` ([fullDateTimeFormats]) -> [OffsetDateTime]
53+
* - `yyyy-MM-dd` ([fullDateFormat]) -> [LocalDate]
4554
* - else -> `null`
4655
*/
4756
@VisibleForTesting
48-
internal fun parseStartDate(dateString: String): Temporal? {
49-
for ((formatter, parse) in acceptableFormats) {
57+
internal fun parseFullDate(dateString: String): Temporal? {
58+
for (formatter in fullDateTimeFormats) {
5059
try {
51-
return parse(dateString, formatter)
60+
return OffsetDateTime.parse(dateString, formatter)
5261
} catch (_: DateTimeParseException) {
5362
// ignore: given date is not valid
5463
continue
5564
}
5665
}
5766

67+
// try parsing as full date only (no time)
68+
try {
69+
return LocalDate.parse(dateString, fullDateFormat)
70+
} catch (_: DateTimeParseException) {
71+
// ignore: given date is not valid
72+
}
73+
5874
// could not parse date
5975
return null
6076
}
@@ -72,6 +88,8 @@ object EventHandler : DataRowHandler() {
7288
internal fun parsePartialDate(dateString: String): PartialDate? {
7389
var dateString = dateString // to allow modification
7490
return try {
91+
// convert Android partial date/date-time to vCard partial date/date-time so that it can be parsed by ez-vcard
92+
7593
if (dateString.endsWith('Z')) {
7694
// 'Z' is not supported for suffix in PartialDate, replace with actual offset
7795
dateString = dateString.removeSuffix("Z") + "+00:00"
@@ -95,7 +113,7 @@ object EventHandler : DataRowHandler() {
95113
super.handle(values, contact)
96114

97115
val dateStr = values.getAsString(Event.START_DATE) ?: return
98-
val full: Temporal? = parseStartDate(dateStr)
116+
val full: Temporal? = parseFullDate(dateStr)
99117
val partial: PartialDate? = if (full == null) {
100118
parsePartialDate(dateStr)
101119
} else {

lib/src/test/kotlin/at/bitfire/vcard4android/contactrow/EventHandlerTest.kt

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,40 @@ class EventHandlerTest {
2929
// https://android.googlesource.com/platform/packages/apps/Contacts/+/refs/tags/android-13.0.0_r49/src/com/android/contacts/util/CommonDateUtils.java
3030

3131
@Test
32-
fun test_parseStartDate_ISO_DATE_AND_TIME_FORMAT_DateTime() {
32+
fun test_parseFullDate_ISO_DATE_AND_TIME_FORMAT_DateTime() {
3333
assertEquals(
3434
OffsetDateTime.of(1953, 10, 15, 23, 10, 0, 0, ZoneOffset.UTC),
35-
EventHandler.parseStartDate("1953-10-15T23:10:00Z")
35+
EventHandler.parseFullDate("1953-10-15T23:10:00Z")
3636
)
3737
}
3838

3939
@Test
40-
fun test_parseStartDate_FULL_DATE_FORMAT_Date() {
40+
fun test_parseFullDate_FULL_DATE_FORMAT_Date() {
4141
assertEquals(
4242
LocalDate.of(1953, 10, 15),
43-
EventHandler.parseStartDate("1953-10-15")
43+
EventHandler.parseFullDate("1953-10-15")
4444
)
4545
}
4646

47+
48+
@Test
49+
fun test_parsePartialDate_NO_YEAR_DATE_FORMAT() {
50+
assertEquals(
51+
PartialDate.builder().month(10).date(15).build(),
52+
EventHandler.parsePartialDate("--10-15")
53+
)
54+
}
55+
56+
@Test
57+
fun test_parsePartialDate_NO_YEAR_DATE_AND_TIME_FORMAT() {
58+
// Partial date does not support nanoseconds, so they will be removed
59+
assertEquals(
60+
PartialDate.builder().month(8).date(20).hour(23).minute(10).second(12).offset(ZoneOffset.UTC).build(),
61+
EventHandler.parsePartialDate("--08-20T23:10:12.345Z")
62+
)
63+
}
64+
65+
4766
@Test
4867
fun testStartDate_Empty() {
4968
val contact = Contact()

0 commit comments

Comments
 (0)