【问题标题】:Java - auto generate current day + user input timeJava - 自动生成当前日期+用户输入时间
【发布时间】:2018-05-06 11:42:24
【问题描述】:

目前我得到了一个带有 Date 数据类型的类“Flight”;出发和到达日期时间。

航班的添加由用户输入完成。日期应自动为当前日期,时间由用户选择。这意味着一个人只需输入 HH:mm。

目前,所有选择都有些混乱;时间戳、日期、当地时间等

对于这个问题,如何使用扫描仪进行简单的用户输入? 它应该采用今天的日期,添加包含时间的用户输入并将其加在一起以适合我的 Date 数据类型。

有人知道如何做到这一点,或者可以提供一些提示/最佳做法吗?

【问题讨论】:

  • 一个ggod源是Oracle Tutorial: Date Time。我建议您忽略许多过时的网站,这些网站教授同样过时的课程,例如 TimeStampDate。我当然理解您很容易感到困惑。
  • 您在开发控制台应用程序吗?
  • 是的控制台应用
  • LocalDate.now(ZoneId.of("Europe/Copenhagen").atTime(LocalTime.parse("17:45").atZone(ZoneId.of("Europe/Copenhagen"))。当然,将用户所需的时区声明为常量。

标签: java date time user-input


【解决方案1】:

由于您说您正在开发一个桌面应用程序并且您需要当前日期(在 PC 上),您可以使用 LocalDateLocalTime 的组合来实现您的目标。

代码如下:

public static void main(String[] args) {
LocalDate currentDate = LocalDate.now();
LocalTime userInputTime = null;

Scanner sc = new Scanner(System.in);
String dateTimeLine = sc.nextLine();
sc.close();

DateTimeFormatter dt = DateTimeFormatter.ofPattern("HH:mm");
userInputTime = LocalTime.parse(dateTimeLine,dtf);

System.err.println(LocalDateTime.of(currentDate, userInputTime));
}

首先,您使用LocalDate.now() 来生成当前日期(只有日期,没有小时、分钟和秒)。

接下来我们使用Scanner 来读取用户输入的字符串。 为了将字符串转换为LocalTime(此类仅包含有关一天中时间的信息,因此它具有小时、分钟、秒和纳秒的值),我们必须定义一个DateTimeFormatterDateTimeFormatter 定义了如何将字符串转换为 LocalTime 实例。

在我刚刚编写的代码中,我说过字符串输入的类型为“小时:分钟”。例如,可能的值为:

“10:25” - 10 小时 25 分钟,

“23:00” - 23 小时 0 分钟,

“02:13” - 2 小时 13 分钟。

创建LocalTime 对象后,我们要做的就是加入日期和时间对象,以创建LocalDateTime 对象,在这一行中完成:

LocalDateTime.of(currentDate, userInputTime)

假设您当前 PC 上的日期是 2018-05-06。如果您运行程序并在控制台中输入 10:50,则输出应为 LocalDateTime 对象,其中包含 2018-05-06 作为日期和 10 小时 50 分钟作为一天中的时间。

重要的是要注意这一行:

userInputTime = LocalTime.parse(dateTimeLine,dtf);

如果用户输入的字符串不满足要求的格式,将抛出java.time.format.DateTimeParseException

【讨论】:

  • 很好的答案,谢谢。我建议您将时区传递给LocalDate.now(ZoneId),以便您和下一个阅读您代码的程序员都知道您做出了有意识的选择(即使您指定了ZoneId.systemDefault())。
  • 我特别选择不添加区域,因为 OP 说这将是一个桌面应用程序。现在想象一下,如果他将Europe/London 设置为时区并将应用程序发送给澳大利亚的用户。如果澳大利亚用户在凌晨 4 点测试应用程序(例如),他将看到错误的日期值,因为时区。我可以添加 ZoneId.systemDefault() 行,但我相信这只会让用户感到困惑,因为他显然是一个编程初学者,并且添加一行什么都不做(除了让开发人员的意图明确)不会对他有太大帮助。跨度>
  • @Dvorog 通过ZoneId.systemDefault() 并不是“什么都不做”。它表明该行的作者在确定当前日期时了解时区在起作用。这是一个非常少的程序员理解的关键概念。向初学者程序员强调这一点更为重要。
  • 我想说一个初学者程序员可能不知道编程中的时区是什么或它们是如何工作的,我相信他首先需要学习如何使用简单的日期对象,然后再升级到区域和时间表示一般。我同意你在上一条评论中所说的一切,但我认为它不适用于初学者。
【解决方案2】:

tl;博士

LocalDateTime.of(                 // A `LocalDateTime` represents a set of *potential* moments along a range of about 26-27 hours. Not an actual moment, not a point on the timeline.
    LocalDate.systemDefault() ,   // Get the JVM’s current default time zone. Can change at any moment *during* runtime. When crucial, always confirm with the user.
    LocalTime.parse( "14:57" )    // Parse a string in standard ISO 8601 format as a time-of-day without regard for time zone or offset-from-UTC.
)                                 // Returns a `LocalDateTime` object.
.atZone(                          // Determine an actual moment, a point on the timeline.
    ZoneId( "Africa/Tunis" )      // Specify a time zone as `Continent/Region`, never as 3-4 letter pseudo-zones such as `PST`, `CST`, or `IST`.
)                                 // Returns a `ZonedDateTime` object.
.toInstant()                      // Extracts a `Instant` object from the `ZonedDateTime` object, always in UTC by default.

详情

目前有点混乱

日期时间处理是非常令人困惑的工作。

提示:

  • 忘记您自己的时区。考虑UTC,而不是您自己的狭隘时区。
  • 了解真实时刻(时间轴上的点)与时间轴上的日期时间近似值之间的差异,通常称为“本地”值。
  • 阅读 Stack Overflow 或 Internet 上有关日期时间的其他地方时要小心。您会遇到糟糕的建议和许多错误的解决方案。

有所有的选择;时间戳、日期、当地时间等

永远不要使用与 Java 的最早版本捆绑在一起的麻烦的旧旧日期时间类。切勿使用java.sql.Timestampjava.util.Datejava.util.Calendar 等。

➡ 仅使用 java.time package 中的类。

java.time 类是行业领先的日期时间框架。非常精心设计和深思熟虑,从成功的 Joda-Time 项目中吸取了教训。

有人知道如何做到这一点,或者可以提供一些提示/最佳做法吗?

你可能会后悔你问了。继续阅读。

目前我得到了一个带有 Date 数据类型的类“Flight”;出发和到达日期时间。

所以定义一个Flight 类。

在现实生活中,航班发生在足够远的未来,以至于我们冒着政客改变时区定义的风险。最常见的这些更改是采用/删除/更改Daylight Saving Time (DST)。但是出于各种原因,会定期进行任意更改。我们可以辩论这种变化的智慧/理智,但事实是它们发生了。它们经常发生,因为政治家似乎奇怪地倾向于在世界各地的许多国家进行这些改变。几乎所有人都在没有预先警告的情况下这样做,有时只是几周。甚至根本没有警告,如North Korea did this week

了解航空公司的实际运作方式,但从查看航空公司时刻表和各种读数来看,他们似乎试图使用出发地的分区时间来维持他们的时刻表。因此,如果航班计划在早上 6 点从洛杉矶国际机场起飞,他们会在 DST 转换的前一天、当天和后一天保留该航班时刻表。如果这确实是一般意图,那意味着在一个 DST 切换上坐下来消磨时间,同时试图在另一个 DST 切换上节省一个小时。显然,Amtrak adopts this practice 它的火车。让我们继续这种方法。

使用这种“虚构的”日程安排方法意味着我们无法确定未来早上 6 点的确切时间。所以我们需要在不应用时区的情况下记录我们对那个日期和那个时间的渴望。但是我们必须记录所需的时区,以便我们知道在什么情况下我们可以在以后确定确切的时刻,当时间足够接近时,我们不必担心时区变化。

所以我们使用LocalDateLocalTime 类型,因为它们故意缺少任何time zoneContinent/Region 格式的名称)或offset-from-UTC(小时-分钟-秒数)的概念。

ZoneId 类代表一个时区。

我在名称中使用了Unzoned 这个词来提醒我们这些值代表时间轴上的实际时刻。 “本地”这个词往往会使初学者感到困惑。

public class Flight {
    private String flightNumber;
    private LocalDate departureDateUnzoned;
    private LocalTime departureTimeUnzoned;
    private ZoneId departureZoneId ;
}

至于到达,存储该航班的预期时间跨度,而不是到达日期时间。您可以计算到达,因此无需存储它。 Duration 类跟踪小时数、分钟数、秒数和小数秒数。

为了计算到达,让我们使用 LocalDateTime 类返回一个值,该类简单地将 LocalDateLocalTime 组合在一起。我们可以使用这种类型在我们的类定义中创建一个departureUnzoned 成员变量。我选择了单独的LocalDateLocalTime 作为构建块,这样你就可以理解这些部分了。如此多的程序员使用他们的直觉而不是文档来假设LocalDateTime 表示某个地方的特定时刻,而实际上它的意思恰恰相反。 (你会在 Stack Overflow 上发现很多不正确的答案,建议 LocalDateTime 实际上应该使用 InstantZonedDateTime。)

让我们添加一个方法来计算到达时间。

public class Flight {
    private String flightNumber;
    private LocalDate departureDateUnzoned;
    private LocalTime departureTimeUnzoned;
    private ZoneId departureZoneId;
    private Duration duration;

    public LocalDateTime arrivalDateTimeUnzoned () {
        LocalDateTime departureUnzoned = LocalDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned );
        LocalDateTime ldt = departureUnzoned.plus( this.duration );
        return ldt;
    }
}

但是这个返回的LocalDateTime 没有考虑时区。通常,航空公司和火车会向客户报告调整到该地区时区的预计到达时间。所以我们需要一个到达时区。我们可以在计算到达时使用该区域,从而产生ZonedDateTimeZonedDateTime 一个特定的时刻,它时间线上的一个点,不像LocalDateTime。但请记住,如果我们将航班安排到未来,如果我们的代码在政客重新定义时区后运行,计算得到的 ZonedDateTime 将会改变。

public class Flight {
    private String flightNumber;
    private LocalDate departureDateUnzoned;
    private LocalTime departureTimeUnzoned;
    private ZoneId departureZoneId;
    private Duration duration;
    private ZoneId arrivalZoneId;

    public ZonedDateTime arrivalDateTimeZoned () {
        ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
        ZonedDateTime zdt = departureZoned.plus( this.duration );
        return zdt;
    }
}

回到问题中关于自动确定日期的部分。这需要一个时区。对于任何给定的时刻,日期在全球范围内都会有所不同。考虑一下。法国巴黎午夜过后几分钟是新的一天,而魁北克蒙特利尔仍然是“昨天”。

我们可以询问JVM当前的默认时区。

ZoneId userZoneId = ZoneId.systemDefault() ;

但在关键时,您必须与用户确认。

ZoneId userZoneId = ZoneId.of( "America/Montreal" ) ; 

所以现在我们可以添加您要求的构造函数,传递时间(LocalTime,并使用 JVM 的当前默认值猜测时区。

但我们仍然需要所有其他部分。所以默认日期并不能为我们节省太多。

public class Flight {
    private String flightNumber;
    private LocalDate departureDateUnzoned;
    private LocalTime departureTimeUnzoned;
    private ZoneId departureZoneId;
    private Duration duration;
    private ZoneId arrivalZoneId;

    // Constructor
    public Flight ( String flightNumber , LocalTime departureTimeUnzoned , ZoneId departureZoneId , Duration duration , ZoneId arrivalZoneId ) {
        this.flightNumber = flightNumber;
        this.departureTimeUnzoned = departureTimeUnzoned;
        this.departureZoneId = departureZoneId;
        this.duration = duration;
        this.arrivalZoneId = arrivalZoneId;

        // Determine today’s date using JVM’s current default time zone. Not advisable in many business scenarios, but specified by our Question at hand.
        ZoneId z = ZoneId.systemDefault();
        LocalDate today = LocalDate.now( z );
        this.departureDateUnzoned = today;
    }

    public ZonedDateTime arrivalDateTimeZoned () {
        ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
        ZonedDateTime zdt = departureZoned.plus( this.duration );
        return zdt;
    }
}

让我们添加一个toString 报告方法。

我们将日期时间值表示为标准ISO 8601 格式的字符串。 java.time 类在解析/生成字符串时使用这些标准格式。末尾的Z 发音为Zulu,意思是UTC

虽然航空公司和火车会向他们所在地区时区的客户报告日期时间,但我们可以假设他们在内部仅使用 UTC。 Instant 类专门表示 UTC 中的值。所以我们的toStringZonedDateTime 对象中提取Instant 对象。

我们添加了一个main 方法进行演示。这是完整的课程,包括import 等。

package com.basilbourque.example;

import java.time.*;

public class Flight {
    private String flightNumber;
    private LocalDate departureDateUnzoned;
    private LocalTime departureTimeUnzoned;
    private ZoneId departureZoneId;
    private Duration duration;
    private ZoneId arrivalZoneId;

    // Constructor
    public Flight ( String flightNumber , LocalTime departureTimeUnzoned , ZoneId departureZoneId , Duration duration , ZoneId arrivalZoneId ) {
        this.flightNumber = flightNumber;
        this.departureTimeUnzoned = departureTimeUnzoned;
        this.departureZoneId = departureZoneId;
        this.duration = duration;
        this.arrivalZoneId = arrivalZoneId;

        // Determine today’s date using JVM’s current default time zone. Not advisable in many business scenarios, but specified by our Question at hand.
        ZoneId z = ZoneId.systemDefault();
        LocalDate today = LocalDate.now( z );
        this.departureDateUnzoned = today;
    }

    public ZonedDateTime arrivalDateTimeZoned () {
        ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
        ZonedDateTime zdt = departureZoned.plus( this.duration );
        return zdt;
    }

    @Override
    public String toString () {
        ZonedDateTime departureZoned = ZonedDateTime.of( this.departureDateUnzoned , this.departureTimeUnzoned , this.departureZoneId );
        String flightInUtc = departureZoned.toInstant().toString() + "/" + this.arrivalDateTimeZoned().toInstant().toString();
        return "Flight{ " +
                "flightNumber='" + this.flightNumber + '\'' +
                " | departureDateUnzoned=" + this.departureDateUnzoned +
                " | departureTimeUnzoned=" + this.departureTimeUnzoned +
                " | departureZoneId=" + this.departureZoneId +
                " | departureZoned=" + departureZoned +
                " | duration=" + this.duration +
                " | arrivalZoneId=" + this.arrivalZoneId +
                " | calculatedArrival=" + this.arrivalDateTimeZoned() +
                " | flightInUtc=" + flightInUtc +
                " }";
    }

    public static void main ( String[] args ) {
        LocalTime lt = LocalTime.of( 6 , 0 ); // 6 AM.
        Flight f = new Flight( "A472" , lt , ZoneId.of( "America/Los_Angeles" ) , Duration.parse( "PT6H37M" ) , ZoneId.of( "America/Montreal" ) );
        String output = f.toString();
        System.out.println( output );
    }
}

运行时。

航班{ flightNumber='A472' |出发日期未分区=2018-05-06 |出发时间未分区=06:00 |离境区ID=美国/洛杉矶|出发分区=2018-05-06T06:00-07:00[美国/洛杉矶] |持续时间=PT6H37M |到达ZoneId=美国/蒙特利尔|计算到达=2018-05-06T12:37-07:00[美国/洛杉矶] | flightInUtc=2018-05-06T13:00:00Z/2018-05-06T19:37:00Z }

要从控制台使用此功能,请向用户询问 24 小时制的时间。解析输入字符串。

String input = "14:56" ;  // 24-hour clock.
LocalTime lt = LocalTime.parse( input ) ;

对于现实世界的工作来说,这还远远不够。但希望它可以成为一个教育示例。


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多