我正在尝试创建一种将约会添加到arraylist约会日历的方法。此方法将验证日期,以查看用户输入是否等于我的代码中的SimpleDateFormat,约会的startTime和endTime,并查看将来是否约会。
我尝试使用java Date api进行检查,但是当我尝试扩展类以获取对属性的访问权限时,总是会在编译时出错。所以总的来说,我的问题是将约会类型的对象与日期类型的对象进行比较的最佳方法是什么?我尝试使用accesors来获取getDate()以及startTime和endTime,但它也不允许我获取它们。
public AppointmentDate(String appString)
{
// 1) split ithe string into Date/from/to
// 2) consturct the Date object for the appDate
// 3) consturct the Date object for the startTime
// 4) consturct the Date object for the endTime
String[] appDetails = appString.split(",");
if(appDetails.length == 2)
{
try {
SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
this.appDate = df.parse(appDetails[0]);
DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy,mm:HH");
String dFormat = appDetails[0] + "," + appDetails[1];
this.startTime = formatter.parse(dFormat);
dFormat = appDetails[0] + "," + appDetails[2];
this.endTime = formatter.parse(dFormat);
}
catch (Exception ex)
{
}
}
else
{
System.out.print("User Date is Invalid");
}
}
public void setStartTime(Date startTime)
{
this.startTime = startTime;
}
public Date getStartTime()
{
return startTime;
}
public void setEndTime(Date endTime)
{
this.endTime = endTime;
}
public Date getEndTime()
{
return endTime;
}
public void setAppdate(Date appDate)
{
this.appDate = appDate;
}
public Date getAppDate()
{
return appDate;
}
public void add(Appointment a)
{
if (a.equals(a.getDate()))
{
if(a.getStartTime() < a.getEndTime())
{
}
}
else
{
System.out.print("");
}
}
答案 0 :(得分:4)
您的课堂练习代码在错误的位置。您将其停留在类的顶部,这在语法上是不正确的。我们可以在顶部运行代码,例如static block,但需要将其标记为static { … }
。根据我的经验,静态块并不常用。当然,这不是您在那做的正确地方。
main
方法相反,您应该使用a main
method。这个非OOP的小东西是一个技巧,一个技巧,可以解决chicken-or-the-egg的难题,使我们从无应用程序运行到我们的OOP天堂概念,周围漂浮着一堆物体,互相传递消息。
第一次学习Java时,请不要尝试理解main
方法的所有语法和用途。只是将其视为使应用程序运行所必需的邪恶,它仅仅是执行应用程序的入口。重点学习OOP概念和实践。稍后,main
方法和语法会更有意义。
这里是示例代码的简化重写。为了简单起见,我们仅使用LocalDate
,足以显示(a)main
方法和(b)getter / setter accessor methods。
package com.basilbourque.example;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
public class AppointmentDate {
private LocalDate localDate;
// Constructor
public AppointmentDate ( LocalDate localDate ) {
this.localDate = localDate;
}
public LocalDate getLocalDate ( ) {
return localDate;
}
public void setLocalDate ( LocalDate localDate ) {
this.localDate = localDate;
}
@Override
public String toString ( ) {
return "AppointmentDate{ " +
"localDate=" + localDate +
" }";
}
// Not really a part of this class. A `main` method is just a hack to get our app launched.
public static void main ( String[] args ) {
String input = "23/01/2018";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu" );
LocalDate ld = LocalDate.parse( input , f );
AppointmentDate ad = new AppointmentDate( ld );
ad.setLocalDate( ld.plusWeeks( 1 ) );
LocalDate newValue = ad.getLocalDate();
System.out.println( newValue.toString() ); // Generate text representing the value of this `LocalDate` object in standard ISO 8601 format.
List < AppointmentDate > list = new ArrayList <>( 3 );
list.add( ad );
list.add( new AppointmentDate( LocalDate.parse( "2018-02-13" ) ) );
list.add( new AppointmentDate( LocalDate.parse( "2018-12-21" ) ) );
System.out.println( list );
}
}
2018-01-30
[AppointmentDate {localDate = 2018-01-30},AppointmentDate {localDate = 2018-02-13},AppointmentDate {localDate = 2018-12-21}]
您使用的是可怕的旧日期时间类,而这些类早已被 java.time 类所取代。永远不要使用Date
,Calendar
,SimpleDateFormat
等。
虽然约会跟踪在直观上看起来很简单,但实际上您正在研究一个非常棘手的主题。
核心问题是世界各地的政客都喜欢重新定义其管辖范围内的时区。他们经常这样做。他们在相对安静和动荡时期都这样做。
近几十年来,美国和加拿大多次改变了补偿标准。在过去的几年中,土耳其和俄罗斯已经改变了多次使用DST的想法。
政客们很少提前通知就改变了时区。尽管这在越来越多的计算机化社会中引起了越来越大的骚动,但通知似乎越来越短。就在上个月,Morocco announced的国家/地区将永久保留夏令时(DST),并在星期五取消了原定于该周日的DST过渡,并留下0个工作日警告-这对IT员工来说真是一团糟。 North Korea slipped their clock今年五月与韩国同步了一个半小时,显然立即生效(根本没有提前通知)。
这些频繁且不可预测的变化意味着我们无法以负责任的方式将未来的约会作为时间轴上的特定点进行跟踪。当我们说“ 1月23日下午3点”之类的话时,通常是指在政客可能对时钟进行更改后的 。
因此,我们必须将将来的约会存储为日期和时间,而没有时区或UTC偏移。然后,在计算日历时,我们必须针对当天当前定义的预期时区动态应用规则。如果我们今天进行一次动态确定,然后在三天内进行一次动态确定,如果政客们宣布了时区定义的更改,并且我们能够在操作中更新tzdata
数据文件系统,数据库引擎,Java虚拟机和各种库,那么我们将在其他时间到达。
LocalDateTime
Java中的Local…
类型故意缺少任何时区或从UTC偏移的概念。所以他们不能代表一个时刻。因此,我们从不使用它们来查明过去发生的实际事件。但是这些类型是我们未来约会所需要的。
LocalDateTime
类表示一个带有一天中时间的日期,没有任何区域/偏移量。
LocalDate ld = LocalDate.of( 2018 , Month.JANUARY , 23 ) ;
LocalTime lt = LocalTime.of( 15 , 0 ) ; // 3 PM in 24-hour time.
LocalDateTime ldt= LocalDateTime.of( ld , lt ) ;
ZonedDateTime
在计算日历时,当我们需要特定的时刻时,我们应用时区(ZoneId
)来获得ZonedDateTime
对象。
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ; // Determine a moment, a specific point on the timeline.
Instant
我们可以通过提取Instant
来查看UTC中的同一时刻。
Instant instant = zdt.toInstant() ; // Adjust to UTC.
Duration
通常最好将约会存储为起点和持续时间。无需存储停止点,因为可以计算得出。
Duration d = Duration.ofHours( 1 ) ; // A one-hour appointment.
尽管我们通常希望调整到一个时区以向用户显示,但通常是在幕后进行,最佳做法是跟踪UTC中的时刻。因此,应将按时刻计算的约会的起点和终点作为一对Instant
对象来完成。
Instant start = ldt.atZone( z ).toInstant() ;
Instant stop = start.plus( d ) ;
Interval
我们可以利用一个类来表示这对Instant
对象Interval
。
此类可在 ThreeTen-Extra 库中找到,该库由与Joda-Time,JSR 310和 java.time 项目相同的人领导的项目,斯蒂芬·科尔本。
此类具有非常方便的比较方法,例如abuts
,overlaps
,contains
等。您可能希望在调度应用程序中使用这些方法。
Appointment.java
将所有内容放在一起,我们得到一个这样的类:
package com.basilbourque.example;
import org.threeten.extra.Interval;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class Appointment {
private LocalDateTime start;
private Duration duration;
// Constructor.
public Appointment ( LocalDateTime start , Duration duration ) {
this.start = start;
this.duration = duration;
}
// Might add some getter/setter methods in here.
// Dynamically determine the start and stop points of this appointment, given today’s definition of the intended time zone.
public Interval toInterval ( ZoneId zoneId ) {
ZonedDateTime zdtStart = this.start.atZone( zoneId );
Interval interval = Interval.of( zdtStart.toInstant() , this.duration );
return interval;
}
}
通过调用Interval
方法生成toInterval
时,您可能需要各个开始时刻和结束时刻。
Instant start = interval.getStart() ;
Instant stop = interval.getEnd() ;
根据定义,这两个Instant
对象位于UTC中。如果您想通过特定地区的人们所使用的挂钟时间来查看它们,请应用ZoneId
来获取ZonedDateTime
对象。
ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdtStart = start.atZone( z ) ; // Adjust from UTC to some time zone. Same moment, same point on the timeline, different wall-clock time.
ZonedDateTime zdtStop = stop.atZone( z ) ;
您询问是否要检查此约会是否将来。同样,我们需要一个时区来正确回答。目前,世界各地的时区范围大约为26到27小时。因此,在当前时刻的许多小时内,如果不考虑时区,我们就无法判断LocalDateTime
是将来还是过去。
因此,让我们为将来需要增加时区的方法进行测试。
// Dynamically determine if this appointment will be in the future for some specific time zone.
public Boolean isFuture ( ZoneId zoneId ) {
Objects.requireNonNull( zoneId , "Must pass a time zone to determine if an appointment is in the future. Message # e1c64bc1-9a44-4d15-b20d-e68414fb5ab5.");
ZonedDateTime zdtStart = this.start.atZone( zoneId );
ZonedDateTime zdtNow = ZonedDateTime.now( zoneId );
boolean isInTheFuture = zdtNow.isBefore( zdtStart );
return isInTheFuture ;
}
在动态确定力矩时继续使用相同的主题,让我们添加一些方法来返回开始力矩(包括端点)和停止力矩(包括端点)。如上所述,这需要传递一个时区。
调用程序员可以自己完成这项工作。但是我怀疑可能经常需要这样做,以保证方便地添加这些方法。
// Get start moment for a particular time zone.
public ZonedDateTime toStartMoment ( ZoneId zoneId ) {
ZonedDateTime zdt = this.toInterval( zoneId ).getStart().atZone( zoneId );
return zdt;
}
// Get stop moment for a particular time zone.
public ZonedDateTime toStopMoment ( ZoneId zoneId ) {
ZonedDateTime zdt = this.toInterval( zoneId ).getEnd().atZone( zoneId );
return zdt;
}
请注意,我没有使用get…
来命名这些方法。访问器方法,获取器和设置器,按照惯例,意味着访问存储在对象中的简单属性。但是这里我们不存储ZonedDateTime
对象。这些是动态确定的,因此使用get…
方法可能会产生误导。相反,我尝试遵循naming conventions laid down in the java.time project。
从{em> java.time 项目学习的另一课是immutable objects模式。
某些种类的类适合只读,创建但不修改。 java.time 类肯定符合条件。例如,预计发票会“变异”(更改),作为程序员,直觉上我不希望发票上的日期更改,除非我用新对象明确替换了日期。因此,我希望发票是 mutable 对象,但是我希望存储在发票上的LocalDate
对象是 mutmutable 。
我怀疑我们的Appointment
类也可能最好地设计为不可变的。因此,我们不涉及任何setter
方法。要在您的调度应用程序中有效地更改约会,请基于现有Appointment
对象的某些值创建一个新的Appointment
对象。在 java.time 类中,请注意如何使用各种with
方法来完成此操作,其中这些方法会根据原始值(但进行一些更改)返回一个新对象。
Appointment.java
版本2 让我们将所有这些放到一个示例类中。
然后让我们添加一个main
方法来练习此类。首先,我们创建一个约会,并查看其在UTC中动态确定的时刻。第二,我们在集合中收集一些Appointment
对象。
我们添加了toString
方法替代来报告对象的状态。
package com.basilbourque.example;
import org.threeten.extra.Interval;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
// An example class to show date-time handling for future appointments.
// Not necessarily ready for production use. Use at your own risk.
// Methods named according to the java.time naming conventions:
// https://docs.oracle.com/javase/tutorial/datetime/overview/naming.html
public class Appointment {
private LocalDateTime start;
private Duration duration;
// Constructor.
public Appointment ( LocalDateTime start , Duration duration ) {
this.start = start;
this.duration = duration;
}
// Dynamically determine the start and stop points of this appointment, given today’s definition of the intended time zone.
public Interval toInterval ( ZoneId zoneId ) {
Objects.requireNonNull( zoneId , "Must pass a time zone to get the start/stop interval of an appointment. Message # bbf021e6-baa7-468d-83ad-cf73acb6702e." );
ZonedDateTime zdtStart = this.start.atZone( zoneId );
Interval interval = Interval.of( zdtStart.toInstant() , this.duration );
return interval;
}
// Get start moment for a particular time zone.
public ZonedDateTime toStartMoment ( ZoneId zoneId ) {
ZonedDateTime zdt = this.toInterval( zoneId ).getStart().atZone( zoneId );
return zdt;
}
// Get stop moment for a particular time zone.
public ZonedDateTime toStopMoment ( ZoneId zoneId ) {
ZonedDateTime zdt = this.toInterval( zoneId ).getEnd().atZone( zoneId );
return zdt;
}
// Dynamically determine if this appointment will be in the future for some specific time zone.
public Boolean isFuture ( ZoneId zoneId ) {
Objects.requireNonNull( zoneId , "Must pass a time zone to determine if an appointment is in the future. Message # e1c64bc1-9a44-4d15-b20d-e68414fb5ab5." );
ZonedDateTime zdtStart = this.start.atZone( zoneId );
ZonedDateTime zdtNow = ZonedDateTime.now( zoneId );
boolean isInTheFuture = zdtNow.isBefore( zdtStart );
return isInTheFuture;
}
// -----------| Object overrides |---------------------------
@Override
public String toString ( ) {
return "Appointment{ " +
"start=" + start +
" | duration=" + duration +
" }";
}
// -----------| main |-------------
public static void main ( String[] args ) {
// See if a new appointment is in the future.
Appointment a = new Appointment( LocalDateTime.of( 2018 , 12 , 25 , 0 , 0 , 0 , 0 ) , Duration.ofHours( 2 ) );
ZoneId z = ZoneId.of( "America/Montreal" );
System.out.println( "For time zone: " + z + ", appointment interval is: " + a.toInterval( z ) );
System.out.println( "Start: " + a.toStartMoment( z ) );
System.out.println( "Stop: " + a.toStopMoment( z ) );
Boolean isFuture = a.isFuture( z );
System.out.println( a.toString() + " is future t/f: " + isFuture );
// Collect some appointments.
List < Appointment > list = new ArrayList <>( 3 );
list.add( a );
list.add( new Appointment( LocalDateTime.of( 2018 , 12 , 13 , 15 , 0 , 0 , 0 ) , Duration.ofMinutes( 90 ) ) );
list.add( new Appointment( LocalDateTime.of( 2018 , 12 , 30 , 16 , 0 , 0 , 0 ) , Duration.ofHours( 1 ) ) );
System.out.println( list );
}
}
运行时。
对于时区:美国/蒙特利尔,约会时间间隔为:2018-12-25T05:00:00Z / 2018-12-25T07:00:00Z
开始:2018-12-25T00:00-05:00 [美国/蒙特利尔]
停止:2018-12-25T02:00-05:00 [美国/蒙特利尔]
预约{start = 2018-12-25T00:00 | duration = PT2H}是将来的t / f:是
[约会{start = 2018-12-25T00:00 | duration = PT2H},预约{start = 2018-12-13T15:00 | duration = PT1H30M},预约{start = 2018-12-30T16:00 |持续时间= PT1H}]
java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和SimpleDateFormat
。
目前位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310。
您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*
类。
在哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如Interval
,YearWeek
,YearQuarter
和more。