Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change in timezone handling for Date created from Joda DateTime between 2.6.2 and 2.6.3 #76

Closed
philwebb opened this issue Oct 15, 2015 · 11 comments

Comments

@philwebb
Copy link

In 2.6.3, I'm seeing a DateTime that's in UTC move into BST (my timezone) when it's converted into a Date and written as a String. In 2.6.1 and 2.6.2 it stays in UTC. This code reproduces the problem:

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = createObjectMapper();
        DateTime dateTime = new DateTime(1988, 6, 25, 20, 30, DateTimeZone.UTC);
        System.out.println(objectMapper
                .writeValueAsString(dateTime));
        Date date = dateTime.toDate();
        System.out.println(objectMapper
                .writeValueAsString(date));
    }

    private static ObjectMapper createObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(createJodaModule());
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ZZZ"));
        return mapper;
    }

    private static SimpleModule createJodaModule() {
        SimpleModule module = new SimpleModule();
        module.addSerializer(DateTime.class, new DateTimeSerializer(
                new JacksonJodaDateFormat(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss ZZZ")
                        .withZoneUTC())));
            return module;
    }

The output for various versions of Jackson:

2.6.1

"1988-06-25 20:30:00 UTC"
"1988-06-25 20:30:00 +0000"

2.6.2

"1988-06-25 20:30:00 UTC"
"1988-06-25 20:30:00 +0000"

2.6.3

"1988-06-25 20:30:00 UTC"
"1988-06-25 21:30:00 +0100"

(originally raised by @wilkinsona in FasterXML/jackson-databind#973)

@fabianbuch
Copy link

I think this is related to #12 which says that Joda and JSR310 don't support mapper.setDateFormat whereas it's supported for java.util.Date

@islanderman
Copy link

I suppose your createJodaModule() still uses com.fasterxml.jackson.databind.ser.std.DateSerializer. Not quite sure why there has been a change of behavior since 2.6.3; perhaps there has been a change in databind that caused this. However the current behavior looks correct to me.

Since your createJodaModule() does not add serializer to Date.class, the code is essentially the same as below. You might want to reference: https://github.com/FasterXML/jackson-databind/blob/master/src/test/java/com/fasterxml/jackson/databind/ser/DateSerializationTest.java if you want to set TimeZone for java.util.Date serialization.

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ZZZ"));
        DateTime dateTime = new DateTime(1988, 6, 25, 20, 30, DateTimeZone.UTC);
        Date date = dateTime.toDate(); // this is your local time zone
        System.out.println(objectMapper.writeValueAsString(date));

@cowtowncoder
Copy link
Member

I don't know what could cause this, but I thought I remember one particular fix that might be relevant: one where the default timezone initialized and used varied between reading (deser) and writing (ser). But I can not find an issue at this point, or specific commit... so that's bit odd.

Anyway: one thing to double-check, just to be sure, is that 2.6.3 is used both for databind and joda module. I assume this is the case, but just want to be absolutely sure here.

It seems to be the code sample here is correct, and behavior not, so definitely looks like a bug. But I do want to isolate it.

@islanderman
Copy link

@cowtowncoder I suppose the code uses the default DateSerializer for Date.class from databind. Since the timezone is only set inside DateTime, not quite sure why the serialization on Date has to honor the timezone here.

@cowtowncoder
Copy link
Member

@islanderman java.util.Date does not contain any timezone information, so timezone to use has to come from somewhere. But now that you mention it, there is indeed no way for timezone from Joda DateTime to flow into java.util.Date, since while former has it, the latter not.

So maybe there is no way to actually retain it. There is just the question of which timezone should be used, for given configuration, when serializing java.util.Date instances.

@wilkinsona
Copy link

@cowtowncoder I can confirm that the version of every Jackson module was the same

@philwebb
Copy link
Author

Any progress on this? We're probably going to need to stick to Jackson 2.6.1 for Spring Boot 1.3.0.

@cowtowncoder
Copy link
Member

@philwebb Haven't had time to look into this, but I'll try to see what goes on here. As has been mentioned, Joda module doesn't seem involved at all because it's all about serializing java.util.Date; and second, java.util.Date has not timezone information so that simply can not be converted at all from Joda DateTime. So there is no way to retain that, at all; question then is just why is non-UTC timezone getting used by serialization with 2.6.3.

@cowtowncoder
Copy link
Member

@philwebb Ok, now I now know what is happening here, and why the change occurs. I think the trigger is the fix in 2.6.3 for:

FasterXML/jackson-databind#939

which stops TimeZone override which would have previously forced the use of ObjectMapper default TimeZone on SimpleDateFormat that is used.

This can not really be fixed by jackson-databind at this point, esp. for 2.6.x, but calling code can be changed to either:

  1. Call setTimeZone() on SimpleDateFormat to use the default; either directly (TimeZone.getTimeZone("UTC")), or via mapper.getSerializationConfig().getTimeZone()
  2. Explicitly configure timezone on ObjectMapper; this changes mapper to think there is no explicit timezone to force, not just implicit default.

So where does the issue come from? Basically, java.util.Date instance has no timezone information (which could be viewed as a bug, basically, and one of many reasons to use Joda or Java8 date/time), but both ObjectMapper and DateFormat do have it. So which one should be used? If user explicitly passes DateFormat, one suspects it may be set to indicate exact timezone to use, and should NOT be changed. Or perhaps user just constructed it, and does not care, so it SHOULD be overridden.
Alas, there is no way (that I know of) to know if user has specified TimeZone, because instances default to the local timezone, whatever it is (in your case, Central European Timezone it seems).

In the past, Jackson did force mapper's timezone on DateFormat; but it also had conflicting settings for the timezone between serialization and deserialization (alas). This is why your issue was not surfaced before.

Long story short: at this point it is best to explicitly set TimeZone on your DateFormat, to avoid issues. And, if at all possible, use Joda instead of JDK's broken java.util.Date.

Hope this helps!

@philwebb
Copy link
Author

@cowtowncoder Perfect! Thanks for the analysis. We can set the TimeZone on our side when we create the DateFormatter.

Have a good weekend 👍

@cowtowncoder
Copy link
Member

@philwebb No problem, thank you for following up. This is a problematic area but hopefully gets eventually cleared up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants