I'm receiving this timestamp in a json body:
{
"timestamp": "2019-03-27 10:04:01.446937+01"
}
And I would like to convert this timestamp into europe/brussels timezone.
I'm using com.fasterxml.jackson.core so I'm wondering if this is possible with annotations in this class for example.
public class MyClass {
#JsonFormat(...)
Date timestamp;
}
If not how can this be achieved using plain java code?
ISO 8601
If possible, educate the publisher of your data about using standard ISO 8601 formats when exchanging date-time values textually. That means:
Using a T in the middle instead of a SPACE character.
Using hours with minutes in the offset, delimited by a COLON character, rather than abbreviating.
DateTimeFormatter
If switching to ISO 8601 is not possible, define a formatting pattern to match your input.
String input = "2019-03-27 10:04:01.446937+01" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd HH:mm:ss.SSSSSSx" ) ;
java.time.OffsetDateTime
Parse as an OffsetDateTime.
OffsetDateTime odt = OffsetDateTime.parse( input , f ) ;
odt.toString(): 2019-03-27T10:04:01.446937+01:00
ZonedDateTime
Apply your desired time zone.
ZoneId z = ZoneId.of( "Europe/Brussels" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
See this code run at Ideone.com.
zdt.toString(): 2019-03-27T10:04:01.446937+01:00[Europe/Brussels]
In this particular case, Brussels time is already using an offset of one hour ahead of UTC. So no change for the time-of-day from our original.
Extracts the "timestamp" field in the JSON object as String. Then use the java.text.SimpleDateFormat with the correct formatter to parse the string into java.util.Date object. Then change to the required timezone before displayed it back into JSON string.
Here's the plain Java solution. I guess there's more elegant way for this.
static {
// make sure your JVM's timezone won't be "combined" with the input's timezone.
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}
public class DateHandler extends StdDeserializer<Date> {
public DateHandler() {
this(null);
}
public DateHandler(Class<?> clazz) {
super(clazz);
}
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
String date = jsonParser.getText();
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSZ"); // or DateTimeFormatter.ISO_OFFSET_DATE_TIME
return sdf.parse(date+"00");
} catch (Exception e) {
log.error(e, e);
return null; // TODO throw new JsonParseException..
}
}
}
and instead of your #JsonFormat(...)
do #JsonDeserialize(using = DateHandler.class)
Now new ObjectMapper().readValue("{ \"timestamp\": \"2019-03-27 10:04:01.446937+01\"}", MyClass.class)
returns a date with +0100 offset as required.
Related
I have a situation where I need to convert one Java date time format into another but have been having just a bear of a time doing so. Been searching for solutions a long time and have tried many things that have just not worked but I'm at my wits end :(.
I have to convert from
yyyy-MM-dd'T'HH:mm:ss
to
MM/dd/yyyy HH:mm:ss
This is the last thing I've tried, but alas it has no effect at all at transforming the pattern:
private Instant convertInstantFormat(Instant incomingDate) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(AUTH_DATE_PATTERN)
.withZone(ZoneId.systemDefault());
return Instant.parse(formatter.format(incomingDate));
}
Where
AUTH_DATE_PATTERN = "MM/dd/yyyy HH:mm:ss";
incomingDate = 2021-10-22T06:39:13Z
and outgoing date = 2021-10-22T06:39:13Z
I'm sure this is probably just the most naive attempt.
I've tried standardizing the date format and then reformatting, but no go.
I'm just sort of out of steam.
As always, any and all help from this incredible community is tremendously appreciated!
UPDATE
I just wanted to point out that the input and output to this method are of type "Instant."
Apologies for not making this clear initially.
I have to convert from yyyy-MM-dd'T'HH:mm:ss to MM/dd/yyyy HH:mm:ss
Your incoming date time format is ISO_LOCAL_DATE_TIME.
String datetime = "2021-12-16T16:22:34";
LocalDateTime source = LocalDateTime.parse(datetime,DateTimeFormatter.ISO_LOCAL_DATE_TIME);
// desired output format
String AUTH_DATE_PATTERN = "MM/dd/yyyy HH:mm:ss";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(AUTH_DATE_PATTERN);
String output = source.format(formatter);
System.out.println(output);
prints
12/16/2021 16:22:34
If your incoming date is 2021-10-22T06:39:13Z that is a zoned date time and can be parsed-from/formatted-to using
DateTimeFormatter.ISO_ZONED_DATE_TIME.
tl;dr
Instant // Represents a point on the time line.
.parse( "2021-10-22T06:39:13Z" ) // Returns an `Instant` object. By default, parses text in standard ISO 8601 for at where `Z` means offset of zero.
.atOffset( ZoneOffset.UTC ) // Returns an `OffsetDateTime` object.
.format(
DateTimeFormatter.ofPattern( "MM/dd/uuuu HH:mm:ss" )
) // Returns a `String` object.
See this code run live at IdeOne.com.
10/22/2021 06:39:13
Details
You said:
incomingDate = 2021-10-22T06:39:13Z
Your formatting pattern fails to match your input string.
Your input string happens to comply with the ISO 8691 format used by default in Instant.parse. So no need to specify a formatting pattern.
Instant instant = Instant.parse( "2021-10-22T06:39:13Z" ) ;
The Z on the end means an offset of zero hours-minutes-seconds from the prime meridian of UTC.
You asked to generate text representing that moment in the format of MM/dd/yyyy HH:mm:ss. I recommend including an indicator of the offset or time zone. But it you insist on omitting that, read on.
Convert from the basic class Instant to the more flexible OffsetDateTime class.
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
Specify your formatting pattern.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "MM/dd/uuuu HH:mm:ss" ) ;
Generate your desired text.
String output = odt.format( f ) ;
To learn more, search Stack Overflow. These topics have already been addressed many times.
Append timezone 'z' info at the end of the format pattern otherwise parsing throws an exception.
Here's a working example.
Unit Test (Passing)
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class DateFormatConverterTest {
#Test
void convertDate() {
final String incomingDate = "2021-10-22T06:39:13Z";
final String expectedOutgoingDate = "2021/10/22T06:39:13Z";
String actualOutgoingDate = new DateFormatConverter().convertDate(incomingDate);
assertThat(actualOutgoingDate).isEqualTo(expectedOutgoingDate);
}
}
Implementation
import java.time.format.DateTimeFormatter;
public class DateFormatConverter {
private static final DateTimeFormatter INCOMING_DATE_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssz");
private static final DateTimeFormatter OUTGOING_DATE_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy/MM/dd'T'HH:mm:ssz");
String convertDate(String incoming) {
return OUTGOING_DATE_TIME_FORMAT.format(INCOMING_DATE_TIME_FORMAT.parse(incoming));
}
}
I need to convert this date "2021-09-27 16:32:36" into zulu format like this "yyyy-MM-dd'T'HH:mm:ss.SSS'Z".
tl;dr
"2021-09-27 16:32:36"
.replace( " " , "T" )
.concat( ".Z" )
2021-09-27T16:32:36Z
A fractional second of zero can be omitted under ISO 8601.
String manipulation
Usually I would recommend using java.time classes. But in your case the obvious solution is simple string manipulation, as suggested by Andy Turner.
String iso8601 = "2021-09-27 16:32:36".replace( " " , "T" ).concat( ".000Z" ) ;
I would recommend dropping the zero fractional second. The string would still comply with ISO 8601.
String iso8601 = "2021-09-27 16:32:36".replace( " " , "T" ).concat( "Z" ) ;
The resulting string 2021-09-27T16:32:36Z represents a moment as seen with an offset of zero hours-minutes-seconds ahead/behind UTC.
If you need to do further work, parse that as an Instant. Example: Instant.parse( iso8601 )
Time zone is crucial
The Zulu time that you are asking for defines a definite and precise point in time. The string you have got does not. If we don’t know its time zone, it may denote times in a span of more than 24 hours.
For this answer I am assuming that the time is in US Central time (America/Chicago).
The format you are asking for is ISO 8601.
java.time
Like the other answers I am recommending java.time, the modern Java date and time API, for all of your date and time work. It has good support for ISO 8601.
I am using this formatter for parsing your string:
private static final DateTimeFormatter PARSER
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss", Locale.ROOT);
Now the work goes like this:
ZoneId zone = ZoneId.of("America/Chicago");
String dateString = "2021-09-27 16:32:36";
ZonedDateTime dateTime = LocalDateTime.parse(dateString, PARSER).atZone(zone);
String isoZuluString = dateTime.withZoneSameInstant(ZoneOffset.UTC).toString();
System.out.println(isoZuluString);
Output is:
2021-09-27T21:32:36Z
It’s in ISO 8601 format and in Zulu time, so as far as I am concerned, we’re done. The milliseconds you asked for are not there. They were not in the original string either, and according to the ISO 8601 format they are not mandatory, so you should be fine. Only if you encounter a particularly picky service that requires a fraction of second in the string even when it is .000, use a formatter for producing it:
private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendPattern("'T'HH:mm:ss.SSSX")
.toFormatter(Locale.ROOT);
The formatter could have been written with a format pattern alone. I took this opportunity for demonstrating that we may reuse built-in formatters in our own to make it easier and safer to get ISO 8601 right. Format like this:
String isoZuluString = dateTime.withZoneSameInstant(ZoneOffset.UTC)
.format(FORMATTER);
2021-09-27T21:32:36.000Z
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
Example with printing on the console :
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class ZuluZulu {
public static void zuluFormatter(String localDateTime) {
String pattern = "yyyy-MM-dd HH:mm:ss";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
String s = localDateTime;
LocalDateTime dt = LocalDateTime.parse(s, formatter);
System.out.println("dateTime Simple Format without T = " + dt.format(formatter));
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS'Z'");
System.out.println("DateTime Zulu format = " + dt.format(formatter2));
}
public static void main(String[] args) {
zuluFormatter("2021-09-27 16:32:36");
}
}
Output :
dateTime Simple Format without T = 2021-09-27 16:32:36
DateTime Zulu format = 2021-09-27 16:32:36.000Z
this example is exactly what you need without printing on the console :
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class ZuluZulu {
public static String zuluFormatter(String localDateTime) {
String pattern = "yyyy-MM-dd HH:mm:ss";
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern(pattern);
String s = localDateTime;
LocalDateTime dt = LocalDateTime.parse(s, formatter);
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-
MM-dd HH:mm:ss.SSS'Z'");
return dt.format(formatter2);
}
public static void main(String[] args) {
System.out.println(zuluFormatter("2021-09-27 16:32:36"));
}
}
I have a java component to format the date that I retrieve. Here is my code:
Format formatter = new SimpleDateFormat("yyyyMMdd");
String s = "2019-04-23 06:57:00.0";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss.S");
try
{
Date date = simpleDateFormat.parse(s);
System.out.println("Formatter: "+formatter.format(date));
}
catch (ParseException ex)
{
System.out.println("Exception "+ex);
}
The code works great as long as the String s has the format "2019-04-23 06:57:00.0";
My Question is, how to tweak this code so it will work for below scenarios ex,
my s string may have values like
String s = "2019-04-23 06:57:00.0";
or
String s = "2019-04-23 06:57:00";
Or
String s = "2019-04-23";
right now it fails if I don't pass the ms.. Thanks!
Different types
String s = "2019-04-23 06:57:00";
String s = "2019-04-23";
These are two different kinds of information. One is a date with time-of-day, the other is simply a date. So you should be parsing each as different types of objects.
LocalDateTime.parse
To comply with the ISO 8601 standard format used by default in the LocalDateTime class, replace the SPACE in the middle with a T. I suggest you educate the publisher of your data about using only ISO 8601 formats when exchanging date-time values as text.
LocalDateTime ldt1 = LocalDateTime.parse( "2019-04-23 06:57:00".replace( " " , "T" ) ) ;
The fractional second parses by default as well.
LocalDateTime ldt2 = LocalDateTime.parse( "2019-04-23 06:57:00.0".replace( " " , "T" ) ) ;
See this code run live at IdeOne.com.
ldt1.toString(): 2019-04-23T06:57
ldt2.toString(): 2019-04-23T06:57
LocalDate.parse
Your date-only input already complies with ISO 8601.
LocalDate ld = LocalDate.parse( "2019-04-23" ) ;
See this code run live at IdeOne.com.
ld.toString(): 2019-04-23
Date with time-of-day
You can strip out the time-of-day from the date.
LocalDate ld = ldt.toLocalDate() ;
And you can add it back in.
LocalTime lt = LocalTime.parse( "06:57:00" ) ;
LocalDateTime ldt = ld.with( lt ) ;
Moment
However, be aware that a LocalDateTime does not represent a moment, is not a point on the timeline. Lacking the context of a time zone or offset-from-UTC, a LocalDateTime cannot hold a moment, as explained in its class JavaDoc.
For a moment, use the ZonedDateTime, OffsetDateTime, or Instant classes. Teach the publisher of your data to include the offset, preferably in UTC.
Avoid legacy date-time classes
The old classes SimpleDateFormat, Date, and Calendar are terrible, riddled with poor design choices, written by people not skilled in date-time handling. These were supplanted years ago by the modern java.time classes defined in JSR 310.
In case of you have optional parts in pattern you can use [ and ].
For example
public static Instant toInstant(final String timeStr){
final DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH[:mm[:ss[ SSSSSSSS]]]")
.withZone(ZoneId.of("UTC"));
try {
return Instant.from(formatter.parse(timeStr));
}catch (DateTimeException e){
final DateTimeFormatter formatter2 = DateTimeFormatter
.ofPattern("yyyy-MM-dd")
.withZone(ZoneId.of("UTC"));
return LocalDate.parse(timeStr, formatter2).atStartOfDay().atZone(ZoneId.of("UTC")).toInstant();
}
}
cover
yyyy-MM-dd
yyyy-MM-dd HH
yyyy-MM-dd HH:mm
yyyy-MM-dd HH:mm:ss
yyyy-MM-dd HH:mm:ss SSSSSSSS
I'm having trouble to figure out what is this date format: 2019-02-28T12:17:46.279+0000. I have tried different date formats to get this result but nothing worked. Closest pattern was: yyyy-MM-dd'T'HH:mm:ss.SSSZ But with this pattern output was like this: 2019-02-28T12:17:46.279-0000 (- is after seconds instead of +)
I get this exception:
Caused by: java.lang.IllegalArgumentException: 2019-02-28T12:17:46.279+0000
at org.apache.xerces.jaxp.datatype.XMLGregorianCalendarImpl$Parser.skip(XMLGregorianCalendarImpl.java:2932)
at org.apache.xerces.jaxp.datatype.XMLGregorianCalendarImpl$Parser.parse(XMLGregorianCalendarImpl.java:2898)
at org.apache.xerces.jaxp.datatype.XMLGregorianCalendarImpl.<init>(XMLGregorianCalendarImpl.java:478)
at org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl.newXMLGregorianCalendar(DatatypeFactoryImpl.java:230)
at __redirected.__DatatypeFactory.newXMLGregorianCalendar(__DatatypeFactory.java:132)
at javax.xml.bind.DatatypeConverterImpl.parseDate(DatatypeConverterImpl.java:519)
at javax.xml.bind.DatatypeConverter.parseDate(DatatypeConverter.java:431)
at eu.europa.ec.my.custom.package.model.mapper.XsdDateTimeConverter.unmarshal(XsdDateTimeConverter.java:23)
My XsdDateTimeConverter class looks like this:
public class XsdDateTimeConverter {
public static Date unmarshal(String dateTime) {
return DatatypeConverter.parseDate(dateTime).getTime();
}
public static String marshalDate(Date date) {
final Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return DatatypeConverter.printDate(calendar);
}
public static String marshalDateTime(Date dateTime) {
final Calendar calendar = Calendar.getInstance();
calendar.setTime(dateTime);
return DatatypeConverter.printDateTime(calendar);
}
}
And parsed date in my postgres db looks like this:
move_timestamp timestamp(6) with time zone
2019-02-28 12:17:46.279+00
In my rest method I use ObjectMapper like this.
MyCustomResponseDto responseDto = customService.getCustomResponseDto(query);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
String strValue = mapper.writeValueAsString(responseDto);
return Response.ok(strValue).build();
I guess what I really wanted is what is the right pattern for this date. I can go in this page: http://www.sdfonlinetester.info/ and enter my pattern (e.g. yyyy-MM-dd'T'HH:mm:ss.SSSZ) and it gives you an actual date output for that pattern. I need the other way around. I want to enter my date and it will give me the right pattern for it.
tl;dr
OffsetDateTime.parse(
"2019-02-28T12:17:46.279+0000" ,
DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" , Locale.ROOT )
)
java.time
You are using terrible Calendar class that was supplanted years ago by the java.time classes.
ISO 8601
Your input string is in standard ISO 8601 format, designed for human-readable machine-parseable textual representations of date-time values. That is a good thing.
The java.time classes use ISO 8601 formats by default when parsing/generating strings.
OffsetDateTime
You should be able to simply parse with OffsetDateTime.
OffsetDateTime.parse( "2019-02-28T12:17:46.279+0000" )
…but unfortunately the optional COLON character being omitted from the offset (+00:00) is a problem. The OffsetDateTime class has a minor bug where it refuses to parse without that character. The bug is discussed here and here.
The ISO 8601 standard permits the colon’s absence, but practically you should always include it. The OffsetDateTime class is not alone; I have seen other libraries that break when the colon or padding zeros are absent. I suggest asking the publisher of your data to use the full ISO 8601 format including the colon.
The workaround for the OffsetDateTime bug is to define a DateTimeFormatter explicitly.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" , Locale.ROOT ) ;
Then parse.
String input = "2019-02-28T12:17:46.279+0000" ;
OffsetDateTime odt = OffsetDateTime.parse( input , f ) ;
To generate text in full standard ISO 8601 format, simply call toString.
String output = odt.toString() ;
See this code run live at IdeOne.com.
output: 2019-02-28T12:17:46.279Z
The Z on the end means UTC, that is +0000 or +00:00. Pronounced “Zulu”. Very commonly used, more immediately readable than the numeric offset.
If you want same output format as your input, use the same DateTimeFormatter.
String output = odt.format( f ) ;
You may try below code
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.ENGLISH);
String lastmod = format.format(new Date());
Save yourself a mountain of trouble and save the epochtime in millis. Only parse and render dates in UIs. Very very few cases of scheduling for humans require a computer to know hours, day, week, month, year... But saving an instant in time is just a 'long'.
Tried all possible combinations, created a Custom JsonDeserializer class as well modified the DateFormat as per my need. But still no fruitful result.
But result always display date in 'EST' Sat Jan 01 14:08:56 EST 2000.
I want my date to be displayed in UTC or it should be more configurable as per the client needs.
public class CustomJsonDeserializerWithDateFormat extends JsonDeserializer<Date>{
#Override
public Date deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
TimeZone TZ = TimeZone.getTimeZone("US/Central");
dateFormat.setTimeZone(TZ);
dateFormat.getCalendar().setTimeZone(TZ);
Calendar newCalendar = new GregorianCalendar();
newCalendar.setTimeZone(TZ);
dateFormat.setCalendar(newCalendar);
//ZonedDateTime
System.out.println("Time Zone : "+dateFormat.getTimeZone());
JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_STRING) {
String value = p.getText().trim();
try {
Date formattedDate = dateFormat.parse(value);
DateFormat formatter = new SimpleDateFormat
("EEE MMM dd HH:mm:ss zzz yyyy");
TimeZone central = TimeZone.getTimeZone("America/Chicago");
formatter.setTimeZone(central);
Date fromDate = (Date)formatter.parse(formattedDate.toString());
System.out.println(fromDate);
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "EEE MMM dd HH:mm:ss z uuuu" , Locale.ENGLISH );
ZonedDateTime zdt = ZonedDateTime.parse ( formattedDate.toString() , f );
System.out.println(zdt.toString());
ZoneId z = ZoneId.of( "America/Chicago" );
ZonedDateTime zdtChicago = zdt.withZoneSameInstant( z );
System.out.println(zdtChicago.toString());
return fromDate;
} catch (ParseException e) {
e.printStackTrace();
}
}
return null;
}
}
If you have the new java.time classes available, there's no need to mix them with old SimpleDateFormat. To parse the input 2000-01-01T12:08:56.235-0700, just use a java.time.format.DateTimeFormatter and parse it to a java.time.OffsetDateTime - the best choice for this case, as it represents a date and time (2000-01-01T12:08:56.235) in a specific offset (-0700).
Then you convert it to a java.util.Date, using the from method. Your deserializer will be as simple as this:
public class CustomJsonDeserializerWithDateFormat extends JsonDeserializer<Date> {
// will deserialize inputs in the format "2000-01-01T12:08:56.235-0700"
private DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXX");
#Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// parse input
OffsetDateTime odt = OffsetDateTime.parse(p.getText(), fmt);
// convert to java.util.Date
return Date.from(odt.toInstant());
}
}
I also recreated your class and tested with some sample data:
public class RuntimePropertyData {
#JsonProperty("begin-date")
#JsonDeserialize(using = CustomJsonDeserializerWithDateFormat.class)
private Date beginDate;
// getter and setter
}
ObjectMapper om = new ObjectMapper();
Map<String, String> map = new HashMap<>();
map.put("begin-date", "2000-01-01T12:08:56.235-0700");
RuntimePropertyData data = om.convertValue(map, RuntimePropertyData.class);
System.out.println(data.getBeginDate());
Now that's the tricky part. A java.util.Date doesn't have any timezone information. This class has only one value: the number of milliseconds since unix epoch (1970-01-01T00:00Z, or "January 1st 1970 at midnight in UTC"). It simply represents an instant, a specific point in time.
When I System.out.println a Date (or when I log it, or check its value in a debugger), it implicity calls the toString() method, and it converts the date to the JVM default timezone. So, when you see Sat Jan 01 14:08:56 EST 2000, that's the result of Date's toString().
If you want to change the format when serializing the date, you can annotate your field with #JsonFormat, and also set the desired timezone:
#JsonProperty("begin-date")
#JsonDeserialize(using = CustomJsonDeserializerWithDateFormat.class)
#JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXX", timezone = "UTC")
private Date beginDate;
Then, if I serialize it:
// data is the same RuntimePropertyData I've got from the code above
System.out.println(om.writeValueAsString(data));
This will produce the following JSON (with the date in UTC):
{"begin-date":"2000-01-01T19:08:56.235Z"}
To change the timezone, you can also set in the ObjectMapper. In this case, I must remove it from the annotation:
#JsonProperty("begin-date")
#JsonDeserialize(using = CustomJsonDeserializerWithDateFormat.class)
// just the format, without timezone
#JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXX")
private Date beginDate;
And then I set the timezone in the mapper:
om.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
System.out.println(om.writeValueAsString(data));
Now the output is:
{"begin-date":"2000-01-01T13:08:56.235-0600"}
Unfortunately, if I set the timezone again, it doesn't change the output anymore (I'm using Jackson 2.8.8). Apparently, the timezone used in the first serialization is kept, and further modifications have no effect - so, if you want a highly customized output (where the timezone can always change), the solution is to always create a new ObjectMapper.