Hot questions for Using Joda-Time in date conversion

Top Java Programmings / Joda-Time / date conversion

Question:

I got pretty straight forward DateConverter with method that converts String to org.joda.time.DateTime

public class DateConverter {
    private static final DateTimeFormatter DATE_TIME_FORMATTER =
            DateTimeFormat
                    .forPattern( "yyyy-MM-dd HH:mm:ss Z")
                    .withZone( DateTimeZone.forID("Europe/Warsaw") );

    public static DateTime toDateTime(String value) {
        return DateTime.parse(value, DATE_TIME_FORMATTER);
    }
}

Now to test it,

 String okDate = "2018-10-28 00:00:00 +0200";
 String wrongDate = "2018-10-29 00:00:00 +0200";        

 System.out.println("Ok result: " + DateConverter.toDateTime(okDate));
 System.out.println("Wrong result: " + DateConverter.toDateTime(wrongDate));

Which leaves me with output:

Ok result: 2018-10-28T00:00:00.000+02:00 Wrong result: 2018-10-28T23:00:00.000+01:00

All days till end of the month, 29th, 30th, 31th are like 28th. Rest of days are ok. Can someone help me understand what is going on here? Where is the misstake I made?


Answer:

Uh. You specified a timezone with a DST change at that date, so works as intended.

If you want to enter local datetime, you don't want the Z in your format.

Question:

When I run the following code snippet:

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;

public class TimestampTest {

    public static void main(String... args) throws Exception {
        Date aDate1880 = new Date(-20, 0, 1);
        Timestamp timestamp = new Timestamp(aDate1880.getTime());
        DateTime dateTime = new DateTime(timestamp).withZoneRetainFields(DateTimeZone.getDefault());

        System.out.println(dateTime.year().get());

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(timestamp);

        System.out.println(calendar.get(Calendar.YEAR));
    }
}

I get the following output:

1879 1880

As aDate1880 does actually represents year 1880 I don't know why Joda DateTime gives year as 1879. Any help would be appreciated.


Answer:

This code:

Date aDate1880 = new Date(-20, 0, 1);

Creates a Date equivalent to January 1st 1880. As the time fields were not provided, they are set to midnight at the JVM default timezone. In my case, it's "America/Sao_Paulo", so the results below might be different in your case - but it'd explain what's happening anyway. In my case, the date created is:

Thu Jan 01 00:00:00 BRT 1880

(January 1st 1880 at midnight in America/Sao_Paulo timezone - BRT means "Brazilian Time")

Calling aDate1880.getTime() returns the value -2840130000000, which corresponds in UTC to 1880-01-01T03:00:00Z.

When converting to DateTime, it uses joda-time internal timezone data, an in my case the DateTime is:

1879-12-31T23:53:32.000-03:06:28

That's because before 1900, many countries didn't use current timezone rules, and most cities had their own local time. And most offsets were calculated based on the longitude, and that's why it has such strange results like "-03:06:28".

Probably your timezone has a similar issue.

Note: new DateTime(timestamp) already uses the default timezone, so the call to withZoneRetainFiels is redundant:

DateTime dateTime = new DateTime(timestamp);
// this prints "true"
System.out.println(dateTime.equals(dateTime.withZoneRetainFields(DateTimeZone.getDefault())));

To create the date you want, you can use only Joda-Time classes:

// January 1st 1880, at midnight in JVM default timezone
DateTime d = new LocalDate(1880, 1, 1).toDateTimeAtStartOfDay();

Question:

I understand this question could look like FAQ subject but critical things here is time zone and performance. I have integer YYYYMMDD date (20150131 is example). Here is good 'almost working' solution:

import org.joda.time.DateTime;
import java.util.Date;

// ...

public Date extract(final int intDate) {
    Date result = null;

    try {
        result = new DateTime(
                intDate / 10000,
                (intDate / 100) % 100,
                intDate % 100,
                0,
                0,
                0,
                0).toDate();

    } catch (final IllegalArgumentException e) {
        // Log failure
    }
    return result;
}

'almost' is because having 0x0126810d and EET (UTC+2 / +3 when DST) time zone I receive:

java.lang.IllegalArgumentException: Illegal instant due to time zone offset transition: 1930-06-20T22:00:00.000

At least using JODA 1.6. I cannot switch easily. But I'd like it to be 1930-06-21T00:00:00.000+02:00 and I don't care about UTC representation.

  1. Is it possible at all (can java.util.date store such date)?
  2. OK, any better high performance way to achieve this (JODA is just remedy here, not critical)?

Yes, I understand this time does not exist:

roman@node4:$ zdump -v Europe/Kiev | grep 1930
Europe/Kiev  Fri Jun 20 21:59:59 1930 UTC = Fri Jun 20 23:59:59 1930 EET isdst=0 gmtoff=7200
Europe/Kiev  Fri Jun 20 22:00:00 1930 UTC = Sat Jun 21 01:00:00 1930 MSK isdst=0 gmtoff=10800

Answer:

java.time and LocalDate

No matter if using Joda-Time (as in your question) or java.time, the modern Java date and time API, I believe that the solution to your problem is using LocalDate. I suggest that you simply stick to this and neither use org.joda.time.DateTime nor java.util.Date. In particular not the latter, it was always poorly designed and is now long outdated.

I am presenting to ways.

    int intDate = 19300621; // 0x0126810d

    String asString = String.valueOf(intDate);
    LocalDate date = LocalDate.parse(asString, DateTimeFormatter.BASIC_ISO_DATE);

    System.out.println(date);

1930-06-21

I find this code clearer to read than the code doing divisions and modulo operations. It’s not as efficient, but for more than 19 out of 20 cases this should be no concern. If you like the divisions, you can of course do them with java.time too:

    int year = intDate / 10000;
    int monthDay = intDate % 10000;
    int month = monthDay / 100;
    int day = monthDay % 100;
    LocalDate date = LocalDate.of(year, month, day);

If you do need a java.util.Date for a legacy API not yet upgraded to java. time (or Joda-Time), convert like this (no matter which of the above conversions you used):

    Instant asInstant = date.atStartOfDay(ZoneId.systemDefault()).toInstant();
    Date oldfashionedDate = Date.from(asInstant);
    System.out.println(oldfashionedDate);

Output when my default time zone is set to Europe/Zaporozhye:

Sat Jun 21 01:00:00 EET 1930

(We notice that we get 01:00:00 because of transition to summer time (DST). The time of 00:00:00 didn’t exist on this day in this time zone.)

If still using Joda-Time

If you are still using Joda-Time, your own answer using toDateTimeAtStartOfDay() is just fine.

PS I reproduced your problem with Joda-Time 2.9.9, my time zone set to Europe/Zaporozhye and your integer of 19300621 (don’t know why you gave it as hex, 0x0126810d). I got an exception similar to yours: org.joda.time.IllegalInstantException: Illegal instant due to time zone offset transition (daylight savings time 'gap'): 1930-06-21T00:00:00.000 (Europe/Zaporozhye).

Link

Oracle tutorial: Date Time explaining how to use java.time.