Java 8 LocalDate - 如何获取两个日期之间的所有日期?

时间:2016-07-06 09:25:57

标签: java date java-8 java-time

是否可以在新的java.time API中获得两个日期之间的所有日期

假设我有这部分代码:

@Test
public void testGenerateChartCalendarData() {
    LocalDate startDate = LocalDate.now();

    LocalDate endDate = startDate.plusMonths(1);
    endDate = endDate.withDayOfMonth(endDate.lengthOfMonth());
}

现在我需要startDateendDate之间的所有日期。

我正在考虑获取两个日期的daysBetween并迭代:

long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);

for(int i = 0; i <= daysBetween; i++){
    startDate.plusDays(i); //...do the stuff with the new date...
}

有更好的方式来获取日期吗?

9 个答案:

答案 0 :(得分:61)

首先,您可以使用TemporalAdjuster来获取该月的最后一天。接下来,Stream API会提供Stream::iterate,这是解决问题的正确工具。

LocalDate start = LocalDate.now();
LocalDate end = LocalDate.now().plusMonths(1).with(TemporalAdjusters.lastDayOfMonth());
List<LocalDate> dates = Stream.iterate(start, date -> date.plusDays(1))
    .limit(ChronoUnit.DAYS.between(start, end))
    .collect(Collectors.toList());
System.out.println(dates.size());
System.out.println(dates);

答案 1 :(得分:60)

假设您主要想迭代日期范围,那么创建一个可迭代的DateRange类是有意义的。这将允许你写:

for (LocalDate d : DateRange.between(startDate, endDate)) ...

类似的东西:

public class DateRange implements Iterable<LocalDate> {

  private final LocalDate startDate;
  private final LocalDate endDate;

  public DateRange(LocalDate startDate, LocalDate endDate) {
    //check that range is valid (null, start < end)
    this.startDate = startDate;
    this.endDate = endDate;
  }

  @Override
  public Iterator<LocalDate> iterator() {
    return stream().iterator();
  }

  public Stream<LocalDate> stream() {
    return Stream.iterate(startDate, d -> d.plusDays(1))
                 .limit(ChronoUnit.DAYS.between(startDate, endDate) + 1);
  }

  public List<LocalDate> toList() { //could also be built from the stream() method
    List<LocalDate> dates = new ArrayList<> ();
    for (LocalDate d = startDate; !d.isAfter(endDate); d = d.plusDays(1)) {
      dates.add(d);
    }
    return dates;
  }
}

添加equals&amp; amp; hashcode方法,getters,也许有一个静态工厂+私有构造函数来匹配Java时间API等的编码风格。

答案 2 :(得分:20)

Java 9

在Java 9中,library(digest) library(R6) some_class <- R6Class( "some_class", private = list(x = NULL), public = list( initialize = function(x) { private$x <- x } ) ) digest(some_class) #> [1] "8ae94b2fe26264ca68eb39da640da5a1" object <- some_class$new(123) digest(some_class) #> [1] "c4cc5a51c85cb2e4a773cf6e29684f7d" object <- some_class$new(123) digest(some_class) #> [1] "f94260c269920b53db838d2fcf53271f" object <- some_class$new(123) digest(some_class) #> [1] "f94260c269920b53db838d2fcf53271f" object <- some_class$new(123) 类通过LocalDate.datesUntil(LocalDate endExclusive)方法进行了增强,该方法以LocalDate的形式返回日期范围内的所有日期。

Stream<LocalDate>

答案 3 :(得分:9)

您可以使用.isAfter和.plusDays在循环中执行此操作。我不会说更好,因为我没有对该主题进行大量研究,但我可以自信地说它使用Java 8 API并且是轻微替代方案。

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace LastenBoekInfrastructure.Controls.Controls
{
    [DefaultEvent("Click")]
    public class ImageButton : UserControl
    {
        public string ToolTipText
        {
            get { return _bButton.ToolTipText; }
            set { _bButton.ToolTipText = value; }
        }

        public bool CheckOnClick
        {
            get { return _bButton.CheckOnClick; }
            set { _bButton.CheckOnClick = value; }
        }

        public bool DoubleClickEnabled
        {
            get { return _bButton.DoubleClickEnabled; }
            set { _bButton.DoubleClickEnabled = value; }
        }

        public System.Drawing.Image Image
        {
            get { return _bButton.Image; }
            set { _bButton.Image = value; }
        }

        public new event EventHandler Click;
        public new event EventHandler DoubleClick;

        private ToolStrip _tsMain;
        private ToolStripButton _bButton;

        public ImageButton()
        {
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            var resources = new ComponentResourceManager(typeof(ImageButton));
            _tsMain = new ToolStrip();
            _bButton = new ToolStripButton();
            _tsMain.SuspendLayout();
            SuspendLayout();

            // 
            // tsMain
            // 
            _tsMain.BackColor = System.Drawing.Color.Transparent;
            _tsMain.CanOverflow = false;
            _tsMain.Dock = DockStyle.Fill;
            _tsMain.GripMargin = new Padding(0);
            _tsMain.GripStyle = ToolStripGripStyle.Hidden;
            _tsMain.Items.AddRange(new ToolStripItem[] {
            _bButton});
            _tsMain.Location = new System.Drawing.Point(0, 0);
            _tsMain.Name = "_tsMain";
            _tsMain.Size = new System.Drawing.Size(25, 25);
            _tsMain.TabIndex = 0;
            _tsMain.Renderer = new ImageButtonToolStripSystemRenderer();
            // 
            // bButton
            // 
            _bButton.DisplayStyle = ToolStripItemDisplayStyle.Image;
            _bButton.Image = ((System.Drawing.Image)(resources.GetObject("_bButton.Image")));
            _bButton.ImageTransparentColor = System.Drawing.Color.Magenta;
            _bButton.Name = "_bButton";
            _bButton.Size = new System.Drawing.Size(23, 22);
            _bButton.Click += bButton_Click;
            _bButton.DoubleClick += bButton_DoubleClick;
            // 
            // ImageButton
            // 
            Controls.Add(_tsMain);
            Name = "ImageButton";
            Size = new System.Drawing.Size(25, 25);
            _tsMain.ResumeLayout(false);
            _tsMain.PerformLayout();
            ResumeLayout(false);
            PerformLayout();
        }

        void bButton_Click(object sender, EventArgs e)
        {
            if (Click != null)
            {
                Click(this, e);
            }
        }

        void bButton_DoubleClick(object sender, EventArgs e)
        {
            if(DoubleClick != null)
            {
                DoubleClick(this, e);
            }
        }

        public class ImageButtonToolStripSystemRenderer : ToolStripSystemRenderer
        {
            protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e)
            {
                //base.OnRenderToolStripBorder(e);
            }
        }
    }
}

输出

LocalDate startDate = LocalDate.now();
LocalDate endDate = startDate.plusMonths(1);
while (!startDate.isAfter(endDate)) {
 System.out.println(startDate);
 startDate = startDate.plusDays(1);
}

Example Here

答案 4 :(得分:6)

您可以创建streamLocalDate个对象。我也遇到了这个问题,并将我的解决方案发布为java-timestream on github.

使用你的例子......

LocalDateStream
    .from(LocalDate.now())
    .to(1, ChronoUnit.MONTHS)
    .stream()
    .collect(Collectors.toList());

它或多或少等同于此处提出的其他解决方案,但它会处理所有日期数学并知道何时停止。您可以提供特定或相对结束日期,并告诉它跳过每次迭代的时间(上面的默认值是一天)。

答案 5 :(得分:2)

ThreeTen-Extra库具有一个LocalDateRange类,可以完全满足您的要求:

LocalDateRange.ofClosed(startDate, endDate).stream()
        .forEach(/* ...do the stuff with the new date... */);

答案 6 :(得分:1)

在我的时间库Time4J中,我编写了一个优化的分割器来构建具有良好并行化特征的日历日期流。适应您的用例:

LocalDate start = ...;
LocalDate end = ...;

Stream<LocalDate> stream = 
  DateInterval.between(start, end) // closed interval, else use .withOpenEnd()
    .streamDaily()
    .map(PlainDate::toTemporalAccessor);

如果您对相关功能感兴趣,例如每个日历日期的时钟间隔(分区流)或其他间隔功能,并希望避免手写代码笨拙,这个简短的方法可能是一个有趣的起点,另请参阅{的API {3}}

答案 7 :(得分:1)

tl; dr

使用Java 9及更高版本中Answer by Singh的流扩展优质datesUntil

today                                 // Determine your beginning `LocalDate` object.
.datesUntil(                          // Generate stream of `LocalDate` objects.
    today.plusMonths( 1 )             // Calculate your ending date, and ask for a stream of dates till then.
)                                     // Returns the stream.
.collect( Collectors.toList() )       // Collect your resulting dates in a `List`. 
.toString()                           // Generate text representing your found dates.
  

[2018-09-20、2018-09-21、2018-09-22、2018-09-23、2018-09-24、2018-09-25、2018-09-26、2018-09- 27,2018-09-28,2018-09-29,2018-09-30,2018-10-01,2018-10-02,2018-10-03,2018-10-04,2018-10-05, 2018-10-06、2018-10-07、2018-10-08、2018-10-09、2018-10-10、2018-10-11、2018-10-12、2018-10-13、2018- 10-14,2018-10-15,2018-10-16,2018-10-17,2018-10-18,2018-10-19]

LocalDate::datesUntil

从Java 9开始,您可以要求提供日期流。致电LocalDate::datesUntil

从确定今天的日期开始。这需要一个时区。在任何给定时刻,日期都会在全球范围内变化。

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
LocalDate today = LocalDate.now( z ) ;

确定结束日期。

LocalDate stop = today.plusMonths( 1 ) ;

询问从开始到结束的日期流。

Stream< LocalDate > stream = today.datesUntil( today.plusMonths( 1 ) );

从该流中提取日期,并将其收集到List中。

List< LocalDate > datesForMonthFromToday = stream.collect( Collectors.toList() );

打印日期列表,以标准ISO 8601格式生成文本。

System.out.println( datesForMonthFromToday );

关于 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

答案 8 :(得分:0)

您可以使用Google Range库中的Guava功能。在LocalDate个实例中定义了DiscreteDomain个实例后,您可以获得一个ContiguousSet范围内的所有日期。

LocalDate d1 = LocalDate.parse("2017-12-25");
LocalDate d2 = LocalDate.parse("2018-01-05");

DiscreteDomain<LocalDate> localDateDomain = new DiscreteDomain<LocalDate>() {
    public LocalDate next(LocalDate value) { return value.plusDays(1); }
    public LocalDate previous(LocalDate value) { return value.minusDays(1); }
    public long distance(LocalDate start, LocalDate end) { return start.until(end, ChronoUnit.DAYS); }
    public LocalDate minValue() { return LocalDate.MIN; }
    public LocalDate maxValue() { return LocalDate.MAX; }
};

Set<LocalDate> datesInRange = ContiguousSet.create(Range.closed(d1, d2), localDateDomain);