package com.fasterxml.jackson.databind.deser.jdk; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.OptBoolean; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.exc.MismatchedInputException; import com.fasterxml.jackson.databind.exc.InvalidFormatException; import com.fasterxml.jackson.databind.util.ISO8601DateFormat; public class DateDeserializationTest extends BaseMapTest { static class DateAsStringBean { @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="/yyyy/MM/dd/") public Date date; } static class DateAsStringBeanGermany { @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="/yyyy/MM/dd/", locale="fr_FR") public Date date; } static class CalendarAsStringBean { @JsonFormat(shape=JsonFormat.Shape.STRING, pattern=";yyyy/MM/dd;") public Calendar cal; } static class DateInCETBean { @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd,HH", timezone="CET") public Date date; } static class CalendarBean { Calendar _v; void setV(Calendar v) { _v = v; } } static class LenientCalendarBean { @JsonFormat(lenient=OptBoolean.TRUE) public Calendar value; } static class StrictCalendarBean { @JsonFormat(lenient=OptBoolean.FALSE) public Calendar value; } /* /********************************************************** /* Unit tests /********************************************************** */ private final ObjectMapper MAPPER = new ObjectMapper(); public void testDateUtil() throws Exception { long now = 123456789L; java.util.Date value = new java.util.Date(now); // First from long assertEquals(value, MAPPER.readValue(""+now, java.util.Date.class)); // then from String String dateStr = dateToString(value); java.util.Date result = MAPPER.readValue("\""+dateStr+"\"", java.util.Date.class); assertEquals("Date: expect "+value+" ("+value.getTime()+"), got "+result+" ("+result.getTime()+")", value.getTime(), result.getTime()); } public void testDateUtilWithStringTimestamp() throws Exception { long now = 1321992375446L; /* Should be ok to pass as JSON String, as long * as it is plain timestamp (all numbers, 64-bit) */ String json = quote(String.valueOf(now)); java.util.Date value = MAPPER.readValue(json, java.util.Date.class); assertEquals(now, value.getTime()); // #267: should handle negative timestamps too; like 12 hours before 1.1.1970 long before = - (24 * 3600 * 1000L); json = quote(String.valueOf(before)); value = MAPPER.readValue(json, java.util.Date.class); assertEquals(before, value.getTime()); } public void testDateUtilRFC1123() throws Exception { DateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); // let's use an arbitrary value... String inputStr = "Sat, 17 Jan 2009 06:13:58 +0000"; java.util.Date inputDate = fmt.parse(inputStr); assertEquals(inputDate, MAPPER.readValue("\""+inputStr+"\"", java.util.Date.class)); } public void testDateUtilRFC1123OnNonUSLocales() throws Exception { Locale old = Locale.getDefault(); Locale.setDefault(Locale.GERMAN); DateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); // let's use an arbitrary value... String inputStr = "Sat, 17 Jan 2009 06:13:58 +0000"; java.util.Date inputDate = fmt.parse(inputStr); assertEquals(inputDate, MAPPER.readValue("\""+inputStr+"\"", java.util.Date.class)); Locale.setDefault(old); } /** * ISO8601 is supported as well */ public void testDateUtilISO8601() throws Exception { /* let's use simple baseline value, arbitrary date in GMT, * using the standard notation */ String inputStr = "1972-12-28T00:00:00.000+0000"; Date inputDate = MAPPER.readValue("\""+inputStr+"\"", java.util.Date.class); Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); c.setTime(inputDate); assertEquals(1972, c.get(Calendar.YEAR)); assertEquals(Calendar.DECEMBER, c.get(Calendar.MONTH)); assertEquals(28, c.get(Calendar.DAY_OF_MONTH)); // And then the same, but using 'Z' as alias for +0000 (very common) inputStr = "1972-12-28T00:00:00.000Z"; inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class); c.setTime(inputDate); assertEquals(1972, c.get(Calendar.YEAR)); assertEquals(Calendar.DECEMBER, c.get(Calendar.MONTH)); assertEquals(28, c.get(Calendar.DAY_OF_MONTH)); // Same but using colon in timezone inputStr = "1972-12-28T00:00:00.000+00:00"; inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class); c.setTime(inputDate); assertEquals(1972, c.get(Calendar.YEAR)); assertEquals(Calendar.DECEMBER, c.get(Calendar.MONTH)); assertEquals(28, c.get(Calendar.DAY_OF_MONTH)); // Same but only passing hour difference as timezone inputStr = "1972-12-28T00:00:00.000+00"; inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class); c.setTime(inputDate); assertEquals(1972, c.get(Calendar.YEAR)); assertEquals(Calendar.DECEMBER, c.get(Calendar.MONTH)); assertEquals(28, c.get(Calendar.DAY_OF_MONTH)); inputStr = "1984-11-30T00:00:00.000Z"; inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class); c.setTime(inputDate); assertEquals(1984, c.get(Calendar.YEAR)); assertEquals(Calendar.NOVEMBER, c.get(Calendar.MONTH)); assertEquals(30, c.get(Calendar.DAY_OF_MONTH)); } // [Databind#570] public void testISO8601PartialMilliseconds() throws Exception { String inputStr; Date inputDate; Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); inputStr = "2014-10-03T18:00:00.6-05:00"; inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class); c.setTime(inputDate); assertEquals(2014, c.get(Calendar.YEAR)); assertEquals(Calendar.OCTOBER, c.get(Calendar.MONTH)); assertEquals(3, c.get(Calendar.DAY_OF_MONTH)); assertEquals(600, c.get(Calendar.MILLISECOND)); inputStr = "2014-10-03T18:00:00.61-05:00"; inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class); c.setTime(inputDate); assertEquals(2014, c.get(Calendar.YEAR)); assertEquals(Calendar.OCTOBER, c.get(Calendar.MONTH)); assertEquals(3, c.get(Calendar.DAY_OF_MONTH)); assertEquals(18 + 5, c.get(Calendar.HOUR_OF_DAY)); assertEquals(0, c.get(Calendar.MINUTE)); assertEquals(0, c.get(Calendar.SECOND)); assertEquals(610, c.get(Calendar.MILLISECOND)); inputStr = "1997-07-16T19:20:30.45+01:00"; inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class); c.setTime(inputDate); assertEquals(1997, c.get(Calendar.YEAR)); assertEquals(Calendar.JULY, c.get(Calendar.MONTH)); assertEquals(16, c.get(Calendar.DAY_OF_MONTH)); assertEquals(19 - 1, c.get(Calendar.HOUR_OF_DAY)); assertEquals(20, c.get(Calendar.MINUTE)); assertEquals(30, c.get(Calendar.SECOND)); assertEquals(450, c.get(Calendar.MILLISECOND)); // 14-Sep-2015, tatu: Colon for timezone offset is optional, verify inputStr = "1997-07-16T19:20:30.45+0100"; inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class); c.setTime(inputDate); assertEquals(1997, c.get(Calendar.YEAR)); assertEquals(Calendar.JULY, c.get(Calendar.MONTH)); assertEquals(16, c.get(Calendar.DAY_OF_MONTH)); assertEquals(19 - 1, c.get(Calendar.HOUR_OF_DAY)); assertEquals(20, c.get(Calendar.MINUTE)); assertEquals(30, c.get(Calendar.SECOND)); assertEquals(450, c.get(Calendar.MILLISECOND)); // plus may also just have hour part inputStr = "1997-07-16T19:20:30.45+01"; inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class); c.setTime(inputDate); assertEquals(1997, c.get(Calendar.YEAR)); assertEquals(Calendar.JULY, c.get(Calendar.MONTH)); assertEquals(16, c.get(Calendar.DAY_OF_MONTH)); assertEquals(19 - 1, c.get(Calendar.HOUR_OF_DAY)); assertEquals(20, c.get(Calendar.MINUTE)); assertEquals(30, c.get(Calendar.SECOND)); assertEquals(450, c.get(Calendar.MILLISECOND)); } public void testISO8601MissingSeconds() throws Exception { String inputStr; Date inputDate; Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); inputStr = "1997-07-16T19:20+01:00"; inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class); c.setTime(inputDate); assertEquals(1997, c.get(Calendar.YEAR)); assertEquals(Calendar.JULY, c.get(Calendar.MONTH)); assertEquals(16, c.get(Calendar.DAY_OF_MONTH)); assertEquals(19 - 1, c.get(Calendar.HOUR_OF_DAY)); assertEquals(0, c.get(Calendar.SECOND)); assertEquals(0, c.get(Calendar.MILLISECOND)); // 14-Sep-2015, tatu: Colon for timezone offset is optional, verify inputStr = "1997-07-16T19:20+0200"; inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class); c.setTime(inputDate); assertEquals(1997, c.get(Calendar.YEAR)); assertEquals(Calendar.JULY, c.get(Calendar.MONTH)); assertEquals(16, c.get(Calendar.DAY_OF_MONTH)); assertEquals(19 - 2, c.get(Calendar.HOUR_OF_DAY)); assertEquals(0, c.get(Calendar.SECOND)); assertEquals(0, c.get(Calendar.MILLISECOND)); // or just hour inputStr = "1997-07-16T19:20+04"; inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class); c.setTime(inputDate); assertEquals(1997, c.get(Calendar.YEAR)); assertEquals(Calendar.JULY, c.get(Calendar.MONTH)); assertEquals(16, c.get(Calendar.DAY_OF_MONTH)); assertEquals(19 - 4, c.get(Calendar.HOUR_OF_DAY)); assertEquals(0, c.get(Calendar.SECOND)); assertEquals(0, c.get(Calendar.MILLISECOND)); } public void testDateUtilISO8601NoTimezone() throws Exception { // Timezone itself is optional as well... String inputStr = "1984-11-13T00:00:09"; Date inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class); Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); c.setTime(inputDate); assertEquals(1984, c.get(Calendar.YEAR)); assertEquals(Calendar.NOVEMBER, c.get(Calendar.MONTH)); assertEquals(13, c.get(Calendar.DAY_OF_MONTH)); assertEquals(0, c.get(Calendar.HOUR_OF_DAY)); assertEquals(0, c.get(Calendar.MINUTE)); assertEquals(9, c.get(Calendar.SECOND)); assertEquals(0, c.get(Calendar.MILLISECOND)); } // [databind#338] public void testDateUtilISO8601NoMilliseconds() throws Exception { final String INPUT_STR = "2013-10-31T17:27:00"; Date inputDate; Calendar c; inputDate = MAPPER.readValue(quote(INPUT_STR), java.util.Date.class); c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); c.setTime(inputDate); assertEquals(2013, c.get(Calendar.YEAR)); assertEquals(Calendar.OCTOBER, c.get(Calendar.MONTH)); assertEquals(31, c.get(Calendar.DAY_OF_MONTH)); assertEquals(17, c.get(Calendar.HOUR_OF_DAY)); assertEquals(27, c.get(Calendar.MINUTE)); assertEquals(0, c.get(Calendar.SECOND)); assertEquals(0, c.get(Calendar.MILLISECOND)); // 03-Nov-2013, tatu: This wouldn't work, and is the nominal reason // for #338 I think /* inputDate = ISO8601Utils.parse(INPUT_STR, new ParsePosition(0)); c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); c.setTime(inputDate); assertEquals(2013, c.get(Calendar.YEAR)); assertEquals(Calendar.OCTOBER, c.get(Calendar.MONTH)); assertEquals(31, c.get(Calendar.DAY_OF_MONTH)); assertEquals(17, c.get(Calendar.HOUR_OF_DAY)); assertEquals(27, c.get(Calendar.MINUTE)); assertEquals(0, c.get(Calendar.SECOND)); assertEquals(0, c.get(Calendar.MILLISECOND)); */ } public void testDateUtilISO8601JustDate() throws Exception { // Plain date (no time) String inputStr = "1972-12-28"; Date inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class); Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); c.setTime(inputDate); assertEquals(1972, c.get(Calendar.YEAR)); assertEquals(Calendar.DECEMBER, c.get(Calendar.MONTH)); assertEquals(28, c.get(Calendar.DAY_OF_MONTH)); } @SuppressWarnings("deprecation") public void testDateSql() throws Exception { java.sql.Date value = new java.sql.Date(0L); value.setYear(99); // 1999 value.setDate(19); value.setMonth(Calendar.APRIL); long now = value.getTime(); // First from long assertEquals(value, MAPPER.readValue(String.valueOf(now), java.sql.Date.class)); // then from default java.sql.Date String serialization: java.sql.Date result = MAPPER.readValue(quote(value.toString()), java.sql.Date.class); Calendar c = gmtCalendar(result.getTime()); assertEquals(1999, c.get(Calendar.YEAR)); assertEquals(Calendar.APRIL, c.get(Calendar.MONTH)); assertEquals(19, c.get(Calendar.DAY_OF_MONTH)); /* [JACKSON-200]: looks like we better add support for regular date * formats as well */ String expStr = "1981-07-13"; result = MAPPER.readValue(quote(expStr), java.sql.Date.class); c.setTimeInMillis(result.getTime()); assertEquals(1981, c.get(Calendar.YEAR)); assertEquals(Calendar.JULY, c.get(Calendar.MONTH)); assertEquals(13, c.get(Calendar.DAY_OF_MONTH)); /* 20-Nov-2009, tatus: I'll be damned if I understand why string serialization * is off-by-one, but day-of-month does seem to be one less. My guess is * that something is funky with timezones (i.e. somewhere local TZ is * being used), but just can't resolve it. Hence, need to comment this: */ // assertEquals(expStr, result.toString()); } public void testCalendar() throws Exception { // not ideal, to use (ever-changing) current date, but... java.util.Calendar value = Calendar.getInstance(); long l = 12345678L; value.setTimeInMillis(l); // First from long Calendar result = MAPPER.readValue(""+l, Calendar.class); assertEquals(l, result.getTimeInMillis()); // Then from serialized String String dateStr = dateToString(new Date(l)); result = MAPPER.readValue(quote(dateStr), Calendar.class); // note: representation may differ (wrt timezone etc), but underlying value must remain the same: assertEquals(l, result.getTimeInMillis()); } public void testCustom() throws Exception { final ObjectMapper mapper = new ObjectMapper(); DateFormat df = new SimpleDateFormat("yyyy-MM-dd'X'HH:mm:ss"); df.setTimeZone(TimeZone.getTimeZone("PST")); mapper.setDateFormat(df); String dateStr = "1972-12-28X15:45:00"; java.util.Date exp = df.parse(dateStr); java.util.Date result = mapper.readValue("\""+dateStr+"\"", java.util.Date.class); assertEquals(exp, result); } /** * Test for [JACKSON-203]: make empty Strings deserialize as nulls by default, * without need to turn on feature (which may be added in future) */ public void testDatesWithEmptyStrings() throws Exception { assertNull(MAPPER.readValue(quote(""), java.util.Date.class)); assertNull(MAPPER.readValue(quote(""), java.util.Calendar.class)); assertNull(MAPPER.readValue(quote(""), java.sql.Date.class)); } public void test8601DateTimeNoMilliSecs() throws Exception { // ok, Zebra, no milliseconds for (String inputStr : new String[] { "2010-06-28T23:34:22Z", "2010-06-28T23:34:22+0000", "2010-06-28T23:34:22+00:00", "2010-06-28T23:34:22+00", }) { Date inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class); Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); c.setTime(inputDate); assertEquals(2010, c.get(Calendar.YEAR)); assertEquals(Calendar.JUNE, c.get(Calendar.MONTH)); assertEquals(28, c.get(Calendar.DAY_OF_MONTH)); assertEquals(23, c.get(Calendar.HOUR_OF_DAY)); assertEquals(34, c.get(Calendar.MINUTE)); assertEquals(22, c.get(Calendar.SECOND)); assertEquals(0, c.get(Calendar.MILLISECOND)); } } public void testTimeZone() throws Exception { TimeZone result = MAPPER.readValue(quote("PST"), TimeZone.class); assertEquals("PST", result.getID()); } public void testCustomDateWithAnnotation() throws Exception { final String INPUT = "{\"date\":\"/2005/05/25/\"}"; DateAsStringBean result = MAPPER.readValue(INPUT, DateAsStringBean.class); assertNotNull(result); assertNotNull(result.date); Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); long l = result.date.getTime(); if (l == 0L) { fail("Should not get null date"); } c.setTimeInMillis(l); assertEquals(2005, c.get(Calendar.YEAR)); assertEquals(Calendar.MAY, c.get(Calendar.MONTH)); assertEquals(25, c.get(Calendar.DAY_OF_MONTH)); // 27-Mar-2014, tatu: Let's verify that changing Locale won't break it; // either via context Locale result = MAPPER.readerFor(DateAsStringBean.class) .with(Locale.GERMANY) .readValue(INPUT); assertNotNull(result); assertNotNull(result.date); l = result.date.getTime(); if (l == 0L) { fail("Should not get null date"); } c.setTimeInMillis(l); assertEquals(2005, c.get(Calendar.YEAR)); assertEquals(Calendar.MAY, c.get(Calendar.MONTH)); assertEquals(25, c.get(Calendar.DAY_OF_MONTH)); // or, via annotations DateAsStringBeanGermany result2 = MAPPER.readerFor(DateAsStringBeanGermany.class).readValue(INPUT); assertNotNull(result2); assertNotNull(result2.date); l = result2.date.getTime(); if (l == 0L) { fail("Should not get null date"); } c.setTimeInMillis(l); assertEquals(2005, c.get(Calendar.YEAR)); assertEquals(Calendar.MAY, c.get(Calendar.MONTH)); assertEquals(25, c.get(Calendar.DAY_OF_MONTH)); } public void testCustomCalendarWithAnnotation() throws Exception { CalendarAsStringBean cbean = MAPPER.readValue("{\"cal\":\";2007/07/13;\"}", CalendarAsStringBean.class); assertNotNull(cbean); assertNotNull(cbean.cal); Calendar c = cbean.cal; assertEquals(2007, c.get(Calendar.YEAR)); assertEquals(Calendar.JULY, c.get(Calendar.MONTH)); assertEquals(13, c.get(Calendar.DAY_OF_MONTH)); } public void testCustomCalendarWithTimeZone() throws Exception { // And then with different TimeZone: CET is +01:00 from GMT -- read as CET DateInCETBean cet = MAPPER.readValue("{\"date\":\"2001-01-01,10\"}", DateInCETBean.class); Calendar c = Calendar.getInstance(getUTCTimeZone()); c.setTimeInMillis(cet.date.getTime()); // so, going to UTC/GMT should reduce hour by one assertEquals(2001, c.get(Calendar.YEAR)); assertEquals(Calendar.JANUARY, c.get(Calendar.MONTH)); assertEquals(1, c.get(Calendar.DAY_OF_MONTH)); assertEquals(9, c.get(Calendar.HOUR_OF_DAY)); } // Based on an external report; was throwing an exception for second case here public void testISO8601Directly() throws Exception { final String TIME_STR = "2015-01-21T08:56:13.533+0000"; Date d = MAPPER.readValue(quote(TIME_STR), Date.class); assertNotNull(d); ISO8601DateFormat f = new ISO8601DateFormat(); Date d2 = f.parse(TIME_STR); assertNotNull(d2); assertEquals(d.getTime(), d2.getTime()); } /* /********************************************************** /* Test(s) for array unwrapping /********************************************************** */ public void testCalendarArrayUnwrap() throws Exception { ObjectReader reader = new ObjectMapper() .readerFor(CalendarBean.class) .without(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); final String inputDate = "1972-12-28T00:00:00.000+0000"; final String input = aposToQuotes("{'v':['"+inputDate+"']}"); try { reader.readValue(input); fail("Did not throw exception when reading a value from a single value array with the UNWRAP_SINGLE_VALUE_ARRAYS feature disabled"); } catch (MismatchedInputException exp) { verifyException(exp, "Can not deserialize"); verifyException(exp, "out of START_ARRAY"); } reader = reader.with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); CalendarBean bean = reader.readValue(input); assertNotNull(bean._v); assertEquals(1972, bean._v.get(Calendar.YEAR)); // and finally, a fail due to multiple values: try { reader.readValue(aposToQuotes("{'v':['"+inputDate+"','"+inputDate+"']}")); fail("Did not throw exception while reading a value from a multi value array with UNWRAP_SINGLE_VALUE_ARRAY feature enabled"); } catch (JsonMappingException exp) { verifyException(exp, "Attempted to unwrap"); } } /* /********************************************************** /* Tests for leniency /********************************************************** */ public void testLenientCalendar() throws Exception { final String JSON = aposToQuotes("{'value':'2015-11-32'}"); // with lenient, can parse fine LenientCalendarBean lenBean = MAPPER.readValue(JSON, LenientCalendarBean.class); assertEquals(Calendar.DECEMBER, lenBean.value.get(Calendar.MONTH)); assertEquals(2, lenBean.value.get(Calendar.DAY_OF_MONTH)); // with strict, ought to produce exception try { MAPPER.readValue(JSON, StrictCalendarBean.class); fail("Should not pass with invalid (with strict) date value"); } catch (MismatchedInputException e) { verifyException(e, "Can not deserialize value of type java.util.Calendar"); verifyException(e, "from String \"2015-11-32\""); verifyException(e, "expected format"); } // similarly with Date... ObjectMapper mapper = new ObjectMapper(); mapper.configOverride(java.util.Date.class) .setFormat(JsonFormat.Value.forLeniency(Boolean.FALSE)); try { mapper.readValue(quote("2015-11-32"), java.util.Date.class); fail("Should not pass with invalid (with strict) date value"); } catch (MismatchedInputException e) { verifyException(e, "Can not deserialize value of type java.util.Date"); verifyException(e, "from String \"2015-11-32\""); verifyException(e, "expected format"); } } /* /********************************************************** /* Tests to verify failing cases /********************************************************** */ public void testInvalidFormat() throws Exception { try { MAPPER.readValue(quote("foobar"), Date.class); fail("Should have failed with an exception"); } catch (InvalidFormatException e) { verifyException(e, "Can not deserialize value of type java.util.Date from String"); assertEquals("foobar", e.getValue()); assertEquals(Date.class, e.getTargetType()); } catch (Exception e) { fail("Wrong type of exception ("+e.getClass().getName()+"), should get " +InvalidFormatException.class.getName()); } } /* /********************************************************** /* Helper methods /********************************************************** */ private String dateToString(java.util.Date value) { /* Then from String. This is bit tricky, since JDK does not really * suggest a 'standard' format. So let's try using something... */ DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); return df.format(value); } private static Calendar gmtCalendar(long time) { Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); c.setTimeInMillis(time); return c; } }