Hot questions for Using Joda-Time in date formatting

Top Java Programmings / Joda-Time / date formatting

Question:

My team is looking to switch from Joda time to java.time, but we're seeing different behavior in formatting using the same pattern. The issue arises when we're using the week-of-week-year w symbol:

final String dateString = "2016-01-04 00:00:00";
final String inputPattern = "yyyy-MM-dd HH:mm:ss";

// parse the input string using Joda
final org.joda.time.format.DateTimeFormatter jodaInputFormatter = org.joda.time.format.DateTimeFormat.forPattern(inputPattern);
final org.joda.time.DateTime jodaDateTime = jodaInputFormatter.parseDateTime(dateString);

// parse the input string using two different java.time classes
final java.time.format.DateTimeFormatter javaTimeInputFormatter = java.time.format.DateTimeFormatter.ofPattern(inputPattern).withZone(java.time.ZoneOffset.UTC);
final java.time.LocalDateTime localDateTime = java.time.LocalDateTime.parse(dateString, javaTimeInputFormatter);
final java.time.ZonedDateTime zonedDateTime = java.time.ZonedDateTime.parse(dateString, javaTimeInputFormatter);

final String outputPattern = "'week' w - dd/MM/yyyy HH:mm:ss";
final org.joda.time.format.DateTimeFormatter jodaOutputFormatter = org.joda.time.format.DateTimeFormat.forPattern(outputPattern);
final java.time.format.DateTimeFormatter javaTimeOutputFormatter = java.time.format.DateTimeFormatter.ofPattern(outputPattern);

// output: week 1 - 04/01/2016 00:00:00
System.out.println("With joda: " + jodaOutputFormatter.print(jodaDateTime));
// output: week 2 - 04/01/2016 00:00:00
System.out.println("With LocalDateTime: " + javaTimeOutputFormatter.format(localDateTime));
// output: week 2 - 04/01/2016 00:00:00
System.out.println("With ZonedDateTime: " + javaTimeOutputFormatter.format(zonedDateTime));

For some reason, the output from the w symbol is off-by-one across the two implementations.

What is causing this inconsistency? Is the w symbol inconsistently implemented across Joda time and java.time?


Answer:

Well, it is a little bit speculative, but since you told me that your system timezone is EST (-05:00) I assume that you are sitting in US (New York?). And US does not apply ISO-8601-week rules. Weeks start on Sunday, and the first week of the year does not need to contain at least 4 days (even one day is enough to be counted as first week of year).

So let's look at your example date of 4th of January. It is a Monday. The first US-week is from 2016-01-01 until 2016-01-02 (2 days - enough for US). And the second US-week starts on Sunday the 3rd of January, so the fourth of January is in the second week, too.

And now the critical point: java.time (JSR-310) uses a localized week of week-based-year for the pattern symbol w, see also its backport which should have the same code. Code excerpt:

} else if (cur == 'w') {
    if (count > 2) {
        throw new IllegalArgumentException("Too many pattern letters: " + cur);
    }
    appendInternal(new WeekFieldsPrinterParser('w', count));

...

static final class WeekFieldsPrinterParser implements DateTimePrinterParser {
    private final char letter;
    private final int count;

    public WeekFieldsPrinterParser(char letter, int count) {
        this.letter = letter;
        this.count = count;
    }

    @Override
    public boolean print(DateTimePrintContext context, StringBuilder buf) {
        WeekFields weekFields = WeekFields.of(context.getLocale());
        DateTimePrinterParser pp = evaluate(weekFields);
        return pp.print(context, buf);
    }

The use of WeekFields.of(context.getLocale()) for the pattern symbol "w" is evident.

In contrast, Joda-Time only uses ISO-8601-week-definition which let weeks start on Monday and count that week as first week of year which contains at least four days in current calendar year. So the Monday 4th of January is the start of the first week-of-year because the three days before are not enough for ISO-8601 to be counted as week. Those preceding days are instead considered as last week of previous year.

Consequently, Joda-Time displays week 1 for 4th of January while java.time uses the US-week 2.

Solution of your problem is to specify the locale such that the formatter will use ISO-weeks so you get the same result as in Joda-Time. For example, you could choose Locale.UK which also uses English but other week rules. Don't rely on your default locale. This can fool you.

Question:

Using the below code I'm attempting to convert a time: "2014-10-31T23:59:59" to just "yyyy-MM-dd" format, so in this case "2014-10-31".

Here is the code :

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class TestConvert {

    public static void main(String args[]){
        DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
        DateTime dt = formatter.parseDateTime("2014-10-31T23:59:59");
        System.out.println("formatted date is "+dt.toString());
    }

}

exception :

Exception in thread "main" java.lang.IllegalArgumentException: Invalid format: "2014-10-31T23:59:59" is malformed at "T23:59:59"
    at org.joda.time.format.DateTimeFormatter.parseDateTime(DateTimeFormatter.java:899)
    at (TestConvert.java:11)

How to ignore the time and just create a date?


Answer:

You need a different formatter to parse the date in its original format:

public static void main(String args[]){
    DateTimeFormatter parser = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss");
    DateTime dt = parser.parseDateTime("2014-10-31T23:59:59");

    DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
    System.out.println("formatted date is " + formatter.print(dt));
}

Question:

I have a primitive int variable with possible values from 1 to 7 (actually it received from calling getDayOfWeek() on org.joda.time.DateTime object and then persisted).

Now I need to get a string representation of this day of week. I can do it by creating a new DateTime object and setting that day of week to created object:

LocalDateTime.now().withDayOfWeek(dayOfWeek).dayOfWeek().getAsText();

The question is: can I get the same string without redundant creation of a LocalDateTime object?


Answer:

To achieve what you want without creating a LocalDateTime instance, you can use the org.joda.time.chrono.ISOChronology class to get a org.joda.time.DateTimeField that corresponds to the day of week.

In the example below I'm using org.joda.time.DateTimeConstants, which has values from Monday (1) to Sunday (7). I'm also using java.util.Locale to get the names in English, but you can use whatever Locale you want (the code in your question uses the system's default locale, in this case you could just use Locale.getDefault() instead):

DateTimeField dayOfWeek = ISOChronology.getInstance().dayOfWeek();

System.out.println(dayOfWeek.getAsText(DateTimeConstants.MONDAY, Locale.ENGLISH));
System.out.println(dayOfWeek.getAsText(DateTimeConstants.TUESDAY, Locale.ENGLISH));
System.out.println(dayOfWeek.getAsText(DateTimeConstants.WEDNESDAY, Locale.ENGLISH));
System.out.println(dayOfWeek.getAsText(DateTimeConstants.THURSDAY, Locale.ENGLISH));
System.out.println(dayOfWeek.getAsText(DateTimeConstants.FRIDAY, Locale.ENGLISH));
System.out.println(dayOfWeek.getAsText(DateTimeConstants.SATURDAY, Locale.ENGLISH));
System.out.println(dayOfWeek.getAsText(DateTimeConstants.SUNDAY, Locale.ENGLISH));

The output is:

Monday Tuesday Wednesday Thursday Friday Saturday Sunday


New Java Date/Time API

Joda-Time is in maintainance mode and is being replaced by the new APIs, so I don't recommend start a new project with it. Even in joda's website it says: "Note that Joda-Time is considered to be a largely "finished" project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".

If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.

If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).

The code below works for both. The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.

for(DayOfWeek dow : DayOfWeek.values()) {
    System.out.println(dow.getDisplayName(TextStyle.FULL, Locale.ENGLISH));
}

This will also output:

Monday Tuesday Wednesday Thursday Friday Saturday Sunday

You can also get a DayOfWeek from an int value - from 1 (Monday) to 7 (Sunday) - using DayOfWeek.of method:

// 1 is Monday, 2 is Tuesday and so on, until 7 (Sunday)
System.out.println(DayOfWeek.of(1).getDisplayName(TextStyle.FULL, Locale.ENGLISH));

Question:

I am trying to format the following date

Thu, 15 Jan 2015, 9:56 AM

Using the following:

public String parse(String oldDate){
        final String OLD_FORMAT = "EEE, d MMM yyyy, HH:mm:ss zz";
        final String NEW_FORMAT = "yyyy/MM/dd";

        // August 12, 2010
        String oldDateString = oldDate;
        String newDateString;

        DateTimeFormatter formatterOld = DateTimeFormat.forPattern(OLD_FORMAT);
        DateTimeFormatter formatterNew = DateTimeFormat.forPattern(NEW_FORMAT);
        LocalDate localDate = formatterOld.parseLocalDate( oldDateString );
        return newDateString = formatterNew.print( localDate );
    }

I am getting a

Caused by: java.lang.IllegalArgumentException: Invalid format: "Thu, 15 Jan 2015, 9:56 AM" is malformed at " AM"

How do I represent AM/PM properly in that date format? I tried using Z but get the same and changed to zz but invain. What is the correct representation for AM/PM?

I also checked with "hh:mm a" but that again does nto seem to help.


Answer:

Z and z are for TimeZones - you want a which is the format-code for half-day.

The formatting codes are describe in the JavaDoc for org.joda.time.format.DateTimeFormat

The code below works for me (running on a Java 8 JRE with Joda 2.6)

public static void main(String[] args) {
    String format = "EEE, d MMM yyyy, HH:mm a";
    DateTimeFormatter formatter = DateTimeFormat.forPattern(format);
    final LocalDate date = formatter.parseLocalDate("Thu, 15 Jan 2015, 9:56 AM");
    System.out.println(date);
}

Question:

DateTime now = new DateTime(2017, 2, 27, 17, 50, 31);
System.out.println(now.toString("s") + " <-- must 3\n" + now.toString("ss")+" <-- must 31");

output:

31 <-- must 3
31 <-- must 31

somtimes i get correct result mostly wrong!

JodaTime 2.9.7


Answer:

From the Javadoc for DateTimeFormat:

Number: The minimum number of digits. Shorter numbers are zero-padded to this amount. When parsing, any number of digits are accepted

Thus s specifies that the output must be at least one digit. If the seconds were less than 10 it would be one digit, but with numbers ≥ 10 you will get two digits.

BTW, it makes absolutely no sense to truncate the numerical value 31 to only 3.