【问题标题】:How to get only the dates where the last day of the month comes on Monday between two given dates如何仅在两个给定日期之间获取该月最后一天在星期一的日期
【发布时间】:2018-10-08 02:25:17
【问题描述】:

返回值应该是类似“10/31/2016 8/31/2017”的字符串。此函数返回一年中的所有星期一,但如果它们是每月的最后一天,我只需要每个月最后一个星期一的日期。

function getMondays(startYear, endYear) {
  const startDate = new Date(startYear, 0, 2)
  const endDate = new Date(endYear, 11, 32)

  var i = startDate,
  month = i.getYear(),
  mondaysOfYear = []

  while (i.getDay() !== 2) {
  i.setDate(i.getDate() + 1)
  }

  while (i.getYear() === month) {
    mondaysOfYear.push(new Date(i.getTime()))
    i.setDate(i.getDate() + 7)
  }
  return mondaysOfYear
}
getMondays(2017, 2017)

【问题讨论】:

  • 迭代范围内的几个月,将日期设置为每个月的第一天,返回一天date.setDate( date.getDate() - 1); 并检查是否date.getDay() === 2
  • 如果 startYear 是 2017,你如何得到 10/31/2016
  • 为什么是endDate = new Date(endYear, 11, 32)?那将是次年的 1 月 1 日。
  • 仅供参考,非常麻烦的旧日期时间类,例如 java.util.Datejava.util.Calendarjava.text.SimpleDateFormat,现在是 legacy,被 Java 8 中内置的 java.time 类所取代,之后。见Tutorial by Oracle
  • 您的示例日期“10/31/2016 8/31/2017”不正确。 2016 年和 2017 年月末是星期一的情况是:2016 年 2 月 29 日、2016 年 10 月 31 日、2017 年 7 月 31 日。查看在线日历for 2016for 2017

标签: javascript date


【解决方案1】:

您的逻辑似乎不合适。有:

month = i.getYear(),

getYear 返回一个 2 位数的年份,将其分配给一个名为“月”的变量似乎并不明智。那么:

while (i.getDay() !== 2) {
  i.setDate(i.getDate() + 1)
}

将增加日期直到 getDay 返回 2,即星期二。那么:

if (i.getMonth() != prevMonth) mondaysOfYear.push(prevDate);

对我来说没有任何意义。我猜你的算法是将日期设置为星期一,然后添加几周,如果它在月底结束,保存它。但是你没有那样实现它。

一种更简单且可能更快的算法是获取每个月的最后一天从开始到结束,如果是星期一,则将其推送到数组中。有很多方法可以做到这一点,这里有一个。

function getMondays(startYear, endYear) {
  // Set start to 1 Jan of start year
  var start = new Date(startYear, 0);
  // Set end to 31 Dec of end year
  var end = new Date(endYear, 11,31);
  var mondays = [];
  
  do {
    // setDate to end of month 
    start.setMonth(start.getMonth() + 1, 0);

    // If it's a Monday, store it in US mm/dd/yyyy format
    if (start.getDay() == 1) {
      mondays.push(start.toLocaleString('en-us', {
        year:  'numeric',
        month: '2-digit',
        day:   '2-digit'
      }));
    }

    // Set date to start of next month
    start.setDate(start.getDate() + 1);
  } while (start <= end) 
 
  return mondays;
}

console.log( getMondays(2016,2017) );

【讨论】:

【解决方案2】:

function getMondays(startYear, endYear) {
  mondaysOfYear = []
  for (var y = startYear; y <= endYear; y++)
    for (var m = 1; m < 13; m++) {
      var d = new Date(y, m, 0);   // console.log(d.toDateString()) // "Mon Jul 31 2017"
      if (d.getDay() === 1) 
        mondaysOfYear.push(d.toLocaleDateString())
    }
  return mondaysOfYear
}

console.log(getMondays(2016, 2017).join(" "))        // "2/29/2016 10/31/2016 7/31/2017"

【讨论】:

    【解决方案3】:

    这将在最后一个星期一为您服务 -

    function getMondays(startYear, endYear) {
      const startDate = new Date(startYear, 0, 2)
      const endDate = new Date(endYear, 11, 32)
    
      var i = startDate,
      month = i.getYear(),
      mondaysOfYear = []
    
      while (i.getDay() !== 2) {
      i.setDate(i.getDate() + 1)
      }
      var prevMonth = 0;
      var prevDate;
      while (i.getYear() === month) {
        if(i.getMonth()!= prevMonth) mondaysOfYear.push(prevDate);
        prevMonth = i.getMonth(); //Retaining the last month to compare
        prevDate = new Date(i.getTime()); //Retaining the last monday
        i.setDate(i.getDate() + 7); 
      } 
      mondaysOfYear.push(prevDate);//for December
      return mondaysOfYear
    }
    getMondays(2017, 2017);
    

    过滤掉月份变化的日期。最后在循环之后添加 12 月的最后一个星期一。

    【讨论】:

      【解决方案4】:

      这样的?

      -- 编辑

      这是jsfiddle的链接,以防它在stackoverflow上给出不正确的结果:)

      function getMondays(startYear, endYear) {
        var months = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        var mondays = [];
        while(startYear <= endYear) {
          for(var i = 0; i < 12; i++) {
            var leapYear = startYear % 4 === 0 && i === 1;
            var currentDate = new Date(startYear, i, leapYear ? months[i]+1 : months[i]);
            if(currentDate.getDay() === 1)
              mondays.push(currentDate);
          }
          startYear += 1;
        }
        return mondays;
      }
      
      var mondays = getMondays(2016, 2017)
      console.log(mondays)

      【讨论】:

        【解决方案5】:

        您的问题不清楚,但显然您想查找该月最后一天恰好是星期一的所有事件。

        java.time

        现代方法使用 java.time 类。具体来说,LocalDate 类表示没有时间和时区的仅日期值。还有 YearYearMonthDayOfWeek 类,顾名思义。

        您可以传递 Year 对象以确保类型安全,而不是仅传递整数,从而使您的代码更具自我记录性。

        Year startYear = Year.of( 2016 );
        Year stopYear = Year.of( 2018 );
        

        定义你的实用方法。

        public List< LocalDate > calcMondays ( Year startYear , Year stopYear ) { … }
        

        传递那对对象。

        List< LocalDate> dates = this.calcMondays( startYear , stopYear ) ;
        

        我建议始终使用半开放方法来定义时间跨度。在这种方法中,开头是inclusive,而结尾是exclusive。因此,2017-2019 年期间将从 2017 年的第一天开始,一直持续到但不包括 2019 年的第一天,为期 2 年。

        获取开始和结束的YearMonth,代表整个月份。

        YearMonth startYm = YearMonth.from( startYear.atDay( 1 ) ) ;
        YearMonth stopYm = YearMonth.from( stopYear.atDay( 1 ) ) ;
        

        定义我们找到的日期的集合。

        int initialCapacity = ( int ) ChronoUnit.MONTHS.between( startYear.atDay( 1 ) , stopYear.atDay( 1 ) );
        List< LocalDate > lastMondays = new ArrayList<>( initialCapacity ) ;
        

        定义我们定位的星期几。使用DayOfWeek枚举。

        DayOfWeek dow = DayOfWeek.MONDAY ; 
        

        使用TemporalAdjusters 类中的实现定义TemporalAdjuster

        TemporalAdjuster ta = TemporalAdjusters.lastInMonth( dow ) ;
        

        循环月份。对于每个,确定第一个星期一。使用 TemporalAdjusters 类中的 TemporalAdjuster 实现。

        YearMonth ym = startYm ;
        while( ym.isBefore( stopYm ) ) {
            LocalDate firstOfMonth = ym.atDay( 1 ) ;
            LocalDate ld = firstOfMonth.with( ta ) ;
            lastMondays.add( ld ) ;  // Collect our last-monday-of-this-month. 
            // Set up the next loop.
            ym = ym.plusMonths( 1 ) ;
        }
        

        报告。

        System.out.println( lastMondays ) ;
        

        看到code run live at IdeOne.com

        [2016-01-25, 2016-02-29, 2016-03-28, 2016-04-25, 2016-05-30, 2016-06-27, 2016-07-25, 2016-08- 29、2016-09-26、2016-10-31、2016-11-28、2016-12-26、2017-01-30、2017-02-27、2017-03-27、2017-04-24、 2017-05-29、2017-06-26、2017-07-31、2017-08-28、2017-09-25、2017-10-30、2017-11-27、2017-12-25]

        现在让我们修改该循环以仅关注每月最后一天恰好是星期一的情况。

        YearMonth ym = startYm;
        while ( ym.isBefore( stopYm ) ) {
            LocalDate endOfMonth = ym.atEndOfMonth();
            if ( endOfMonth.getDayOfWeek().equals( dow ) ) {
                lastMondays.add( endOfMonth );  // Collect our last-monday-of-this-month.
            }
            // Set up the next loop.
            ym = ym.plusMonths( 1 );
        }
        

        看到这个code run live at IdeOne.com

        [2016-02-29, 2016-10-31, 2017-07-31]

        请注意,这些结果与问题中给出的结果不同。我验证了这里的这些是正确的,并且问题的结果是不正确的。


        关于java.time

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

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

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

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

        从哪里获得 java.time 类?

        ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-12-29
          • 2013-03-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多