从lambda表达式中获取时区(java)

时间:2017-04-19 21:41:48

标签: java lambda

所以我想在java gui中创建2个下拉菜单,允许用户在时区之间进行选择并比较其中两个。但是我遇到的问题是尝试获取用户从lambda表达式中选择的时区的变量。我仍然是相当新的java所以任何帮助将不胜感激

这是给我问题的代码

 tz tz1= new tz();

ComboBox<String> cboNation = new ComboBox<>();
ObservableList<String> items = FXCollections.observableArrayList
  ("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");
cboNation.getItems().addAll(items);
cboNation.setValue(items.get(0));
cboNation.setOnAction(e -> 
{

currentIndex = items.indexOf(cboNation.getValue());
TimeZone tz = TimeZone.getTimeZone(timezones[currentIndex]);
String Timezone2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(tz);
tz1.firstdisplay = Timezone2;
});

gridPane.add(new Label(tz1.firstdisplay), 0, 0);

我希望能够从表达式中获取tz,并能够将其格式化为显示日期的字符串。如果有帮助的话,它会出现在gui上。

编辑 我尝试通过引用传递程序现在运行但时区没有显示在gui中。我添加了应该显示它们的行,但没有发生任何事情

2 个答案:

答案 0 :(得分:1)

tl; dr

定义ComboBox < ZoneOffset > offsetsMenu而不是<String>

offsetsMenu.setOnAction( ( ActionEvent actionEvent ) -> { this.chosenOffset = offsetsMenu.getValue(); } );
  • 我实际上并没有解决您的问题,但是简化了代码以消除问题。
  • 您太努力了。
    • 通过泛型使用智能对象而不是哑字符串来支持菜单。
    • 制作ZoneOffset类型的弹出菜单小部件,以避免在后备列表中查找。
  • 仅将现代的 java.time 类用于日期时间工作。在此,具体使用InstantZoneOffsetZoneIdZoneRules
    • 从不使用TimeZoneSimpleDateFormatjava.util.Date等。

时区与偏移量

偏移量

在时区之间进行选择,并比较两个时区。

...

((“ 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”); < / p>

这些不是时区。这些是偏移量,在本初子午线之前或之后数小时-数分钟-秒。

它们还不完整。偏移量可以是任何小时数-分钟-秒。各地的人们使用不同的偏移量。例如,现代印度使用+05:30的偏移量,比UTC提前五个半小时。

时区

时区远不止于此。时区是特定地区人民过去,现在和将来对偏移量的更改的历史记录。

Continent/Region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用2-4个字母的缩写,例如ESTIST,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

java.time

您正在使用的是可怕的旧类,现在这些旧类已被JSR 310中定义的现代 java.time 类取代。

如果要表示比UTC提前14小时的时间,请使用ZoneOffset

ZoneOffset offset = ZoneOffset.ofHours( 14 ) ;

表示比UTC提前五个半小时的时间。

ZoneOffset offset = ZoneOffset.ofHoursMinutes( 5 , 30 ) ;

更好,使用时区。该类为java.time.ZoneId,代替了旧版TimeZone

ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;

通过ZoneRules类向ZoneId对象询问当前使用的偏移量。

ZoneOffset offsetInUseNowInIndia = z.getRules().getOffset( Instant.now() ) ;

在时区之间进行选择,并比较两个时区

让用户选择时区而不是时区偏移会更有意义。但是,如果您坚持要抵消...

列出类型为ZoneOffset而不是字符串的列表。

List< ZoneOffset > offsets ;

让我们在特定时刻使用所有偏移量。

Instant instant = Instant.now() ;  // Capture the current moment as seen in UTC.

循环所有时区。对于每个,获取规则,并查询偏移量。存储在一组TreeSet中以消除重复项(许多区域可能在任何特定时刻共享特定的偏移量)。

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 );

偏移量= [+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]

输入offsets的副本以创建所需的列表。 (我不使用JavaFX。)

您问:

将其格式化为显示日期的字符串

以我们的代码中的Instant对象为准,以UTC的当前时刻为准,并应用ZoneOffset得到一个OffsetDateTime。同样,这里没有时区,只有偏移量。我建议您改用时区(ZoneIdZoneOffsetZonedDateTimeOffsetDateTime)。但是您要求补偿,所以我们开始吧。

如果您列出的是文本而不是ZoneOffset对象,请创建ZoneOffset对象。

ZoneOffset offset = ZoneOffset.of( "+05:30" ) ;

将偏移量应用于Instant对象,从而产生OffsetDateTime

OffsetDateTime odt = instant.atOffset( offset ) ;

以标准ISO 8601格式生成文本。

String output = odt.toString() ;

odt.toString()= 2020-02-13T02:16:57.883278 + 05:30

与您所需的格式接近。您可以在中间替换T。并且您忽略了文本的偏移量。我不建议这样做,因为它会使阅读此值的人产生歧义。但是,如果您坚持使用,请使用忽略了偏移量的预定义常量DateTimeFormatter对象,然后替换T

String output = 
    odt
    .format( DateTimeFormatter.ISO_LOCAL_DATE_TIME )
    .replace( "T" , " " )
;

输出= 2020-02-13 02:16:57.883278

在JavaFX代码中使用数据类型

虽然我是JavaFX的新手,但您的弹出菜单小部件似乎支持泛型,并且可以表示智能对象而不是哑字符串。使您的ComboBox的类型为ZoneOffsetComboBox< ZoneOffset >)时,默认情况下,小部件似乎正在调用ZoneOffset.toString()以便用户在屏幕上显示文本。该toString方法在standard ISO 8601 format中生成文本。

现在,我们不再需要对偏移量列表进行查找。用户的选择本身就是ZoneOffset对象!无需进一步查看。

示例应用

我今天制作了我的第一个JavaFX应用程序,以演示此代码。

我不知道如何在Label上添加Scene,因此我将其留给读者练习。但是您可以在此处看到我们如何获得当前正在使用的所有偏移量的列表。然后,我们将ZoneOffset对象的列表分配给我们的弹出菜单小部件ComboBox< ZoneOffset >。当用户选择一个项目时,该项目本身就是列表中的ZoneOffset对象。因此,我们不再真正关心列表本身。这种策略在面向对象的用户界面框架中很常见。

运行该应用程序时,每次从列表中选择一项时,请观看控制台。

screenshot of demo app running with source code in the background

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();
    }
}

其他信息

答案 1 :(得分:0)

实际上你在lambda表达式中创建了TimeZone tz变量,并且你试图使用的是在你的lambda表达式之外的代码行 String Timezone2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(tz);。您应该将TimeZone tz变量声明为实例变量。