LocalDateTime using FormatStyle.MEDIUM plus time zone

I have a LocalDateTime object that I’m formatting as follows:

LocalDateTime localDateTime = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
System.out.println(localDateTime.format(formatter));

That prints a nicely readable date of Oct 20, 2021 1:00:02 PM.

But I would like to also display the time zone. My understanding is I need to use ZonedDateTime:

ZonedDateTime zdt = localDateTime.atZone(ZoneId.of("America/New_York"));
System.out.println(zdt);

But that produces the not as readable 2021-10-20T13:00:02.921-04:00[America/New_York].

Is there some way to format a ZonedDateTime so it’s concise and readable like that produced by FormatStyle.MEDIUM, but appended by the timezone (e.g: Oct 20, 2021 1:00:02 PM EST)?

Note: I gather from this answer, that I should not actually use “pseudo-zones”, such as EST, due to their non-standardized nature.

Answer

tl;dr

If you want a hint as to the time zone in use, use the LONG format for time-of-day portion. Optionally specify a different format such as MEDIUM for the date portion.

ZonedDateTime
    .now( ZoneId.of( "America/New_York" ) )
    .format(
        DateTimeFormatter
            .ofLocalizedDateTime( 
                FormatStyle.MEDIUM ,     // Date portion style.
                FormatStyle.LONG         // Time-of-day portion style.
            )   
            .withLocale( Locale.US )     // Locale determines the human language and cultural norms used in localizing.
    )

See this code run live at IdeOne.com.

Oct 20, 2021, 4:48:46 PM EDT

Never use EDT, CST, IST, and such for data-exchange. Do not attempt parsing of such values. These are not real time zones, are not standardized, and are not even unique!

Avoid LocalDateTime.now

I cannot imagine any scenario where calling LocalDateTime.now is the right thing to do. You capture the date and time-of-day but lack the context of a time zone or offset-from-UTC. So a LocalDateTime by definition cannot represent a moment, is not a point on the timeline.

If you want to capture the current moment without committing to a particular time zone, capture the current moment as seen with an offset-from-UTC of zero hours-minutes-seconds.

Instant instant = Instant.now() ;  // Current moment as seen in UTC. 

Use ZonedDateTime.now

If you want to capture the current moment as seen in America/New_York time zone, start with a ZonedDateTime.

ZonedDateTime.now( ZoneId.of( "America/New_York" ) )

To generate text representing a java.time object, use toString method to get text in standard ISO 8601 format. While such output may seem less readable at first glance, the standard formats are designed to be maximally readable by people across cultures.

To get a localized format, I suggest you let java.time automatically localize.

ZoneId z = ZoneId.of( "America/New_York" );
ZonedDateTime zdt = ZonedDateTime.now( z );
System.out.println( "zdt represented in standard ISO 8601 format: " + zdt.toString() );

Locale locale = Locale.US ; 
DateTimeFormatter f_US = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.MEDIUM ).withLocale( locale ) ;
String outputLocalized_US = zdt.format( f_US ) ;
String outputLocalized_CA_fr = zdt.format( f_US.withLocale( Locale.CANADA_FRENCH ) ) ;

System.out.println( outputLocalized_US ) ;
System.out.println( outputLocalized_CA_fr ) ;

When run.

zdt represented in standard ISO 8601 format: 2021-10-20T16:37:57.752554-04:00[America/New_York]
Oct 20, 2021, 4:37:57 PM
20 oct. 2021 16 h 37 min 57 s

Separate formats for date & time

You can specify different formats for the date versus time-of-day portions. Use a longer format for time-of-day portion to get a hint of the time zone while using a shorter format for date portion, if you so desire.

        DateTimeFormatter
            .ofLocalizedDateTime( 
                FormatStyle.MEDIUM ,     // Date portion format.
                FormatStyle.LONG         // Time-of-day portion format.
            )