3

I need to convert "11/17" string date (November 2017) to "2017-11-01" (November 1, 2017).

What is the best way to achieve this in Java?

I tried:

String dayMonthYear = "11/17";
dayMonthYear = "01/" + dayMonthYear;

DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/MM/yy");
DateTime dt = formatter.parseDateTime(dayMonthYear);
dt.year().setCopy(dt.getYear() + 2000);

DateTimeFormatter dtfOut = DateTimeFormat.forPattern("yyyy-MM-dd");
dayMonthYear = dtfOut.print(dt);
System.out.println(dayMonthYear); // "2017-11-01";

And:

SimpleDateFormat dateFormatTwoDigitsYear = new SimpleDateFormat(TWO_DIGITS_YEAR_DATE_FORMAT);  //"dd/MM/yy"
SimpleDateFormat dateFormatFourDigitsYear = new SimpleDateFormat(FOUR_DIGITS_YEAR_DATE_FORMAT);//  "yyyy-MM-dd"
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, -100);//??? I AM NOT SURE WHY DO I NEEED THIS??
dateFormatTwoDigitsYear.set2DigitYearStart(calendar.getTime());
try
{
    dayMonthYear = dateFormatFourDigitsYear.format(dateFormatTwoDigitsYear.parse(dayMonthYear));
}
catch (ParseException e)
{
    log.error("Error while formatting date in yyyy-MM-dd format. Date is " + dayMonthYear);
}

I am not sure why I need this line in second appproach:

calendar.add(Calendar.YEAR, -100);

Both of them are working. But I am not sure if there is better solution.

Bilberryfm
  • 507
  • 2
  • 11
  • 28

2 Answers2

9

You don't need to add values to the year. The 2 digit value (17) is automatically ajusted to 2017. Also, there's no need to append day 1 in the input. When the day is not present, SimpleDateFormat automatically sets to 1:

// input format: MM/yy
SimpleDateFormat parser = new SimpleDateFormat("MM/yy");
// output format: yyyy-MM-dd
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(formatter.format(parser.parse("11/17"))); // 2017-11-01

PS: if you need to set to another day (other than 1), you can set the date to a Calendar and change it before formatting:

// parse the date
Date date = parser.parse("11/17");
// set to a Calendar
Calendar cal = Calendar.getInstance();
cal.setTime(date);
// change to whatever day you want
cal.set(Calendar.DAY_OF_MONTH, whateverDayIwant);
// format it
System.out.println(formatter.format(cal.getTime()));

Joda-Time

The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.

I've seen you're using Joda-Time, so here's how to do it with this API.

In Joda-Time, when you parse to a DateTime, it sets default values for all the missing fields (in this case, day, hour, minute, etc). But this API has lots of other types that can suit best for each use case.

As the input has only month and year, you can parse it to a org.joda.time.YearMonth and then set the day to 1. This will create a org.joda.time.LocalDate, which can be printed directly (as the toString() method already returns the date in the format you want):

DateTimeFormatter parser = DateTimeFormat.forPattern("MM/yy");
// parse to YearMonth
YearMonth ym = YearMonth.parse("11/17", parser);
// set day to 1
System.out.println(ym.toLocalDate(1)); // 2017-11-01

I prefer this approach because you can set it to whatever day you want, and don't need to create a full DateTime object with fields that you don't need/care about (such as hour, minutes, etc).

If you need to store this value in a String, just call the toString() method:

// store "2017-11-01" in a String
String output = ym.toLocalDate(1).toString();

The format yyyy-MM-dd is the default used by toString(). If you need a different format, you can pass it to toString():

// convert to another format (example: dd/MM/yyyy)
String output = ym.toLocalDate(1).toString("dd/MM/yyyy"); // 01/11/2017

Or you can use another DateTimeFormatter:

// convert to another format (example: dd/MM/yyyy)
DateTimeFormatter fmt = DateTimeFormat.forPattern("dd/MM/yyyy");
String output = fmt.print(ym.toLocalDate(1)); // 01/11/2017

PS: dt.year().setCopy returns a new object, and as you don't assign it to any variable, this value is lost - this method doesn't change the original DateTime object, so dt is not changed by its line of code.


Java new 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 can't (or don't want to) migrate from Joda-Time to the new API, you can ignore this section.

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 6 or 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need 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.

The code is very similar to Joda-Time (the API's are not exactly the same, but they have lots of similarities). You can use a Java 8's java.time.format.DateTimeFormatter and parse the input to a java.time.YearMonth (or, in Java 7's ThreeTen Backport, use a org.threeten.bp.format.DateTimeFormatter and parse to a org.threeten.bp.YearMonth):

DateTimeFormatter parser = DateTimeFormatter.ofPattern("MM/yy");
YearMonth ym = YearMonth.parse("11/17", parser);
System.out.println(ym.atDay(1)); // 2017-11-01

If you need to store this value in a String, just call the toString() method:

// store "2017-11-01" in a String
String output = ym.atDay(1).toString();

The format yyyy-MM-dd is the default used by toString(). If you need a different format, you can use another DateTimeFormatter:

// convert to another format (example: dd/MM/yyyy)
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String output = fmt.format(ym.atDay(1)); // 01/11/2017
  • 2
    In my opinion, the java.time approach should come first in your answer. – VGR Oct 25 '17 at 18:02
  • 1
    @VGR I decided to first use what the OP had, and then say *"actually this is bad/old and there's a better/modern alternative"*. Mainly because it's not clear from the question if they're using Java 8 - if that was the case, I'd surely start with `java.time`. –  Oct 25 '17 at 18:16
  • 1
    tl;dr: `YearMonth.parse( "11/17" , DateTimeFormatter.ofPattern( "MM/yy" ) ).atDay( 1 )` – Basil Bourque Oct 25 '17 at 19:30
1

You can define two utility methods one converting the string to date and the other converting it back to string.

static Date stringToDate(String string) throws ParseException
{
    DateFormat dateFormat = new SimpleDateFormat("MM/yyyy");
    return dateFormat.parse(string);
}

static String dateToString(Date date)
{
    DateFormat outputFormatter = new SimpleDateFormat("yyyy-MM-dd");
    return outputFormatter.format(date);
}

static String formatString(String string)
{
    return dateToString(stringToDate(string));
}


//Just call formatString() with your argument

You can combine them if you only intend to convert the first string type to the second type but I like to keep them separate in case I need a date object from string or string directly from a date object.

DobromirM
  • 2,017
  • 2
  • 20
  • 29