tl;dr
Instant // Represent a moment in UTC.
.parse( // Generate a `Instant` object from the content of text input.
"2019-08-13T18:00:00Z" // String in standard ISO 8601 format.
) // Returns a `Instant` object.
.atZone( // Adjust from UTC to the wall-clock time used by the people of a particular region (a time zone).
ZoneId.of( "Europe/London" ) // Specify a time zone using name in proper `Continent/Region` format. Never use 2-4 letter pseudo-zones such as `BST`.
) // Returns a `ZonedDateTime` object.
.toLocalTime() // Extract the time-of-day, without a date and without a time zone or offset. Returns a `LocalTime` object.
.format( // Generate text representing the content of this `LocalTime` object.
DateTimeFormatter
.ofLocalizedTime ( FormatStyle.SHORT ) // Automatically localize while generating a `String`.
.withLocale ( Locale.UK ) // Locale determines the human language and cultural norms to use in localizing.
) // Returns a `String` object.
19:00
Avoid legacy date-time classes
Your are mixing the terrible legacy classes (SimpleDateFormat
, Date
) with the modern java.time classes. Don’t do that. Use only java.time.
Instant
= moment in UTC
Skip your first two lines of code. Your input string "2019-08-13T18:00:00Z"
is in standard ISO 8601 format. These standard formats are used by default by the java.time classes when parsing/generating strings. So no need to specify a formatting pattern.
String input = "2019-08-13T18:00:00Z" ;
Instant instant = Instant.parse( input ) ;
instant.toString(): 2019-08-13T18:00:00Z
Instant
is not flexible
I suspect your problem was in your attempt to format the value within a Instant
. The Instant
class is a basic building-block class within java.time. It merely represents a moment in UTC. It is not intended for things such as flexible generation of strings.
The more flexible classes are OffsetDateTime
& ZonedDateTime
classes.
ZonedDateTime
Apply a ZoneId
to your Instant
to adjust into a time zone, rendering a ZonedDateTime
object.
ZoneId z = ZoneId.of( "Europe/London" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
zdt.toString(): 2019-08-13T19:00+01:00[Europe/London]
You seem to want to focus on the time-of-day alone. Extract a LocalTime
object.
LocalTime lt = zdt.toLocalTime ();
lt.toString(): 19:00
For the London region in Daylight Saving Time (DST) on that date, the offset-from-UTC is one hour ahead. So we see the time-of-day is 7 PM versus the 6 PM of UTC.
Proper time zone
By the way BST
is not a time zone. I suggest you avoid using these pseudo-zones.
Specify a proper time zone name in the format of Continent/Region
, such as America/Montreal
, Africa/Casablanca
, or Pacific/Auckland
. Never use the 2-4 letter abbreviation such as BST
or EST
or IST
as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
Use smart objects, not dumb strings
Your example code suggests you are too focused on strings. Use smart objects, not dumb strings.
Get your objects straight, using appropriate types. Generating strings should be the last step, a side-effort, akin to localization. Your business logic should be done by using proper objects, not by manipulating strings.
Localization
And speaking of localization:
Locale locale = Locale.UK;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedTime ( FormatStyle.MEDIUM ).withLocale ( locale );
String output = lt.format ( f );
19:00:00
Switch the locale to Locale.US
for a different kind of result:
7:00:00 PM
All the code above was run in Java 13 early-access with the ThreeTen-Backport library per your needs stated in the Question.
import org.threeten.bp.* ;
import org.threeten.bp.format.* ;
Note to the reader: The ThreeTen-Backport library is further adapted for early Android in the ThreeTenABP library. See How to use ThreeTenABP in Android. If using Android 26 and later, the java.time classes are bundled, so you do not need the back-port at all.