0

我一直在使用以下代码来计算两个日期之间的差异,但我遇到了一个不寻常的错误:

如果我通过以下日期:

d1 = 2017 年 8 月 12 日 d2 = 2022 年 7 月 31 日

它返回:24055 ..

结果不应该作为月数返回吗?

 public static int CalcDateDiff( java.util.Date  date1,  java.util.Date  date2) {
    if(date1 == null || date2 ==null )
    {
        if(date2 == null)
        {
              Calendar d1 = Calendar.getInstance();
                d1.setTime(date1);
                final Calendar d2 = Calendar.getInstance();
                int diff = (d2.get(Calendar.YEAR) - d1.get(Calendar.YEAR)) * 12 + d2.get(Calendar.MONTH) - d1.get(Calendar.MONTH);

                return diff;
        }
        else
            return -1;
    }
    else
    {

            Calendar d1 = Calendar.getInstance();
            d1.setTime(date1);
            final Calendar d2 = Calendar.getInstance();
            d2.setTime(date2);
            int diff = (d2.get(Calendar.YEAR) - d1.get(Calendar.YEAR)) * 12 + d2.get(Calendar.MONTH) - d1.get(Calendar.MONTH);

            return diff;
        }
}

提前致谢!

4

2 回答 2

1

最佳解决方案:

如果您已经有java.sql.Date实例,那么

final Date d1 = new java.sql.Date(2017 - 1900, 12, 8);
final Date d2 = new java.sql.Date(2022 - 1900, 07, 31);

我知道我在 中使用了一个不推荐使用的构造函数java.sql.Date,这只是为了方便以最少的代码行获得我需要的东西。 永远不要在生产代码中使用这个构造函数!

获得所需内容的最简单最直接的自我记录方法:

final long monthsBetween ChronoUnit.MONTHS.between(d1.toLocalDate(),d2.toLocalDate()) + 1;

因为使用这种方法,您不必处理TimeZone信息,因为对于以LocalDate这种方式创建的实例,所有信息都可以保证是正确的。

出于某种原因,只有java.sql.Date哪个.toLocalDate()对你有好处,因为那是你从数据库中得到的。

public LocalDate toLocalDate() 将此 Date 对象转换为 LocalDate 该转换创建一个 LocalDate ,它表示与本地时区中此 Date 相同的日期值

返回:表示相同日期值的 LocalDate 对象自:1.8


对代码更正的评论:

正确的公式是:

(y2 - y1) * 12 + (m2 - m1) + 1

此外,您的代码过于复杂并且使用了非常旧的类。

Java 8 解决方案和您的 Java 7 兼容的更正解决方案:

public class Q47717075
{
    /*
    https://stackoverflow.com/questions/1086396/java-date-month-difference
     */
    public static void main(@Nonnull final String[] args)
    {
        final Date d1 = new java.sql.Date(2017 - 1900, 12, 8);
        final Date d2 = new java.sql.Date(2022 - 1900, 07, 31);
        System.out.println(CalcDateDiff(d1, d2));
        /* Obtains an instance of Instant from a text string such as 2007-12-03T10:15:30.00Z. */
        System.out.println(intervalInMonths(LocalDate.of(2017,12,8), LocalDate.of(2022,7,31)));
        System.out.println(ChronoUnit.MONTHS.between(d1.toLocalDate(),d2.toLocalDate()) + 1);
    }


    /*
       Alternate Java 8 version
     */
    public static int intervalInMonths(@Nonnull final LocalDate i1, @Nonnull final LocalDate i2)
    {
        final Period p = Period.between(i1, i2);
        return (p.getYears() * 12) + p.getMonths() + 1;
    }

    /**
     * Your versin corrected
     */
    public static int CalcDateDiff(@Nonnull final Date date1, @Nonnull final Date date2)
    {
        final Calendar d1 = Calendar.getInstance();
        d1.setTime(date1);
        final Calendar d2 = Calendar.getInstance();
        d2.setTime(date2);

        return ((d2.get(YEAR) - d1.get(YEAR)) * 12 + (d2.get(MONTH) - d1.get(MONTH)) + 1);
    }
}

正确的输出是:

56
56
56
于 2017-12-08T16:29:59.757 回答
0

tl;博士

ChronoUnit.MONTHS.between(                                           // Use enum method to calculate elapsed time.
    date1.toInstant().atZone( ZoneId.of( "Africa/Casablanca " ) ) ,  // Convert from legacy `Date` class to modern `java.time.Instant` class. Adjust from UTC to a specific time zone. 
    date2.toInstant().atZone( ZoneId.of( "Africa/Casablanca " ) ) 
)                                                                    // Return a number of months elapsed. 

细节

罗伯逊的回答很好。这是一个替代方案。

您对麻烦Date类的使用现在已经过时了。该遗留类Instant在 java.time 包中被替换。两者都代表一个时刻,即UTC时间轴上的一个点。现代类的分辨率为纳秒而不是毫秒

使用添加到旧类的新方法进行转换。

Instant instant = myUtilDate.toInstant() ;

要确定月份数,我们需要日期。确定日期需要时区。对于任何给定的时刻,日期在全球范围内因区域而异。

ZoneId z = ZoneId.of( "Pacific/Auckland " ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

ChronoUnit枚举提供了between计算经过时间的方法。

long months = ChronoUnit.MONTHS.between( zdt1 , zdt2 ) ;
于 2017-12-08T16:43:34.113 回答