Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Format LocalDateTime using FormatStyle.MEDIUM plus time zone

Tags:

java

date

I have a LocalDateTime object that I format 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 add 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 so 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 also 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.

like image 557
Woodchuck Avatar asked Oct 20 '21 20:10

Woodchuck


People also ask

How do I format LocalDateTime?

LocalDateTime is an immutable date-time object that represents a date-time with default format as yyyy-MM-dd-HH-mm-ss. zzz. It provides a factory method that takes LocalDate and LocalTime input arguments to create LocalDateTime instance.

Which time format is followed in Java 8?

For time patterns, use mm for two-digit minute and ss for two-digit second. The hh pattern generates the two-digit hour for a 12-hour clock (e.g., 6PM is "06") and HH generates the two-digit hour for a 24-hour clock (e.g., 6PM is "18").

Does LocalDate have timezone?

Class LocalDate. A date without a time-zone in the ISO-8601 calendar system, such as 2007-12-03 . LocalDate is an immutable date-time object that represents a date, often viewed as year-month-day.

What is Iso_local_date?

ISO_LOCAL_DATE. public static final DateTimeFormatter ISO_LOCAL_DATE. The ISO date formatter that formats or parses a date without an offset, such as '2011-12-03'. This returns an immutable formatter capable of formatting and parsing the ISO-8601 extended local date format.


4 Answers

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.
            )   
like image 190
Basil Bourque Avatar answered Oct 18 '22 01:10

Basil Bourque


Is there some way to format a ZonedDateTime so it reads similar to that produced by FormatStyle.MEDIUM, but is appended by the timezone - say, something like: Oct 20, 2021 4:05:13 PM EST?

You can use the format, MMM d, uuuu h:mm:ss a zzz with the DateTimeFormatter.

Demo:

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/New_York"));
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MMM d, uuuu h:mm:ss a zzz", Locale.ENGLISH);
        System.out.println(now.format(dtf));
    }
}

Output:

Oct 20, 2021 6:41:45 PM EDT

ONLINE DEMO

like image 35
Arvind Kumar Avinash Avatar answered Oct 18 '22 03:10

Arvind Kumar Avinash


the ZonedDateTime can retrieve the current ZoneId. With the Zone, you can just use the getDisplayName(TextStyle style, Locale locale) getter. Just experiment which TextStyle is the best for you.

zdt.getZone().getDisplayName(TextStyle.SHORT, Locale.ENGLISH)
==> ET
like image 2
Likqez Avatar answered Oct 18 '22 01:10

Likqez


Note that if you use LONG or FULL for the time style, the zone will be included:

ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("America/New_York"));
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.LONG);
System.out.println(zdt.format(formatter));

In the US locale, this prints:

Oct 20, 2021, 4:34:16 PM EDT

This is documented here:

The FULL and LONG styles typically require a time-zone. When formatting using these styles, a ZoneId must be available, either by using ZonedDateTime or withZone(java.time.ZoneId).

In this case, a time style of LONG happens to be the same as MEDIUM, but if you have to use MEDIUM for some reason, you can use a DateTimeFormatterBuilder and add the zone by hand:

ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("America/New_York"));
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
        .append(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM))
        .appendLiteral(" ")
        .appendZoneText(TextStyle.SHORT).toFormatter();
System.out.println(zdt.format(formatter));

The advantage of this is that you can choose what style of timezone name you want. On the other hand, you might run into localisation issues, because I'm not sure if it is natural in all locales to put the timezone last.

like image 2
Sweeper Avatar answered Oct 18 '22 02:10

Sweeper