3

I want to format a date, which should be localized and includes the "day period".

"Day period" meaning not am/pm but "afternoon", "early morning" etc.

A formatted date based on german locale with the 12 hour setting enabled should look like 03.08.18, 9:01 abends, "abends" meaning evening.

The same example should be 3/8/18 9:01 de la noche in spanish.


Why i think it should be possible:

A: Because the WhatsApp Android app formats dates like this

B: Because the Java 10 source code includes the following (From the german translation file):

<dayPeriods>
<dayPeriodContext type="format">
    <dayPeriodWidth type="wide">
        <dayPeriod type="afternoon">nachmittags</dayPeriod>
        <dayPeriod type="am">vorm.</dayPeriod>
        <dayPeriod type="earlyMorning">morgens</dayPeriod>
        <dayPeriod type="evening">abends</dayPeriod>
        <dayPeriod type="morning">vormittags</dayPeriod>
        <dayPeriod type="night">nachts</dayPeriod>
        <dayPeriod type="noon">Mittag</dayPeriod>
        <dayPeriod type="pm">nachm.</dayPeriod>
    </dayPeriodWidth>
</dayPeriodContext>
...
</dayPeriods>


I tried: (current is the locale)
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, current);

String formattedDate = df.format(System.currentTimeMillis());

Output: 08.12.18 7:24 nachm or 8/12/18 7:54 p. m.


The following code has the same output:
DateFormat abc = SimpleDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, current);

abc.format(System.currentTimeMillis());


I also tried the DateTimeFormatterBuilder (With all of the AMPM_OF_DAY versions):
DateTimeFormatter test = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT);

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
                .append(test)
                .appendText(ChronoField.AMPM_OF_DAY, TextStyle.SHORT)
                .appendText(ChronoField.AMPM_OF_DAY, TextStyle.NARROW)
                .appendText(ChronoField.AMPM_OF_DAY, TextStyle.FULL)
                .appendText(ChronoField.AMPM_OF_DAY, TextStyle.FULL_STANDALONE)
                .toFormatter(current);

ZonedDateTime date = ZonedDateTime.now();
date.format(fmt);

Output: 8 dic. 2018 19:54p. m.p. m.p. m.11


Is there any way i can output the date this way?

Thanks

Nick
  • 33
  • 4
  • A basic problem may be that different cultures divide the day differently, so there is no direct translation. You already got a bit of this in AM and PM, which aren’t used in German. It’s not something I know, but I imagine that people around the mediterranean would expect a “siesta” time of day that is not in your list. And so on and so forth. – Ole V.V. Dec 10 '18 at 12:24
  • 1
    You should certainly prefer the modern `DateTimeFormatter` over the troublesome and old-fashioned `SimpleDateFormat`. – Ole V.V. Dec 10 '18 at 12:26
  • @OleV.V. Thanks for your thoughts. Yes that is a problem. I would like to know how WhatsApp does it. I think they just use the right java date formatter or a little trick, because all necessary information is in the java source. The [Unicode LDML](https://unicode.org/reports/tr35/tr35-dates.html#dayPeriods) specifies the periods. LDML is the spec java uses for the locales – Nick Dec 10 '18 at 12:36
  • Does this answer your question? [Showing Morning, afternoon, evening, night message based on Time in java](https://stackoverflow.com/questions/27589701/showing-morning-afternoon-evening-night-message-based-on-time-in-java) – Ole V.V. Nov 16 '20 at 03:30

2 Answers2

3

There are only two libraries which support day periods, namely ICU4J (partially incorporated in Android) and my lib Time4A. Other formatters like java.text.DateFormat or java.time.format.DateTimeFormatter have no support.

Example in Time4A for the German locale (here with a bridge to the old-fashioned type java.util.Date):

 ChronoFormatter<java.util.Date> f =
      ChronoFormatter.ofPattern(
          "dd.MM.uuuu, h:mm BBBB",
          PatternType.CLDR,
          Locale.GERMAN,
          Moment.axis(TemporalType.JAVA_UTIL_DATE))
      .withStdTimezone();
 System.out.println(f.format(new java.util.Date()));

example output => "11.12.2018, 6:03 morgens"

It uses the special pattern symbol "B" defined by LDML-syntax of CLDR-data (managed by Unicode consortium). The day partition rules are also taken from CLDR data and look for German like:

# day-period-rules
T0000=night1
T0500=morning1
T1000=morning2
T1200=afternoon1
T1300=afternoon2
T1800=evening1

# day-period-translations
P(a)_morning1=morgens
P(a)_morning2=vormittags
P(a)_afternoon1=mittags
P(a)_afternoon2=nachmittags
P(a)_evening1=abends
P(a)_night1=nachts

If you don't agree with these data then you might construct your own customized formatter via the builder pattern.

Note about ICU4J: I have not tested that lib but maybe its class com.ibm.icu.text.SimpleDateFormat supports the pattern symbol B, too (but then the javadoc is outdated).

Another note:

If you like style-based formatting without specifying a concrete pattern then you might go this way using a composition of date and time part:

    Locale loc = Locale.US;

    // our concrete values to be formatted
    Moment moment = SystemClock.currentMoment();
    PlainDate calendarDate = moment.toLocalTimestamp().getCalendarDate();

    // get the localized clock pattern
    Map<String, String> textForms = CalendarText.getIsoInstance(loc).getTextForms();
    String pattern = 
            textForms.containsKey("F_Bhm") ? textForms.get("F_Bhm") : "h:mm B";

    // construct the style-based date formatter
    ChronoFormatter<PlainDate> f1 = 
            ChronoFormatter.ofDateStyle(DisplayMode.SHORT, loc);

    // construct the time formatter
    ChronoFormatter<Moment> f2 = ChronoFormatter.ofMomentPattern(
        pattern,
        PatternType.CLDR,
        loc,
        Timezone.ofSystem().getID());

    System.out.println(f1.format(calendarDate) + " " + f2.format(moment));
    // example output => "12/11/18 6:18 in the morning"
Meno Hochschild
  • 42,708
  • 7
  • 104
  • 126
  • This was already very helpful. Is there any way to get the date and time pattern like the `DateUtils.formatDateTime` outputs? SimpleDateFormat and your examples are missing the ", " (` 12.12.2018, 1:13 nachm.`), which i unfortunately need based on locale (Some locales include it and some dont). – Nick Dec 12 '18 at 13:40
  • @Nick I understand your desire for localized literals combining date and time patterns. Unfortunately Time4J/A does not yet support it. But I have opened a [new issue](https://github.com/MenoData/Time4J/issues/845) to track any suggestions/proposals. Your proposals are welcome. To be realistic, due to the complexity, a final solution will not be present before spring 2019. – Meno Hochschild Dec 12 '18 at 17:36
0

The day period support is there now with Java 16. The new formatter symbol "B" gives this support.

Example -

ZonedDateTime utcDateZoned = ZonedDateTime.now(ZoneId.of("Etc/UTC"));
System.out.println(utcDateZoned.format(DateTimeFormatter.ofPattern("dd:mm:yyy-h:mm B").withLocale(Locale.UK)));
System.out.println(utcDateZoned.format(DateTimeFormatter.ofPattern("dd:mm:yyy-h:mm B").withLocale(Locale.CHINA)));
System.out.println(utcDateZoned.format(DateTimeFormatter.ofPattern("dd:mm:yyy-h:mm B").withLocale(Locale.ITALY)));
System.out.println(utcDateZoned.format(DateTimeFormatter.ofPattern("dd:mm:yyy-h:mm B").withLocale(Locale.GERMAN)));

Output -

06:26:2023-4:26 in the afternoon

06:26:2023-4:26 下午

06:26:2023-4:26 del pomeriggio

06:26:2023-4:26 nachmittags

Dilan Tharaka
  • 517
  • 5
  • 15