Please see #136 for further discussion.
Birthdays without year
-
It would be nice, if you support birthdays without a year in carddav with vcard 4.0.
-
@jofrax Do you know whether Android actually supports that? START_DATE seems to be a
String
, but which format does it have for birthdays without year?Also, I have never seen a contacts app supporting this yet. Maybe they will just crash… do you know a contacts app supporting this?
-
The google contact app supports this.
There is an option yearOptional to activate this in res/xml/contacts.xml:
<DataKind kind=“event” dateWithTime=“false” >
<Type maxOccurs=“1” type=“birthday” yearOptional=“false” />
<Type maxOccurs=“1” type=“anniversary” />
</DataKind>I think START_DATE has to be in format 2017-05-24 or --05-24
-
@jofrax Which server do you use? So that I can test it
-
@rfc2822 I extended my server to speak VCard 4.0 and wanted to test this with Davdroid. So I stumbled on that problem. It will soon be available.
I’m not an android developer, but it should support dates without year. If I sync my contacts with google the year is optional.
Also I saw the property yearOptional in the Davdroid source code.VCard 4.0 does support dates without year in the rfc 6350, the ezvcard-library has a getPartialDate() function, that supports dates without year.
-
Seems like for format is really “–mm-dd”:
// No year format is not supported by Exchange. mockNameValues.put(Event.START_DATE, "--06-01"); mockNameValues.put(Event.TYPE, Event.TYPE_BIRTHDAY); oldState.addEntry(ValuesDelta.fromAfter(mockNameValues)); mockNameValues = new ContentValues(); mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE); mockNameValues.put(Event.START_DATE, "1980-08-02"); // Anniversary is not supported by Exchange mockNameValues.put(Event.TYPE, Event.TYPE_ANNIVERSARY); oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
So this should be possible.
-
Should be implemented by vcard4android/0cb821c7 and davdroid/fab9dbaf, but needs some real-life testing. Could you do that?
-
You are really fast.
One thing you should notice is that PartialDate is not working with VCard 3.0, because in 3.0 the date must have a year. Ezvcard will throw an exception.I will test it on friday.
-
@jofrax said in Birthdays without year:
One thing you should notice is that PartialDate is not working with VCard 3.0, because in 3.0 the date must have a year. Ezvcard will throw an exception.
In my tests, it just doesn’t output a
BDAY
in VCard/3 when there’s only a partial date. -
I did some tests with the server and got NullPointerExceptions. I fixed this in your code and will make a MergeRequest.
For VCard 3.0 I set the year in your code to 1900, if there was no year - otherwise you cannot save the birthday. Maybe you can set a VCard property - apple makes it this way (BDAY;X-APPLE-OMIT-YEAR=1604:1604-08-14), but the server must understand this.
-
Seems I’m not allowed to make MergeRequests on your project!
I forked the project to https://gitlab.com/jofrax/vcard4android and changed 3 files. Everything (VCard 3.0, 4.0, with and without year) is working for me so far.
-
@jofrax Can you please post a patch here for the latest vcard4android?
And I’ll still have to find a server which I can use to test, I’ll try with Nextcloud
-
This post is deleted! -
This post is deleted! -
Index: src/main/java/at/bitfire/vcard4android/AndroidContact.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- src/main/java/at/bitfire/vcard4android/AndroidContact.java (revision 0cb821c7b4b5cb30995b1e75802f7aefd85ef5f3) +++ src/main/java/at/bitfire/vcard4android/AndroidContact.java (revision 7ce4ce8ae74a0a482a9e81a5384dcb5a4136d1be) @@ -46,6 +46,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; @@ -1231,6 +1232,17 @@ } protected void insertEvent(BatchOperation batch, int type, DateOrTimeProperty dateOrTime) { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US); + String startDate; + if (dateOrTime.getPartialDate() != null) { + startDate = dateOrTime.getPartialDate().toString(); + } else if (dateOrTime.getDate() != null) { + startDate = formatter.format(dateOrTime.getDate()); + } else { + Constants.log.warning("Ignoring contact event (birthday/anniversary) without date"); + return; + } + final BatchOperation.Operation op; final ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(dataSyncURI()); if (id == null) @@ -1241,7 +1253,7 @@ } builder .withValue(Event.MIMETYPE, Event.CONTENT_ITEM_TYPE) .withValue(Event.TYPE, type) - .withValue(Event.START_DATE, dateOrTime.getPartialDate().toString()); + .withValue(Event.START_DATE, startDate); batch.enqueue(op); Constants.log.log(Level.FINER, "Built Event data row", builder.build()); } Index: src/main/java/at/bitfire/vcard4android/Contact.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- src/main/java/at/bitfire/vcard4android/Contact.java (revision 7ce4ce8ae74a0a482a9e81a5384dcb5a4136d1be) +++ src/main/java/at/bitfire/vcard4android/Contact.java (revision 4f3c2508b33309f95d1d0f01745f9741aa942c14) @@ -17,6 +17,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; +import java.util.Calendar; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -502,8 +503,22 @@ if (anniversary != null) vCard.setAnniversary(anniversary); // BDAY - if (birthDay != null) + if (birthDay != null) { + if (vCardVersion == VCardVersion.V4_0) { - vCard.setBirthday(birthDay); + vCard.setBirthday(birthDay); + } else { + if (birthDay.getPartialDate() == null) { + vCard.setBirthday(birthDay); + } else { + Calendar c = Calendar.getInstance(); + c.clear(); + c.set(Calendar.YEAR, birthDay.getPartialDate().getYear() == null ? 1900 : birthDay.getPartialDate().getYear()); + c.set(Calendar.MONTH, birthDay.getPartialDate().getMonth() - 1); + c.set(Calendar.DAY_OF_MONTH, birthDay.getPartialDate().getDate()); + vCard.setBirthday(new Birthday(c.getTime())); + } + } + } // RELATED for (Related related : relations) Index: src/test/java/at/bitfire/vcard4android/ContactTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- src/test/java/at/bitfire/vcard4android/ContactTest.java (revision 4f3c2508b33309f95d1d0f01745f9741aa942c14) +++ src/test/java/at/bitfire/vcard4android/ContactTest.java (revision 8567d01ce5b63939e5868bc19f0102f028afd8bc) @@ -365,7 +365,8 @@ @Test public void testVCard4FieldsAsVCard3() throws IOException { Contact c = regenerate(parseContact("vcard4.vcf", null), VCardVersion.V3_0); - assertNull(c.birthDay); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US); + assertEquals("1900-04-16", dateFormat.format(c.birthDay.getDate())); } @Test
-
Please also make the changes in src/main/java/at/bitfire/vcard4android/Contact.java.
vcard.setBirthday runs into an exception, if vcard version is 3.0 and the birthday contains a PartialDate. -
@jofrax Are you sure that it throws an exception? Which kind of exception? In my tests, there’s only a validation warning and ez-vcard just doesn’t output the
BDAY
, which seems to be the best solution in this case. -
Hm. You’re right, but I’m sure, I got an exception - maybe it was in another version of ezvcard.
The vcard that is produced contains an empty BDAY.
VERSION:3.0 PRODID:ez-vcard 0.10.2 BDAY: END:VCARD ``` I'm not sure, if an empty BDAY is allowed in rfc. Also the given birthday will be lost completely when using VCard 3.0.