Birthdays without year



  • It would be nice, if you support birthdays without a year in carddav with vcard 4.0.


  • developer

    @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


  • developer

    @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.


  • developer

    Seems like for format is really "--mm-dd":

    https://android.googlesource.com/platform/packages/apps/ContactsCommon/+/android-7.1.2_r11/tests/src/com/android/contacts/common/RawContactModifierTests.java#1052

            // 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.


  • developer

    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.


  • developer

    @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.


  • developer

    @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.


  • developer

    @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.

Log in to reply
 

Looks like your connection to Bitfire App Forums was lost, please wait while we try to reconnect.