0

I have an app that displays dates and times in a localized format for different areas, that is - it shows a user dates and times based on his preference. But my issue is that it always displays numbers, ie yyyy.mm.dd, or mm/dd/yyy, or dd,mm,yyyy. What I would like to have is this: show date so that the day is a number, year is a number, but the month is displayed as a string, ie. Jan / Xin / Gen / Jān / Yan... or 01 Jan 2018 / Jan 01 2018 / 2018 Jan 01, and so on, but still keep the current local formatting. I know that I could do that if I hardcode it like MMM, but I want it to change, depending on the localized format.

So basically this is my question: how can I display localized time, from a value I get from the server (yyyy-MM-dd HH: mm: ss), with month displayed as a three letter word, no matter what locale it uses?

I know this question sounds familiar, but so far I have tried a lot of answers, both on stack overflow, and other sources, but without any success. Some of the things I have tried include: 1 / 2 / 3 / 4 / 5 / 6 ... but I couldn't make it work in my example.

My Adapter:

public void onBindViewHolder(RestaurantsAdapter.RestaurantsViewHolder holder, int position) {
    // getting restaurant data for the row
    Restaurant restaurant = restaurant Items.get(position);

    holder.userName.setText(restaurant .getUserName());
    holder.date.setText(convertDate(restaurant .getDateTime())); //string dobiti u formatu, pretvoriti ga u localized i podijeliti na dva dijela
    holder.time.setText(convertTime(restaurant .getDateTime()));

    TextDrawable.IBuilder builder = TextDrawable.builder()
            .beginConfig()
            .withBorder(0)
            .toUpperCase()
            .endConfig()
            .roundRect(10);
    ColorGenerator generator = ColorGenerator.MATERIAL;
    int color = generator.getColor(restaurant.getUserId());
    TextDrawable textDrawable = builder.build(restaurant Items.get(position).getUserName().substring(0, 1), color);
    holder.thumbNail.setImageDrawable(textDrawable);

    Picasso.with(context)
            .load(AppConfig.URL_PROFILE_PHOTO + restaurant.getThumbnailUrl())
            .placeholder(textDrawable)
            .error(textDrawable)
            .transform(new RoundedTransform(12, 0))
            .fit()
            .centerCrop()
            .into(holder.thumbNail);
}

 private String convertDate(String time) {
    final DateFormat shortDateFormat = android.text.format.DateFormat.getDateFormat(context.getApplicationContext());

    SimpleDateFormat dateFormatReceived = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
    SimpleDateFormat dateFormatConverted = new SimpleDateFormat(((SimpleDateFormat) shortDateFormat).toPattern(), Locale.getDefault());

    java.util.Date date = null;

    try {
        date = dateFormatReceived.parse(time);
    } catch (ParseException e) {
        e.printStackTrace();
    }

    return dateFormatConverted.format(date);
}

private String convertTime(String time) {
    final DateFormat shortTimeFormat = android.text.format.DateFormat.getTimeFormat(context.getApplicationContext());
    SimpleDateFormat timeFormatReceived = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
    SimpleDateFormat timeFormatConverted = new SimpleDateFormat(((SimpleDateFormat) shortTimeFormat).toPattern(), Locale.getDefault());

    java.util.Date date = null;

    try {
        date = timeFormatReceived.parse(time);
    } catch (ParseException e) {
        e.printStackTrace();
    }

    return timeFormatConverted.format(date);
}

UPDATE:

I tried adding this to my solution:

 private String convertDate(String time) {
    final DateFormat shortDateFormat = android.text.format.DateFormat.getDateFormat(context.getApplicationContext());
    Calendar Now = Calendar.getInstance();
    Now.getDisplayName(Calendar.HOUR, Calendar.SHORT, Locale.getDefault());
    SimpleDateFormat dateFormatReceived = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
    SimpleDateFormat dateFormatConverted = new SimpleDateFormat(((SimpleDateFormat) shortDateFormat).toPattern(), Locale.getDefault());
    dateFormatConverted.getDisplayName(Calendar.HOUR, Calendar.SHORT, Locale.getDefault());
    java.util.Date date = null;

    try {
        date = dateFormatReceived.parse(time);
    } catch (ParseException e) {
        e.printStackTrace();
    }

    return dateFormatConverted.format(date);
}
Banana
  • 2,435
  • 7
  • 34
  • 60
  • can you add what format you get the date/time in and what is the output you get? Also are there specific locales that the MMM format doesn't work? On another note, I don't think Locale really matters when you have a date format like 1 Jan 1970. It will matter if the date is 11/12/17 in which case it makes a case for a confusion. – LeoNeo Dec 24 '17 at 17:13
  • @LeoNeo Hey LeoNeo, this is the format I get from the server: `yyyy-MM-dd HH:mm:ss`. I couldn't get it to display on a few different formats, 3 devices and 1 emulator :) Also, I originally displayed it as you said, with hardcoded 01 Jan 2018, but I wanted to make it a bit prettier. – Banana Dec 24 '17 at 17:17
  • 1
    Please, for your own best, stop using the long outdated and notoriously troublesome `SimpleDateFormat` and friends. Even on Android I recommend using [JSR-310, AKA `java.time`, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/) instead. It is so much nicer to work with. – Ole V.V. Dec 24 '17 at 21:23

2 Answers2

1

Please try to use getDisplayName(...) method

In order to get more information you had better go through this documentation


Editted

More sufficiently you can try this way:

int monthOfYear = Calendar.JULY; // 6
String monthName = new 
DateFormatSymbols(Locale.getDefault()).getShortMonths()[monthOfYear];
Tarlan Ahad
  • 370
  • 4
  • 15
  • I have already tried adding this to my code like this: `dateFormatConverted.getDisplayName(Calendar.HOUR, Calendar.SHORT, Locale.getDefault());` but I get a message that it cannot resolve method getDisplayName. – Banana Dec 24 '17 at 17:25
  • Please try to use method getDisplayName(..) with the instance of Calendar class. It will work: Try like this: `Calendar Now = Calendar.getInstance(); Now.getDisplayName(Calendar.HOUR, Calendar.SHORT, Locale.getDefault());` – Tarlan Ahad Dec 24 '17 at 17:31
  • I havetried this, but I still get the same issue that it cannot reslove the method. I have added my edit to my updated code above – Banana Dec 24 '17 at 17:58
  • I think i know your problem please use other library `import java.util.Calendar;' it is your problem i guess – Tarlan Ahad Dec 24 '17 at 18:01
  • I used the `import java.util.Calendar;` from the start but it couldn't make it work – Banana Dec 24 '17 at 18:02
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/161905/discussion-between-tarlan-ahad-and-kemo). – Tarlan Ahad Dec 24 '17 at 18:10
1

I think the following method will give you the date-time formatter you want.

public static DateTimeFormatter getLocalizedDateFormatter(Locale requestedLocale) {
    String formatPatternString = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
            FormatStyle.SHORT, null, 
            Chronology.ofLocale(requestedLocale), requestedLocale);
    // if not already showing month name, modify so it shows abbreviated month name
    if (! formatPatternString.contains("MMM")) {
        formatPatternString = formatPatternString.replaceAll("M+", "MMM");
    }
    return DateTimeFormatter.ofPattern(formatPatternString, requestedLocale);
}

Test:

    Locale[] locales = { Locale.US, Locale.FRANCE, Locale.GERMANY,
            Locale.forLanguageTag("da-DK"), Locale.forLanguageTag("sr-BA") };
    LocalDate today = LocalDate.now(ZoneId.of("Europe/Sarajevo"));
    for (Locale currentLocale : locales) {
        DateTimeFormatter ldf = getLocalizedDateFormatter(currentLocale);
        System.out.format(currentLocale, "%-33S%s%n", 
                currentLocale.getDisplayName(), today.format(ldf));
    }

Output:

ENGLISH (UNITED STATES)          Dec/24/17
FRENCH (FRANCE)                  24/déc./17
GERMAN (GERMANY)                 24.Dez.17
DANSK (DANMARK)                  24-dec.-17
SERBIAN (BOSNIA AND HERZEGOVINA) 17-дец-24

JSR-310 also known as java.time

You tried to use the long outdated and notoriously troublesome classes DateFormat and SimpleDateFormat. Instead I recommend you make it a habit to use JSR-310, the modern Java date and time API. So I do that in the method above.

I don’t think it was part of the question, but for the sake of completeness, parse the date-time string as follows. The formatter you get from my getLocalizedDateFormatter can be used for formatting a LocalDateTime and many other date-time types in addition to LocalDate.

    LocalDateTime ldt = LocalDateTime.parse("2017-12-24 22:51:34",
            DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss"));
    String formattedDate = ldt.format(getLocalizedDateFormatter(Locale.UK));

Question: Can I use JSR-310 on Android?

Yes you can. It just requires at least Java 6.

  • In Java 8 and later the new API comes built-in.
  • In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (ThreeTen for JSR 310).
  • On Android, use the Android edition of ThreeTen Backport. It’s called ThreeTenABP.

In the latter two cases, make sure to add the appropriate edition of the backport library to your project, use the links below, and to import the classes I use from org.threeten.bp and org.threeten.bp.format.

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • I will take a look at this tonight after work and let you know :) Thanks for the comprehensive answer :) – Banana Dec 25 '17 at 07:43
  • There is a slight issue, my min API is 17 and it requires 26 to work. – Banana Dec 25 '17 at 17:40
  • 1
    I had to add another comment, I have asked a lot of questions, and this is the best, most comprehensive, encompassing, multilayered (you even found sr-BA, which is not really used in Bosnia, but has Cyrillic spelling for the date :) ) answer I have got so far. This also might be the best answer I have found on stack overflow altogether. As I said, my minor issue is the min API, but I couldn't accept any other answer :) – Banana Dec 25 '17 at 17:44
  • Have you added ThreeTenABP to your project, and are you importing the classes from [`org.threeten.bp.format`](http://www.threeten.org/threetenbp/apidocs/org/threeten/bp/format/package-frame.html) and [`org.threeten.bp`](http://www.threeten.org/threetenbp/apidocs/org/threeten/bp/package-frame.html)? @Kemo – Ole V.V. Dec 25 '17 at 17:44
  • I will have to look at it in depth over the weekend :) Thanks again – Banana Dec 25 '17 at 17:51
  • 1
    My mistake was not importing the correct classes. It works perfectly. Thank you – Banana Dec 31 '17 at 12:23
  • 1
    I failed to explain that from the outset. Thanks, @Kemo, for pointing out so I can do better some other time. Happy New Year. – Ole V.V. Dec 31 '17 at 12:55
  • It was actually my rookie mistake :) Again, this is the best answer I have ever seen on stackoverflow. Thanks again and have a great 2018 :) – Banana Dec 31 '17 at 13:01