public static Calendar toCalendar(String date) throws IOException
    {
        Calendar retval = null;
        if ((date != null) && (!date.trim().isEmpty()))
        {
            date = date.trim();

            // these are the default values
            int month = 1;
            int day = 1;
            int hour = 0;
            int minute = 0;
            int second = 0;
            // first string off the prefix if it exists
            try
            {
                SimpleTimeZone zone = null;
                
                if (Pattern.matches("^\\d{4}-\\d{2}-\\d{2}T.*", date))
                {
                    // Assuming ISO860 date string
                    return fromISO8601(date);
                }
                else if (date.startsWith("D:"))
                {
                    date = date.substring(2);
                }

                date = date.replaceAll("[-:T]", "");

                if (date.length() < 4)
                {
                    throw new IOException("Error: Invalid date format '" + date + "'");
                }
                int year = Integer.parseInt(date.substring(0, 4));
                if (date.length() >= 6)
                {
                    month = Integer.parseInt(date.substring(4, 6));
                }
                if (date.length() >= 8)
                {
                    day = Integer.parseInt(date.substring(6, 8));
                }
                if (date.length() >= 10)
                {
                    hour = Integer.parseInt(date.substring(8, 10));
                }
                if (date.length() >= 12)
                {
                    minute = Integer.parseInt(date.substring(10, 12));
                }

                int timeZonePos = 12;
                if (date.length() - 12 > 5 || (date.length() - 12 == 3 && date.endsWith("Z")))
                {
                    second = Integer.parseInt(date.substring(12, 14));
                    timeZonePos = 14;
                }

                if (date.length() >= (timeZonePos + 1))
                {
                    char sign = date.charAt(timeZonePos);
                    if (sign == 'Z')
                    {
                        zone = new SimpleTimeZone(0, "Unknown");
                    }
                    else
                    {
                        int hours = 0;
                        int minutes = 0;
                        if (date.length() >= (timeZonePos + 3))
                        {
                            if (sign == '+')
                            {
                                // parseInt cannot handle the + sign
                                hours = Integer.parseInt(date.substring((timeZonePos + 1), (timeZonePos + 3)));
                            }
                            else
                            {
                                hours = -Integer.parseInt(date.substring(timeZonePos, (timeZonePos + 2)));
                            }
                        }
                        if (sign == '+')
                        {
                            if (date.length() >= (timeZonePos + 5))
                            {
                                minutes = Integer.parseInt(date.substring((timeZonePos + 3), (timeZonePos + 5)));
                            }
                        }
                        else
                        {
                            if (date.length() >= (timeZonePos + 4))
                            {
                                minutes = Integer.parseInt(date.substring((timeZonePos + 2), (timeZonePos + 4)));
                            }
                        }
                        zone = new SimpleTimeZone(hours * 60 * 60 * 1000 + minutes * 60 * 1000, "Unknown");
                    }
                }

                if (zone == null)
                {
                    retval = new GregorianCalendar();
                }
                else
                {
                    updateZoneId(zone);
                    retval = new GregorianCalendar(zone);
                }
                retval.clear();
                retval.set(year, month - 1, day, hour, minute, second);
            }
            catch (NumberFormatException e)
            {

                // remove the arbitrary : in the timezone. SimpleDateFormat can't handle it
                if (date.charAt(date.length() - 3) == ':' && 
                    (date.charAt(date.length() - 6) == '+' || date.charAt(date.length() - 6) == '-'))
                {
                    // that's a timezone string, remove the :
                    date = date.substring(0, date.length() - 3) + date.substring(date.length() - 2);
                }
                for (int i = 0; (retval == null) && (i < POTENTIAL_FORMATS.length); i++)
                {
                    try
                    {
                        Date utilDate = POTENTIAL_FORMATS[i].parse(date);
                        retval = new GregorianCalendar();
                        retval.setTime(utilDate);
                    }
                    catch (ParseException pe)
                    {
                        // ignore and move to next potential format
                    }
                }
                if (retval == null)
                {
                    // we didn't find a valid date format so throw an exception
                    throw new IOException("Error converting date:" + date, e);
                }
            }
        }
        return retval;
    }
@Test
    void testDateConversion() throws Exception
    {
        // Test partial dates
        Calendar convDate = DateConverter.toCalendar("2015-02-02");
        assertEquals(2015, convDate.get(Calendar.YEAR));

        //Test missing seconds
        assertEquals(DateConverter.toCalendar("2015-12-08T12:07:00-05:00"),
                     DateConverter.toCalendar("2015-12-08T12:07-05:00"));
        assertEquals(DateConverter.toCalendar("2011-11-20T10:09:00Z"),
                     DateConverter.toCalendar("2011-11-20T10:09Z"));
        
        // Test some time zone offsets
        String testString1 = "";
        String testString2 = "";

        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm[:ss][.SSS][XXX]");

        //Test missing seconds
        testString1 = "2015-12-08T12:07:00-05:00";
        testString2 = "2015-12-08T12:07-05:00";

        assertEquals(DateConverter.toCalendar(testString1), DateConverter.toCalendar(testString2));
        assertEquals(DateConverter.toCalendar(testString1).toInstant(),ZonedDateTime.parse(testString1, dateTimeFormatter).toInstant());
        assertEquals(DateConverter.toCalendar(testString2).toInstant(),ZonedDateTime.parse(testString2, dateTimeFormatter).toInstant());

        // Test some time zone offsets
        testString1 = "2015-02-02T16:37:19.192Z";
        testString2 = "2015-02-02T16:37:19.192Z";

        assertEquals(DateConverter.toCalendar(testString2).toInstant(),ZonedDateTime.parse(testString1, dateTimeFormatter).toInstant());

        testString1 = "2015-02-02T16:37:19.192+00:00";
        testString2 = "2015-02-02T16:37:19.192Z";

        assertEquals(DateConverter.toCalendar(testString2).toInstant(),ZonedDateTime.parse(testString1, dateTimeFormatter).toInstant());

        testString1 = "2015-02-02T16:37:19.192+02:00";
        testString2 = "2015-02-02T16:37:19.192+02:00";

        assertEquals(DateConverter.toCalendar(testString2).toInstant(),ZonedDateTime.parse(testString1, dateTimeFormatter).toInstant());

        testString1 = "2015-02-02T16:37:19.192Z";
        testString2 = "2015-02-02T08:37:19.192PST";

        assertEquals(DateConverter.toCalendar(testString2).toInstant(),ZonedDateTime.parse(testString1, dateTimeFormatter).toInstant());

        testString1 = "2015-02-02T16:37:19.192+01:00";
        testString2 = "2015-02-02T16:37:19.192Europe/Berlin";

        assertEquals(DateConverter.toCalendar(testString2).toInstant(),ZonedDateTime.parse(testString1, dateTimeFormatter).toInstant());

        // PDFBOX-4902: half-hour TZ
        testString1 = "2015-02-02T16:37:19.192+05:30";
        assertEquals(DateConverter.toCalendar(testString1).toInstant(),ZonedDateTime.parse(testString1, dateTimeFormatter).toInstant());

        testString1 = "2015-02-02T16:37:19.192-05:30";
        assertEquals(DateConverter.toCalendar(testString1).toInstant(),ZonedDateTime.parse(testString1, dateTimeFormatter).toInstant());

        testString1 = "2015-02-02T16:37:19.192+10:30";
        assertEquals(DateConverter.toCalendar(testString1).toInstant(),ZonedDateTime.parse(testString1, dateTimeFormatter).toInstant());
    }