顺序 - 随后几年的同一日期

Sequence - the same date in following years

我想创建一个序列,结果是 returns 所有年份的星期几与函数参数中的相同 (例如:自开始日期起,2 月 12 日为星期日的所有年份)。

let myDate (dw:System.DayOfWeek) (start:System.DateTime) =
    seq {
            ...
        }

希望你明白我的意思。

这个怎么样:

let myDate (dw:System.DayOfWeek) (start:System.DateTime) =
    Seq.initInfinite ((+)1) // from 1..∞
    |> Seq.map start.AddYears
    |> Seq.takeWhile (fun x -> x < DateTime.MaxValue)
    |> Seq.filter (fun x -> x.DayOfWeek = dw)
    |> Seq.map (fun x -> x.Year)

myDate System.DayOfWeek.Monday (new DateTime(2001,1,1)) |> Dump

let myDate (dw:System.DayOfWeek) (start:System.DateTime) =
    let rec inner i =
        seq {
            let someDay = start.AddYears(i)
            if someDay.DayOfWeek = dw then yield someDay.Year
            yield! inner (i+1)
        }
    inner 1

myDate System.DayOfWeek.Monday (new DateTime(2001,12,1)) |> Dump

Stuart 的回答很好,但我忍不住想 "eh, I'd delete some of that code." 你知道的,从那个特定 month/day 的未终止年份序列开始,让来电者决定要拿多少或添加自己的过滤器。

此时你应该在思考 "fool's errand"。 :) 我 运行 遇到的第一个问题——因为我的第二个微弱测试失败了——是你不能愉快地继续递增 DateTime 永远,所以你必须编写代码来限制它。

因此,由于要处理变幻莫测的真实数据,我最终得到的代码更多而不是更少:

// All instances of this specified month/day beginning with 'start'.
let myDate (dw:System.DayOfWeek) (start:System.DateTime) =
    // Start with a sequence of one date for each year...
    seq {
        // Don't run the date past DateTime.MaxValue, there lurks an
        // ArgumentOutOfRangeException. Comparison also must be on the
        // valid side of DateTime.MaxValue. Don't bother with try/with
        // here in a sequence expression, you can't do that.
        let maxDateBarrier = DateTime.MaxValue.AddYears(-1)
        let mutable keepGoing, date = true, start

        while keepGoing do
            yield date

            // if date.Year % 100 = 0 then printfn "%A" date.Year

            if date <= maxDateBarrier then
                date <- date.AddYears(1)
            else
                keepGoing <- false
    }
    |> Seq.where (fun date -> date.DayOfWeek = dw)

有点啰嗦。测试 #1 工作正常:

let printDates (dates : DateTime seq) =
    for date in dates do
        printfn "%A" date.Year

// Take the next 5
myDate DayOfWeek.Sunday DateTime.Now
    |> Seq.take 5
    |> printDates

测试 #2 实际上遍历了我序列中的 所有 个日期:

// Take up to 5 before 2040.
// Note: this statement actually iterates through *all* the years in the
// sequence if you truncate to a length longer than Seq.where returns.
myDate DayOfWeek.Sunday (DateTime(2017, 2, 13))
    |> Seq.where (fun date -> date.Year < 2040)
    |> Seq.truncate 5
    |> printDates

测试 #2 是向 myDate 函数添加 endDate 的原因吗?不,更多的是更好地使用序列的争论。使用 takeWhile 而不是 where:

// Try again: Take up to 5 before 2040.
// Terminate the sequence early with takeWhile.
myDate DayOfWeek.Sunday (DateTime(2017, 2, 13))
    |> Seq.takeWhile (fun date -> date.Year < 2040)
    |> Seq.truncate 5
    |> printDates

好多了。错过了 "less code," 的目标,但我可以接受。 :)

我懒得编写代码,但我会通过这种方式找到一种有效且快速的算法,并将闰年考虑在内。我认为这是一个有趣的任务,将速度与其他答案进行比较会很有趣。

有 14 种可能的日历。如果您 google 它,您可以很快找到这些日历如何重复。我找到了这个。

"The Gregorian calendar repeats itself in 28-year cycles. A calendar for a particular non-leap year repeats itself after 11 years twice and repeats itself after six years once in a 28-year span. Calendars for leap years repeat themselves every 28 years."

一年中特定的一天是如何重复的,我想可能会稍微复杂一些,但如果是这样的话就不会复杂很多。

七个 non-leap 日历中的任何一个当然会与从 1 月到 2 月的闰年日历中的一个相匹配,当然除了 2 月 29 日。乍一看似乎很明显,从 3 月开始也将有一场比赛,只是与 1 月 28 日相比,它将有一天和一个日历的偏差。需要检查。我想知道这是否会影响算法,如果会,如何影响。

如果您输入的是 2 月 29 日,那么显然只需要考虑闰年。

由此看来,写一个算法应该很简单了。

希望这是有道理的。我不确定每 400 年会发生什么,或者是否会出现其他问题。但是 运行 通过所有合理的日期来测试并根据需要进行调整是很容易的。