简体   繁体   English

Java:在Calendar对象中设置日期时,我可以使用日/月/年参考吗?

[英]Java: when setting a date in a Calendar object, can I use day/month/year references?

I'm pretty new to Java world, and I'm practicing a lot. 我是Java世界的新手,我练习了很多。 My last exercize is about an Apartment Renting program. 我最后一次锻炼是关于公寓租赁计划。 My question refers to the "booking" part in the Manager class, in which I have to check if the requested arrival date is linked to the low, medium or high season lot in the array double tariffs[]. 我的问题是指Manager类中的“预订”部分,我必须检查所请求的到达日期是否与阵列双重关税[]中的低,中,高季节相关联。

Here is the portion of code with the bookApartment() method, where code and id are the keys in the HashMaps of Apartments and Clients (booking is correct only if the arrival date is a Saturday): 以下是使用bookApartment()方法的代码部分,其中codeid是HashMaps of Apartments and Clients中的密钥(仅当到达日期为星期六时,预订才正确):

public Booking bookAppartment(String code, String id, int day, int month, int year, int nweeks) throws WrongDay {
    Calendar date = Calendar.getInstance();
    date.set(year, month-1, day);
    int weekday = date.get(Calendar.DAY_OF_WEEK);
    Booking book=null;
    if(code!="" && id!=""){
        if(weekday!=Calendar.SATURDAY)
            throw new WrongDay(date);
        else{
            for(Map.Entry<String , Apartment> apt : apts.entrySet()){
                for(Map.Entry<String, Client> client : clients.entrySet()){
                    if(apt.getKey()==code && client.getKey()==id && weekday==Calendar.SATURDAY){
                        book = new Booking(client.getValue(), apt.getValue(), d, m, y, nweeks);
                        bookings.add(book);
                        book.setPrice(d, m, y, apt.getValue().getTariffs(), nweeks);
                        break;
                    }
                }
            }
        }
    }   
    return book;
}

And here I attach the constructor of the Booking object and my personal override of the setPrice() method, which calculates the entire booking price selecting the correct tariffs[] lot: 在这里,我附加了Booking对象的构造函数和我的setPrice()方法的个人覆盖,它计算整个预订价格,选择正确的关税[]批次:

public class Booking {

    private Client client;
    private Apartment apt;
    private double price;
    private int numweeks;
    private static int day, month, year;

    public Booking(Client client, Apartment apt, int day, int month, int year, int numweeks){
        this.client = client;
        this.apt = apt;
        Booking.day = day;
        Booking.month = month;
        Booking.year = year;
        this.numweeks = numweeks;
    }

    // other stuff 

    public void setPrice(int day, int month, int year, double[] tariff, int numweeks){
        tariff = apt.getTariffs();
        Booking.day=day;
        Booking.month=month;
        Booking.year=year;

        Calendar date = Calendar.getInstance();
        date.set(year, month-1, day);
        Calendar date1 = Calendar.getInstance();
        date1.set(2008, 6, 1);
        Calendar date2 = Calendar.getInstance();
        date2.set(2008, 6, 31);
        Calendar date3 = Calendar.getInstance();
        date3.set(2008, 7, 1);
        Calendar date4 = Calendar.getInstance();
        date4.set(2008, 7, 31);
        Calendar date5 = Calendar.getInstance();
        date5.set(2008, 11, 20);
        Calendar date6 = Calendar.getInstance();
        date6.set(2009, 0, 1);

        if(date.equals(date1) || date.equals(date2) || (date.after(date1) && date.before(date2))){
            this.price = tariff[1] * numweeks;
        } else if(date.equals(date3) || date.equals(date4) || (date.after(date3) && date.before(date4))){
            this.price = tariff[2] * numweeks;
        } else if(date.equals(date5) || date.equals(date6) || (date.after(date5) && date.before(date6))){   
            this.price = tariff[2] * numweeks;
        } else{
            this.price = tariff[0] * numweeks;
        }

    }    
}

I encounter the problem when setting the price of a Booking object with arrival date on the 20th December 2008 (considered high season): it skips the third if check (expected) and goes directly to the last else . 与20日2008年12月(考虑旺季)抵达日期设置预约对象的价格时,我遇到的问题:它会跳过如果检查(预期)第三,直接进入到最后的

But if I run my own program to check if the dates are the same, passing directly the values to day, month and year, the test is passed. 但是如果我运行自己的程序来检查日期是否相同,直接将值传递给日,月和年,则测试通过。

So it seems to me that I cannot pass only references not pointing to an int value not manually setted. 所以在我看来,我不能仅传递未指向未手动设置的int值的引用。 Is it possible I am right? 我有可能吗? If so, I really don't know how to go on. 如果是这样,我真的不知道该怎么做。

Thanks in advance: I hope I used all the right words in the right places. 在此先感谢:我希望我在正确的地方使用了所有正确的词语。

When you get a calendar instance, it defaults to using the current time (right down to the millisecond). 当您获得日历实例时,它默认使用当前时间(直到毫秒)。 Thus, when set your date in it: 因此,当您在其中设置日期时:

Calendar date = Calendar.getInstance();
date.set(year, month-1, day);

... the date is still left with "random" values for the hours, minutes, seconds and milliseconds. ... date仍然是小时,分钟,秒和毫秒的“随机”值。 The same goes for date1 through to date6 . date1date6

In your code, you create all the dates one right after the other, so the speed of executing those instructions may mean that the first few dates end up with identical values for hours, minutes, seconds and milliseconds. 在您的代码中,您可以一个接一个地创建所有日期,因此执行这些指令的速度可能意味着前几个日期最终具有相同的小时,分​​钟,秒和毫秒值。 However there is no guarantee of this. 但是不能保证这一点。

What you're finding is that when you do, for example, date.equals(date3) , the year month and day match, but the other fields potentially don't. 您所发现的是,例如,当您执行date.equals(date3) ,年月和日匹配时,但其他字段可能不会。

To solve this, call clear() first: 要解决此问题,请先调用clear()

Calendar date = Calendar.getInstance();
date.clear();
date.set(year, month-1, day);

Also, you probably don't actually want to compare calendars for equality. 此外,您可能实际上并不想比较日历的相等性。 You can, but if you look at the Javadoc for it, it says: 你可以,但如果你看一下它的Javadoc,它会说:

 * Compares this <code>Calendar</code> to the specified
 * <code>Object</code>.  The result is <code>true</code> if and only if
 * the argument is a <code>Calendar</code> object of the same calendar
 * system that represents the same time value (millisecond offset from the
 * <a href="#Epoch">Epoch</a>) under the same
 * <code>Calendar</code> parameters as this object.
 *
 * <p>The <code>Calendar</code> parameters are the values represented
 * by the <code>isLenient</code>, <code>getFirstDayOfWeek</code>,
 * <code>getMinimalDaysInFirstWeek</code> and <code>getTimeZone</code>
 * methods. If there is any difference in those parameters
 * between the two <code>Calendar</code>s, this method returns
 * <code>false</code>.
 *
 * <p>Use the {@link #compareTo(Calendar) compareTo} method to
 * compare only the time values.

You're probably better off using: 你可能最好使用:

if (date.getTime().equals(date1.getTime()))
{
  ...
}

... and comparing the returned Date objects, or doing as the Javadoc suggests and using compareTo() : ...并比较返回的Date对象,或者像Javadoc建议的那样并使用compareTo()

if (date.compareTo(date1) == 0)
{
  ...
}

I understand you are doing an exercise, but you should know: 我知道你正在做练习,但你应该知道:

(a) Avoid java.util.Calendar (a)避免使用java.util.Calendar

The java.util.Date and .Calendar classes bundled with Java are notoriously troublesome. 与Java捆绑在一起的java.util.Date和.Calendar类非常麻烦。 Avoid them. 避免他们。 Use either the new java.time package bundled with Java 8, or the Joda-Time library which inspired java.time. 使用与Java 8捆绑在一起的新java.time包 ,或者启发java.time的Joda-Time库。 Both java.time and Joda-Time have some pros and cons over each other, both are active projects, and you can even use them both in a project. java.time和Joda-Time都有一些优点和缺点,两者都是活动项目,你甚至可以在项目中使用它们。

(b) Date-Only (b)仅限日期

The old .Date & .Calendar classes lack a representation of date-only without a time-of-day. 旧的.Date和.Calendar类缺少日期表示,没有时间。 But that is what your Question demands, a class that is date-only without time and time zones. 但这就是你的问题所要求的,这是一个仅限日期而没有时区的课程。 Fortunately both Joda-Time and java.time have such a class, both called LocalDate . 幸运的是,Joda-Time和java.time都有这样一个类,都叫做LocalDate

(c) Half-Open (c)半开放

The best approach to spans of time is called "Half-Open" where the beginning is inclusive and the ending exclusive. 跨越时间跨度的最佳方法称为“半开放”,其中开头是包容性的,结尾是排他性的。 For example the month of June would be June 1 and going up to, but not including, July 1. This simplifies things whether doing date-only or date-time work. 例如,6月份的月份将是6月1日,但不包括7月1日。这简化了日期工作或日期工作。 Joda-Time and java.time adopt this approach. Joda-Time和java.time采用这种方法。

The other answer by Greg Kopff seems to be correct, the time-of-day portion is throwing you off. Greg Kopff另一个答案似乎是正确的,时间部分会让你失望。

Here is some example code in Joda-Time 2.4 to get you headed in the right direction. 以下是Joda-Time 2.4中的一些示例代码,可帮助您朝着正确的方向前进。

LocalDate target = new LocalDate( 2008, 12, 20 );

LocalDate highSummerStart = new LocalDate( 2008, 6, 1 );  // Half-Open. Inclusive.
LocalDate highSummerStop = new LocalDate( 2008, 7, 1 );  // Exclusive.

LocalDate lateSummerStart = new LocalDate( 2008, 7, 1 );  // Half-Open. Inclusive.
LocalDate lateSummerStop = new LocalDate( 2008, 8, 1 );  // Exclusive.

LocalDate holidaysStart = new LocalDate( 2008, 11, 20 );  // Half-Open. Inclusive.
LocalDate holidaysStop = new LocalDate( 2009, 1, 2 );  // Exclusive.

if ( this.rangeContainsTarget( highSummerStart, highSummerStop, target ) ) {
    System.out.println( "Apply High Summer rates." );
} else if ( this.rangeContainsTarget( lateSummerStart, lateSummerStop, target ) ) {
    System.out.println( "Apply Late Summer rates." );
} else if ( this.rangeContainsTarget( holidaysStart, holidaysStop, target ) ) {
    System.out.println( "Apply Holidays rates." );
} else { // Else not in special season.
    System.out.println( "Apply default rates." );
}

And the comparison method. 和比较方法。

private boolean rangeContainsTarget( LocalDate start, LocalDate stop, LocalDate target )
{
    // Half-Open approach. If the Target is GREATER THAN OR EQUAL TO Start AND Target is LESS THAN Stop.
    if ( start.isAfter( stop ) ) {
        return false; // Or throw exception.
    }
    boolean startGood = ( target.isEqual( start ) || target.isAfter( start ) );
    boolean stopGood = target.isBefore( stop );
    boolean containsTarget = ( startGood && stopGood );
    return containsTarget;
}

The old .Date/.Calendar classes lack a way to represent a span of time. 旧的.Date / .Calendar类缺少表示时间跨度的方法。 Joda-Time offers three classes to define a span of time in various ways: Interval, Period, and Duration. Joda-Time提供三个类来以各种方式定义时间跨度:间隔,周期和持续时间。 Unfortunately they work only with DateTime, not LocalDate. 不幸的是,它们仅适用于DateTime,而不适用于LocalDate。 So I did not use them in the example above, where Interval would have been handy. 所以我没有在上面的例子中使用它们,其中Interval会很方便。


By the way, if in Joda-Time you do need a date plus time-of-day yet want to focus on days, call the withTimeAtStartOfDay() method to get a DateTime object set to the first moment of the day. 顺便说一句,如果在Joda-Time中你确实需要一个日期加上时间而又想要关注几天,请调用withTimeAtStartOfDay()方法将DateTime对象设置为当天的第一个时刻。 That first moment is not always the time 00:00:00.000 because of Daylight Saving Time and perhaps other anomalies. 第一个时刻并不总是时间00:00:00.000因为夏令时和其他异常现象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM