【问题标题】:Manipulating and comparing dates with GregorianCalendar and can't get the code to work properly使用 GregorianCalendar 操作和比较日期,无法使代码正常工作
【发布时间】:2020-11-11 20:28:52
【问题描述】:

我很难在此处的代码中找到错误。当它应该有一个数字时,paytotal 出现 0。

firstDayOfPaycheck 是 2020 年 10 月 23 日的日期。

lastDayOfPaycheck 是 2020 年 11 月 6 日的日期。

我的简单日期格式sdf 是“MMM dd, yyyy”。

传递给 runPayroll 的字符串 dateInQuestion 是“2020 年 10 月 31 日”,它最初来自与上述相同的 sdf。

我是 java 新手,还没有处理过像这样操作日历的问题。感觉下面的代码应该可以了。

 private void runPayroll(String dateInQuestion, long payForTask){   
     c.setTime(firstDayOfPaycheck);
        
        //loop through days of paycheck. number from time.compareTo(time2) is negative while time is before time2 
        while(c.getTime().compareTo(lastDayOfPaycheck)<=0){

            if(dateInQuestion != null) {

                Date questionDate = sdf.parse(dateInQuestion, new ParsePosition(0));
                if (c.getTime().compareTo(questionDate) == 0) {
                    payTotal += payForTask;
                }
            }
            c.add(Calendar.DAY_OF_YEAR, 1);
        }
} //ran inside the query to get the total pay

private void buildPayrollDash(){
    String strPayrollAmt = "$" + payTotal;


    String startDate = sdf.format(firstDayOfPaycheck);
    String trimmedStart = startDate.split(",")[0]; //cuts out the year in the date
    String endDate = sdf.format(lastDayOfPaycheck);
    String trimmedEnd = endDate.split(",")[0];
    int holdBack = sharedPreferences.getInt("payroll holdback", 7);
    c.setTime(lastDayOfPaycheck);
    c.add(Calendar.DAY_OF_YEAR, holdBack);
    String payDate = sdf.format(c.getTime());
    String trimmedPaydate = payDate.split(",")[0];

    tvPayrollTimefame.setText("Pay from " + trimmedStart + " - " + trimmedEnd);
    tvPayrollAmount.setText(strPayrollAmt + " due " + trimmedPaydate);

【问题讨论】:

  • 你好,伙计。不再推荐使用 Calendar 类,因为它包含一些设计缺陷。推荐的工具现在位于 java.time 包中,例如 LocalDateTime。如果您要在两个日期之间进行测量,请查看 Period 类。
  • 您可以找到关于 Period here 使用的 Java 教程。这应该可用于更简洁地计算您的代码当前尝试查找的天数。
  • 谢谢蒂姆。我会切换到那个
  • 既然你要切换到更好的 API,现在可能没关系,但你应该有created a Minimal, Reproducible Example。就你的问题而言,我不知道出了什么问题,坦率地说,我怀疑任何人都可以。所以请回答你的下一个问题。

标签: java android java-time


【解决方案1】:

我很难在这里找到我的代码中的错误。

您正在使用糟糕的日期时间类,这些类在几年前已被现代 java.time 类所取代。切勿使用Date、Calendar、GregorianCalendar 或其亲属。

firstDayOfPaycheck 是 2020 年 10 月 23 日的日期。

使用LocalDate 表示没有时间和时区的日期。

LocalDate firstDayOfPayPeriod = LocalDate.of( 2020 , Month.OCTOBER , 23 ) ;

lastDayOfPaycheck 是 2020 年 11 月 6 日的日期。

如果您使用 Half-Open 方法定义时间跨度,您会发现日期时间处理要容易得多。开头是inclusive,而结尾是exclusive。因此,与其关注支付期的最后一天,不如关注下一个期间的第一天。

LocalDate firstDayOfSuccessivePayPeriod = LocalDate.of( 2020 , 11 , 7 ) ;

提示:如果您将ThreeTen-Extra 库添加到您的Java 项目中,您可以将支付期的日期范围表示为LocalDateRange 对象。

我的简单日期格式sdf 是“MMM dd, yyyy”。

您不应该将业务逻辑与本地化代码混为一谈。日期时间的自定义格式只能用于向用户展示。

当以文本形式交换日期时间值作为数据时,请使用标准ISO 8601 格式。对于仅日期值,标准格式为 YYYY-MM-DD。 java.time 默认使用 ISO 8601 格式,因此无需指定任何格式模式。

传递给runPayroll 的字符串dateInQuestion 是“2020 年10 月31 日”,它最初来自与上述相同的sdf。

LocalDate dateInQuestion = LocalDate.parse( "2020-10-31" ) ;

如果您必须提供格式化日期字符串而不是标准 ISO 8601 格式的输入,请使用 DateTimeFormatter。这已经在 Stack Overflow 上多次介绍过,所以请搜索更多信息。

与其稍后检查有效数据,不如尽早在代码中检查您的输入。俗话说“快速失败”。

try
{
    LocalDate dateInQuestion = LocalDate.parse( "2020-10-31" );
}
catch ( DateTimeParseException e )
{
    // … Handle faulty input.
    e.printStackTrace();
}

我是 java 新手,还没有处理过像这样操作日历的问题。感觉下面的代码应该可以了。

使用java.time,您的代码会简单得多。一方面,java.time 类提供方便的isBefore、isAfter 和isEqual 方法,因此无需笨拙的compareTo 调用。

LocalDate firstDayOfPayPeriod = LocalDate.of( 2020 , Month.OCTOBER , 23 );
LocalDate firstDayOfSuccessivePayPeriod = LocalDate.of( 2020 , 11 , 7 );

String input = "2020-10-31";
LocalDate dateInQuestion = null;
try
{
    dateInQuestion = LocalDate.parse( input );
}
catch ( DateTimeParseException e )
{
    // Handle faulty input.
    e.printStackTrace();
}

// Validate dates.
Objects.requireNonNull( firstDayOfPayPeriod );
Objects.requireNonNull( firstDayOfSuccessivePayPeriod );
Objects.requireNonNull( dateInQuestion );
if ( ! firstDayOfPayPeriod.isBefore( firstDayOfSuccessivePayPeriod ) )
{
    throw new IllegalStateException( "…" );
}
if ( dateInQuestion.isBefore( firstDayOfPayPeriod ) )
{
    throw new IllegalStateException( "…" );
}
if ( ! dateInQuestion.isBefore( firstDayOfSuccessivePayPeriod ) )
{
    throw new IllegalStateException( "…" );
}


long payPerDay = 100;
long partialPay = 0;

LocalDate localDate = firstDayOfPayPeriod;
while ( localDate.isBefore( firstDayOfSuccessivePayPeriod ) )
{
    if ( localDate.isBefore( dateInQuestion ) )
    {
        partialPay = ( partialPay + payPerDay );
    }

    // Set up the next loop.
    // Notice that java.time uses immutable objects. So we generate a new object based on another’s values rather than alter (mutate) the original.
    localDate = localDate.plusDays( 1 ); // Increment to next date.
}

System.out.println( "Partial pay earned from firstDayOfPayPeriod " + firstDayOfPayPeriod + " to dateInQuestion " + dateInQuestion + " is " + partialPay );

看到这个code run live on IdeOne.com。

从 firstDayOfPayPeriod 2020-10-23 到 dateInQuestion 2020-10-31 赚取的部分工资为 800

如果您拥有更多 Java 编程经验,您可能希望使用流来完成此类工作。见LocalDate::datesUntil。

顺便说一句,如果您想跳过周末,请添加以下内容:

Set< DayOfWeek > weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY ) ;
…
if ( weekend.contains( localDate.getDayOfWeek() ) ) { … }

关于java.time

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

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

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

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

从哪里获取 java.time 类?

【讨论】:

  • 切换到这个 API 解决了我的问题。谢谢罗勒
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-24
  • 1970-01-01
  • 1970-01-01
  • 2019-07-10
  • 1970-01-01
相关资源
最近更新 更多