0

I'm using org.apache.cxf.xjc.runtime.DataTypeAdapter.parseDate(StringDate) to parse the input string date from XML to JAVA date. But i got a Date objects with day-1 value.

For example

<birth>1939-11-19+01:00</birth>

I want to ignore the offset. I want only the date, so no matter what the offset is, I should get

Sun Nov 19 00:00:00 CET 1939

But I do get:

Sat Nov 18 23:00:00 CET 1939

The issue is at the level of date's time offset. I tried to fix it with ignoring the time offset but that didn't work. I'm using java 6 (constraint project). Any ideas?

My best attempt at a minimal, complete and verifiable example:

public static Date parse(final String str) {
    Calendar c = DatatypeConverter.parseDateTime(str);
    System.out.println(str + "\t" + c.getTime().getTime() / 1000);
    return c.getTime();
}
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
hoss
  • 113
  • 5
  • Possible duplicate of [How to convert String to Date with TimeZone?](https://stackoverflow.com/questions/48418975/how-to-convert-string-to-date-with-timezone) – Ole V.V. Feb 07 '18 at 10:13
  • If you only want the date, compared to the linked question you can do it a bit simpler: `LocalDate.parse("1939-11-19+01:00", DateTimeFormatter.ISO_OFFSET_DATE)`. – Ole V.V. Feb 07 '18 at 10:15
  • I have a constraint to use java 6 – hoss Feb 07 '18 at 10:20
  • Poor you. :-) Can you add [ThreeTen Backport](http://www.threeten.org/threetenbp/) to your project? Then it will work in Java 6 too. – Ole V.V. Feb 07 '18 at 10:21
  • There are some known issues. The Hijrah calendar system does not work. Formatting and parsing often depends on data only available in Java SE 8. Zone id and text parsing is significantly less powerful. – hoss Feb 07 '18 at 10:43
  • Moreover, i'm using Ant for dependency management – hoss Feb 07 '18 at 10:45
  • You may still use the backport for this isolated problem (I can’t believe Ant can‘t handle it). Alternatively `javax.xml.bind.DatatypeConverter` (built into Java 6) can parse your date string into a `GregorianCalendar` (this is also what CXF is using behind the scenes). This might be a way through too. – Ole V.V. Feb 07 '18 at 11:26
  • Funny, when on my Java 8 I emulate what the CXF method does (according to [grepcode](http://grepcode.com/file/repo1.maven.org/maven2/org.apache.cxf.xjc-utils/cxf-xjc-runtime/2.6.0/org/apache/cxf/xjc/runtime/DataTypeAdapter.java#DataTypeAdapter.parseDate%28java.lang.String%29)), I get `Sun Nov 19 00:00:00 CET 1939`. – Ole V.V. Feb 07 '18 at 11:51
  • Since I could not reproduce, you should probably give us [a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve). Also, what would be your desired outcome if the offset from XML was `-01:00`? If it was `+03:00`? Would you want `Sun Nov 19 00:00:00 CET 1939` in both cases, or would you want the time to be different? – Ole V.V. Feb 07 '18 at 12:06
  • I want to ignore completely the offset, i want only the date Sun Nov 19 00:00:00 CET 1939 in both cases. – hoss Feb 07 '18 at 13:02
  • public static Date parse(final String str) { Calendar c = DatatypeConverter.parseDateTime(str); System.out.println(str + "\t" + (c.getTime().getTime()/1000)); return c.getTime(); } – hoss Feb 07 '18 at 13:04
  • I have reproduced your observed behaviour after setting my JVM’s default time zone to Europe/Monaco. Europe/Monaco and my usual time zone, Europe/Copenhagen, both print as `CET`, but Monaco was at offset 0 from UTC on that date in 1939, while Copenhagen was at +01, hence the different behaviour. – Ole V.V. Feb 08 '18 at 08:02
  • Have you an idea about how to install threeTen backport using Ant ? – hoss Feb 08 '18 at 08:31
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/164735/discussion-between-ole-v-v-and-hoss). – Ole V.V. Feb 08 '18 at 08:43

1 Answers1

1

java.time

    String birth = "1939-11-19+01:00";
    LocalDate date = LocalDate.parse(birth, DateTimeFormatter.ISO_OFFSET_DATE);
    System.out.println(date);

This prints

1939-11-19

A java.time.LocalDate is a date without time-of-day, so this matches your requirements exactly. At the same time java.util.Date and friends are long outdated and have proven poorly designed. java.time, the modern Java date and time API, is so much nicer to work with. So my first recommendation is you stop here.

If you do need a Date, for example for a legacy API, first realize that despite the name it does not represent a date. It represents a point in time, and at that point in time the date will not be the same everywhere. This probably caused your problem, as you are aware: parseDate and parseDateTime parse your string into 00:00 at the specified offset from UTC on the date in question. At this point in time, the date may not be the same in your time zone, so when you print the Date you get, thereby invoking its toString method, you may get a different date. I understand that you want the time at 0:00 in your own time zone instead (CET, probably Central European Time, in turn a name of several different time zones that may be at offsets 0 or +01 on that date in 1939; the result you observed would make sense in time zones Europe/Paris, Africa/Ceuta, Europe/Andorra, Europe/Madrid and Europe/Monaco, all of which were at offset 0). To convert:

    Date oldfashionedDateObject 
            = Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant());
    System.out.println(oldfashionedDateObject);

This printed

Sun Nov 19 00:00:00 CET 1939

I tested with my time zone set to America/New_York and to Europe/Moscow, and always got Sun Nov 19 00:00:00, only in different time zones, EST and MSK.

Java 6 and ThreeTen Backport

I understand that you have to use Java 6. java.time has been backported to Java 6 and 7 in the ThreeTen Backport. So get this from the link at the bottom and add it to your project, and the above code will work except the conversion happens a little differently:

    Date oldfashionedDateObject
            = DateTimeUtils.toDate(date.atStartOfDay(ZoneId.systemDefault()).toInstant());

You don’t need to rewrite all of your code to use the ThreeTen Backport, and especially not if there are parts where you don’t trust the modern API to behave the way you want. The above snippet can coexist nicely with the code you already have.

The really really old-fashioned way

If you really insist, the way I could find to get the Date you want using DatatypeConverter and Calendar:

    Calendar calWithParsedOffset = DatatypeConverter.parseDate(birth);
    Calendar calWithMyTimeZone = Calendar.getInstance(TimeZone.getDefault());
    calWithMyTimeZone.clear();
    calWithMyTimeZone.set(calWithParsedOffset.get(Calendar.YEAR),
            calWithParsedOffset.get(Calendar.MONTH),
            calWithParsedOffset.get(Calendar.DAY_OF_MONTH));
    Date oldfashionedDateObject = calWithMyTimeZone.getTime();
    System.out.println(oldfashionedDateObject);

We can’t just change the offset or time zone of the Calendar we got from parsing since this will not change its point in time and therefore not the Date object we get. Instead we need to set a different Calendar object to the year, month and day-of-month of the first Calendar. The call to clear() sets hours, minutes, seconds and milliseconds to 0. This snippet too has been tested with my JVM’s time zone set to America/New_York and to Europe/Moscow.

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161