tl;dr
Defining ComboBox < ZoneOffset > offsetsMenu
rather than <String>
:
offsetsMenu.setOnAction( ( ActionEvent actionEvent ) -> { this.chosenOffset = offsetsMenu.getValue(); } );
- I did not actually solve your problem, but simplified your code to eliminate the problem.
- You are working too hard.
- Use smart objects rather than dumb strings to back your menu, via generics.
- Make your pop-up menu widget of type
ZoneOffset
to avoid look-ups into the backing list.
- Use modern java.time classes only for date-time work. Here, specifically use
Instant
, ZoneOffset
, ZoneId
, and ZoneRules
.
- Never use
TimeZone
, SimpleDateFormat
, java.util.Date
, and so on.
Time zone versus offset
Offset
choose between time zones and compare two of them.
…
("GMT+14", "GMT+13", "GMT+12", "GMT+11","GMT+10", "GMT+9", "GMT+8",
"GMT+7", "GMT+6", "GMT+5", "GMT+4", "GMT+3", "GMT+2", "GMT+1",
"GMT0" ,"GMT-1", "GMT-2", "GMT-3", "GMT-4", "GMT-5", "GMT-6",
"GMT-7", "GMT-8", "GMT-9", "GMT-10", "GMT-11", "GMT-12", "GMT-13", "GMT-14");
Those are not time zones. Those are offsets, a number of hours-minutes-seconds ahead or behind the prime meridian.
And they are not complete. An offset can be any number of hours-minutes-seconds. People in various places use various offsets. For example, modern India uses an offset of +05:30
, five and a half hours ahead of UTC.
Time zone
A time zone is much more. A time zone is a history of past, present, and future changes to the offset used by the people of a particular region.
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 EST
or IST
as they are not true time zones, not standardized, and not even unique(!).
java.time
You are using terrible old classes that are now supplanted by the modern java.time classes defined in JSR 310.
If you want to represent an offset of 14 hours ahead of UTC, use ZoneOffset
.
ZoneOffset offset = ZoneOffset.ofHours( 14 ) ;
Represent an offset of five and a half hours ahead of UTC.
ZoneOffset offset = ZoneOffset.ofHoursMinutes( 5 , 30 ) ;
Even better, use time zones. The class for that is java.time.ZoneId
, replacing the legacy TimeZone
.
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
Ask the ZoneId
object for the offset currently in use, via the ZoneRules
class.
ZoneOffset offsetInUseNowInIndia = z.getRules().getOffset( Instant.now() ) ;
choose between time zones and compare two of them
It would make more sense to let the user choose time zones rather than zone offsets. But if you insist on offsets…
Make your list of type ZoneOffset
rather than string.
List< ZoneOffset > offsets ;
Let's get all offsets in use at a particular moment.
Instant instant = Instant.now() ; // Capture the current moment as seen in UTC.
Loop all the time zones. For each, get the rules, and interrogate for the offset. Store in a sorted set, a TreeSet
, to eliminate duplicates (many zones may share a particular offset at any particular moment).
Set < ZoneOffset > offsets = new TreeSet <>(); // A sorted set of `ZoneOffset`, eliminates duplicates.
Instant instant = Instant.now(); // Capture the current moment as seen in UTC.
for ( String zoneIdString : ZoneId.getAvailableZoneIds() )
{
ZoneId zoneId = ZoneId.of( zoneIdString );
ZoneOffset offset = zoneId.getRules().getOffset( instant );
offsets.add( offset );
}
Dump to console.
System.out.println( "offsets = " + offsets );
offsets = [+14:00, +13:45, +13:00, +12:00, +11:00, +10:30, +10:00, +09:30, +09:00, +08:45, +08:00, +07:00, +06:30, +06:00, +05:45, +05:30, +05:00, +04:30, +04:00, +03:30, +03:00, +02:00, +01:00, Z, -01:00, -02:00, -03:00, -03:30, -04:00, -05:00, -06:00, -07:00, -08:00, -09:00, -09:30, -10:00, -11:00, -12:00]
Feed a copy of offsets
to make the list you need. (I do not use JavaFX.)
You asked:
format it into a string that shows the date
Take the Instant
object in our code, the current moment as seen in UTC, and apply the ZoneOffset
to get a OffsetDateTime
. Again, no time zones here, just offsets. I recommend you use time zones instead (ZoneId
versus ZoneOffset
, ZonedDateTime
versus OffsetDateTime
). But you asked for offsets, so here we go.
If you list is text instead of ZoneOffset
objects, make a ZoneOffset
object.
ZoneOffset offset = ZoneOffset.of( "+05:30" ) ;
Apply the offset to the Instant
object, producing a OffsetDateTime
.
OffsetDateTime odt = instant.atOffset( offset ) ;
Generate text in standard ISO 8601 format.
String output = odt.toString() ;
odt.toString() = 2020-02-13T02:16:57.883278+05:30
That is close to your desired format. You can replace the T
in the middle. And you omit the offset from your text. I do not recommend that, as it creates ambiguity for the person reading such a value. But if you insist, use the predefined constant DateTimeFormatter
object that omits the offset, then replace the T
.
String output =
odt
.format( DateTimeFormatter.ISO_LOCAL_DATE_TIME )
.replace( "T" , " " )
;
output = 2020-02-13 02:16:57.883278
Use data types in your JavaFX code
While I am new to JavaFX, it seems that your pop-up menu widget supports generics, and can represent smart objects rather than dumb strings. When making your ComboBox
of type ZoneOffset
(ComboBox< ZoneOffset >
), the widget seems to be calling ZoneOffset.toString()
by default for text to use displayed on screen to the user. That toString
method generates text in standard ISO 8601 format.
Now we no longer need to do a look-up into the list of offsets. The user's choice is itself a ZoneOffset
object! No need to look further.
Example app
I made my very first JavaFX app today, to demo this code.
I do not know how to add a Label
to the Scene
, so I will leave that as an exercise for the reader. But you can see here how we get a list of all offsets currently in use at this moment. And we assign that list of ZoneOffset
objects to our pop-up menu widget, the ComboBox< ZoneOffset >
. When user selects an item, that item is itself the ZoneOffset
object from the list. So we no longer really care about the list per se. This strategy is common in object-oriented user-interface frameworks.
When you run the app, watch the console every time you select an item from the list.

package work.basil.example;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Set;
import java.util.TreeSet;
/**
* JavaFX App
* Untested code example.
* <p>
* © 2020 Basil Bourque.
* Use by terms of ISC License: https://www.isc.org/licenses/ (basically, do anything but sue me)
*/
public class App extends Application
{
ZoneOffset chosenOffset;
@Override
public void start ( Stage stage )
{
var javaVersion = SystemInfo.javaVersion();
var javafxVersion = SystemInfo.javafxVersion();
Set < ZoneOffset > offsets = new TreeSet <>(); // A sorted set of `ZoneOffset`, eliminates duplicates.
Instant instant = Instant.now(); // Capture the current moment as seen in UTC.
for ( String zoneIdString : ZoneId.getAvailableZoneIds() )
{
ZoneId zoneId = ZoneId.of( zoneIdString );
ZoneOffset offset = zoneId.getRules().getOffset( instant );
offsets.add( offset );
}
System.out.println( "offsets = " + offsets );
ComboBox < ZoneOffset > offsetsMenu = new ComboBox <>();
ObservableList < ZoneOffset > items = FXCollections.observableArrayList( offsets );
offsetsMenu.getItems().addAll( items );
offsetsMenu.getSelectionModel().selectFirst(); // Might be more orthodox than: offsetsMenu.setValue( items.get( 0 ) );
this.chosenOffset = offsetsMenu.getSelectionModel().getSelectedItem(); // Record the default we assign.
offsetsMenu.setOnAction( ( ActionEvent actionEvent ) ->
{
this.chosenOffset = offsetsMenu.getValue();
System.out.println( "DEBUG - User chose ZoneOffset: " + this.chosenOffset );
} );
var scene = new Scene( new StackPane( offsetsMenu ) , 640 , 480 );
stage.setScene( scene );
stage.show();
}
public static void main ( String[] args )
{
launch();
}
}
Further info