从今年获得同一天

时间:2019-02-05 23:20:23

标签: java date

我需要在今年的同一天。

示例:现在是2019年,并且变量包含2022年7月15日的值,因此我需要获取2019年7月15日。它适用于所有日期,除了2月(一年中有一天额外的一天,而今年没有这一天),例如:2020年2月29日将在第二天返回我:2019年3月1日,但在这种情况下,我需要返回前一天:2019年2月28日。如何调整逻辑以使其以这种方式工作?

public static java.util.Date getThisDateInThisYear(java.util.Date date) {
    GregorianCalendar gc = new GregorianCalendar();
    gc.setTime(date);
    Date today = new Date();
    GregorianCalendar gcToday = new GregorianCalendar();
    gcToday.setTime(today);
    gc.set(GregorianCalendar.YEAR, gcToday.get(GregorianCalendar.YEAR));
    return gc.getTime();
  }

4 个答案:

答案 0 :(得分:2)

首先计算年份差,并将结果添加到日期

public Date getThisDateInThisYear(Date date) {
   Calendar c = Calendar.getInstance();
   int thisYear = c.get(Calendar.YEAR)
   c.setTime(date);

   int diff = thisYear  - c.get(Calendar.YEAR);
   c.add(Calendar.YEAR, diff);

   return c.getTime();

}

我使用2016-02-29和2020-02-29进行了测试,都返回2019-02-28。

答案 1 :(得分:1)

tl; dr

使用现代的 java.time 类。切勿使用Date / Calendar

  

2020年2月29日将在第二天返回我:2019年3月1日

LocalDate                           // Represent a date-only value, without time-of-day and without time zone.
.of( 2020 , Month.FEBRUARY , 29 )   // Specify a date. Here, Leap Day of 2020. Returns a `LocalDate` object.
.minusYears( 1 )                    // Intelligently move backwards in time one year. Returns another `LocalDate` object, per immutable objects pattern.
.toString()                         // Generate text representing the value of this date, in standard ISO 8601 format of YYYY-MM-DD.

run at IdeOne.com时:

  

2019-02-28

避免使用旧的日期时间类

您使用的是可怕的日期时间类,这些类现在已经被遗留了,完全被 java.time 类所取代。

LocalDate

LocalDate类表示没有日期,没有time zoneoffset-from-UTC的仅日期值。

时区对于确定日期至关重要。在任何给定时刻,日期都会在全球范围内变化。例如,Paris France午夜之后的几分钟是新的一天,而Montréal Québec仍然是“昨天”。

如果未指定时区,则JVM隐式应用其当前的默认时区。该默认值可能在运行时(!)期间change at any moment,因此您的结果可能会有所不同。最好将您的期望/期望时区明确指定为参数。

Continent/Region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用2-4个字母的缩写,例如ESTIST,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Montreal" ) ;  
LocalDate today = LocalDate.now( z ) ;

如果要使用JVM的当前默认时区,请提出要求并作为参数传递。如果省略,代码将变得难以理解,因为我们不确定您是否打算使用默认值,还是像许多程序员一样不知道该问题。

ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.

或指定日期。您可以用数字设置月份,一月至十二月的理智编号为1-12。

LocalDate ld = LocalDate.of( 2020 , 2 , 29 ) ;  // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.

或者更好的是,使用预定义的Month枚举对象,每年的每个月使用一个。提示:在整个代码库中使用这些Month对象,而不是仅使用整数,可以使您的代码更具自记录性,确保有效值并提供type-safetyYearYearMonth的同上。

LocalDate ld = LocalDate.of( 2020 , Month.FEBRUARY , 29 ) ;

日期数学

您可以使用* java.time以几种方式进行日期时间数学运算。一种方法是调用plusminus并传递PeriodDuration。另一种方法是调用便捷方法,例如plusYearsminusYears

LocalDate类似乎只给出了您想要的使用这些方法的行为。

要明确:

  • 2020年是Leap Year。因此2020-02-29是一个有效日期。
  • 2018年,2019年和2021年不是。这29日不是这些年来的有效日期。

请参见下面的示例run live at IdeOne.com

从leap年起

让我们从29日的Day日开始,然后添加一年并减去一年。我们希望两者都能看到28号。

LocalDate leapDay2020 = LocalDate.of( 2020 , Month.FEBRUARY , 29 );
LocalDate yearBeforeLeap = leapDay2020.minusYears( 1 );
LocalDate yearAfterLeap = leapDay2020.plusYears( 1 );

System.out.println( "leapDay2020.toString(): " + leapDay2020 );
System.out.println( "yearBeforeLeap.toString(): " + yearBeforeLeap );
System.out.println( "yearAfterLeap.toString(): " + yearAfterLeap );

实际上就是我们得到的。

  

leapDay2020.toString():2020-02-29

     

yearBeforeLeap.toString():2019-02-28

     

yearAfterLeap.toString():2021-02-28

从非-年开始

现在让我们从2月28日的非-年开始,然后加减一年。我们希望在这三者中都排在28位。 2020年Le年的第29个被忽略。

LocalDate nonLeap2019 = LocalDate.of( 2019 , Month.FEBRUARY , 28 );
LocalDate yearBeforeNonLeapIntoNonLeap = nonLeap2019.minusYears( 1 );
LocalDate yearBeforeNonLeapIntoLeap = nonLeap2019.plusYears( 1 );

System.out.println( "nonLeap2019.toString(): " + nonLeap2019 );
System.out.println( "yearBeforeNonLeapIntoNonLeap.toString(): " + yearBeforeNonLeapIntoNonLeap );
System.out.println( "yearBeforeNonLeapIntoLeap.toString(): " + yearBeforeNonLeapIntoLeap );
  

nonLeap2019.toString():2019-02-28

     

yearBeforeNonLeapIntoNonLeap.toString():2018-02-28

     

yearBeforeNonLeapIntoLeap.toString():2020-02-28


关于 java.time

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

目前位于Joda-Timemaintenance 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中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

答案 2 :(得分:0)

您还可以根据年份减去日期来检查年份是否为leap年

public static boolean isLeapYear(int year) {
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.YEAR, year);
    return cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365;
}

答案 3 :(得分:0)

我看不到java.util.Calendar有任何问题。在JDK 1.8中,该Java类得到了重大改进。因此,以下示例代码可以正常工作:

    SimpleDateFormat sf = new SimpleDateFormat("dd MMM yyyy");
    Date date=sf.parse("29 Feb 2020");
    System.out.println("date="+date);

    Calendar calendar=Calendar.getInstance();
    calendar.setTime(date);
    calendar.add(Calendar.YEAR, -1);
    System.out.println("date="+calendar.getTime());

    calendar.add(Calendar.YEAR, 1);
    System.out.println("date="+calendar.getTime());

它给出的结果与Basil Bourque希望得到的结果相同:

date=Sat Feb 29 00:00:00 CET 2020
date=Thu Feb 28 00:00:00 CET 2019
date=Fri Feb 28 00:00:00 CET 2020

基本上,Java类现在使用与新类相同的ZoneInfo等。