简体   繁体   English

获取给定日期的月份中的星期几

[英]Get the number of the week of the month from a given date

I have a set of dates from which I must obtain the week of the month. 我有一组日期,必须从这些日期中获取月份中的星期几。

There is a lot of literature on how to obtain the week of the year using VBA code but not the week number of the month. 关于如何使用VBA代码获取一年中的星期几的文献很多,而没有月份中的星期几的文献。

For instance 03-Mar-13 would give week 1 of March, instead I end up with a result of week 10. 例如,13年3月3日将给出3月的第1周,而我最终得到第10周的结果。

I know this is old, but I came across this and thought I would add my code. 我知道这很旧,但是我碰到了这一点,以为我会添加我的代码。 (Built from rvalerio's answer) (根据rvalerio的答案构建)

Private Function getWeekOfMonth(testDate As Date) As Integer
    getWeekOfMonth = CInt(Format(testDate, "ww")) - CInt(Format(Format(testDate, "mm/01/yyyy"), "ww")) + 1
End Function

This isn't the most elegant code, but it worked for me. 这不是最优雅的代码,但对我有用。

Some assumptions: 一些假设:

  1. This is a UDF and can be used on a spreadsheet or in code 这是UDF,可以在电子表格或代码中使用
  2. Weeks start on Sundays 周从星期日开始
  3. Week 1 can be incomplete week 第1周可能是不完整的一周

===== =====

Function WeekOfMonth(selDate As Date)
    Dim DayOfFirst As Integer
    Dim StartOfWeek2 As Integer
    Dim weekNum As Integer

    DayOfFirst = Weekday(DateSerial(Year(selDate), Month(selDate), 1), vbSunday)
    StartOfWeek2 = (7 - DayOfFirst) + 2

    Select Case selDate
        Case DateSerial(Year(selDate), Month(selDate), 1) _
        To DateSerial(Year(selDate), Month(selDate), StartOfWeek2 - 1)
            weekNum = 1

        Case DateSerial(Year(selDate), Month(selDate), StartOfWeek2) _
        To DateSerial(Year(selDate), Month(selDate), StartOfWeek2 + 6)
            weekNum = 2

        Case DateSerial(Year(selDate), Month(selDate), StartOfWeek2 + 7) _
        To DateSerial(Year(selDate), Month(selDate), StartOfWeek2 + 13)
            weekNum = 3

        Case DateSerial(Year(selDate), Month(selDate), StartOfWeek2 + 14) _
        To DateSerial(Year(selDate), Month(selDate), StartOfWeek2 + 20)
            weekNum = 4

        Case DateSerial(Year(selDate), Month(selDate), StartOfWeek2 + 21) _
        To DateSerial(Year(selDate), Month(selDate), StartOfWeek2 + 27)
            weekNum = 5

        Case DateSerial(Year(selDate), Month(selDate), StartOfWeek2 + 28) _
        To DateSerial(Year(selDate), Month(selDate) + 1, 1)
            weekNum = 6
    End Select

    WeekOfMonth = weekNum
End Function

You could perhaps calculate it this way: 您也许可以这样计算:

  • calculate week1 = week of the year of 1st of MONTH 计算week1 = MONTH年1月1日
  • calculate week2 = week of the year of Nth of MONTH 计算week2 = MONTH年的第N周
  • desired result = week2 - week1 + 1 所需结果=第2周-第1周+ 1

Does this help? 这有帮助吗?

There are a few clever answers here, however the question did say, "For instance 03-Mar-13 would give week 1 of march." 这里有一些聪明的答案,但是问题确实说:“例如13年3月3日将给3月的第1周。” The answers provided here will return week 2 for that date because March 1st is day 6 of a week, which makes March 3rd day 1 again, and thus part of week 2. 此处提供的答案将返回该日期的第2周,因为3月1日是一周的第6天,因此3月3日又是第1天,因此是第2周的一部分。

To put it another way, the methods given by guitarthrower, rvalerio, and user3496574 are equivalent to counting the rows on a monthly calendar, as opposed to counting the full weeks starting at day one. 换句话说,guitarthrower,rvalerio和user3496574给出的方法等效于对每月日历中的行进行计数,而不是对从第一天开始的整周进行计数。 So the results can go up to 6, whereas if you are counting by the full week, you can go up only to 5 (and February obviously has only 4 full weeks). 因此,结果最多可以增加到6,而如果按整周计算,则最多只能增加5(而2月显然只有4个整周)。

It depends on how you want to count the weeks. 这取决于您要如何计算周数。 I don't know that either way is right or wrong. 我不知道这是对还是错。 But technically if you want to count weeks as blocks of 7 days that begin on the 1st of the month -- which is what I wanted to do -- then you need to get the day of the month, divide by 7, and then round up. 但是从技术上讲,如果您希望将周数从7月1日的第一天开始算起-这就是我想要做的-那么您需要获取月中的某天,除以7,然后四舍五入起来

So in Excel it is something like: 因此在Excel中,它类似于:

=ROUNDUP(DAY(MyDate)/7,0)

VBA doesn't have a round up function built-in, but obviously this would work: VBA没有内置的舍入功能,但是显然这可以工作:

Function WeekOfMonth(TestDate As Date) As Integer
    WeekOfMonth = Application.WorksheetFunction.RoundUp(Day(TestDate) / 7, 0)
End Function 

To be as complete as possible, here is a solution that is slightly longer but doesn't access an Excel function, and since it uses fairly simple logic it might be more efficient if more data is involved: 为了尽可能完整,这是一个稍长一些的解决方案,但是不访问Excel函数,并且由于它使用的是相当简单的逻辑,因此如果涉及到更多数据,它可能会更有效:

Function WeekOfMonth(TestDate As Date) As Integer

    WeekOfMonth = RoundUpVBA(Day(TestDate) / 7,0)

End Function


Function RoundUpVBA(InputDbl As Double, Digits As Integer) As Double

    If InputDbl >= O Then
        If InputDbl = Round(InputDbl, Digits) Then RoundUpVBA = InputDbl Else RoundUpVBA = Round(InputDbl + 0.5 / (10 ^ Digits), Digits)
    Else
        If InputDbl = Round(InputDbl, Digits) Then RoundUpVBA = InputDbl Else RoundUpVBA = Round(InputDbl - 0.5 / (10 ^ Digits), Digits)
    End If

End Function

Also, here is the Excel formula for user3496574's answer, which calculates the week number the other way: 另外,这是user3496574答案的Excel公式,它以另一种方式计算周数:

=WEEKNUM(MyDate) - WEEKNUM(EOMONTH(MyDate,-1)+1)+1

Which could well be faster in some cases than the VBA version. 在某些情况下,这可能比VBA版本更快。

The rest of this here is just additional discussion about the algorithms, rounding, and optimization that you can read if you're interested, but the main answer is above. 本文的其余部分只是有关算法,舍入和优化的其他讨论,如果您有兴趣的话可以阅读,但主要答案在上面。

Stack Overflow provides a discussion of some user-contributed rounding functions in VBA, but the answers are inconsistent. 堆栈溢出讨论了VBA中一些由用户提供的舍入函数,但是答案不一致。 There is also a C++ discussion of rounding up only that is not hard to follow. 还有C ++关于仅舍入的讨论,这并不难理解。 But I think that the method above is fairly to-the-point. 但是我认为上面的方法很关键。

If you do want to round in VBA, then take care that you're not trying to round up an integer, which is already rounded (and maybe not in the way that you wanted). 如果确实要舍入VBA,请注意不要舍入已经舍入的整数(并且可能不是您想要的方式)。

Also note that, probably to try to confuse us, VBA's Round function uses Banker's rounding ( round-half-even ), whereas Excel's rounding does not -- and nor does CInt, which also works differently from Int (and Fix), which truncate the fractional part. 还要注意的是,可能是试图迷惑我们,VBA的Round函数使用银行家舍入( 圆半连 ),而Excel的四舍五入没有-而且也不CINT,这也是工作方式不同 ,从INT(和修复),其截小数部分。

I don't think that using Banker's rounding was a wise engineering decision, but it's what they decided to use. 我认为使用Banker的取整不是明智的工程决策,但这是他们决定使用的方法。 This type of rounding, which can round the .5 part either up or down, reminds me of how cars today try to outsmart us. 这种类型的舍入可以向上或向下舍入.5部分,这使我想起了当今汽车如何超越我们。 For instance, if the windshield wipers are on in mine and I shift into reverse then the rear wipers go on, which at first seems like an electrical glitch, rather than a feature. 例如,如果我的挡风玻璃刮水器打开并且我倒车,则后刮水器继续打开,起初看起来像是电子故障,而不是功能。 The reason for Banker's rounding is to eliminate the upward bias that accumulates if you try to round a whole bunch of numbers. 银行家四舍五入的原因是消除了如果您试图四舍五入一整堆数字所累积的向上偏差。

Now, you can get the results supplied by the previous answers here by using my method, too, if you just offset the day. 现在,如果您只是取消一天,您也可以使用我的方法来获得以前的答案所提供的结果。

So in Excel: 因此在Excel中:

=ROUNDUP(((DAY(MyDate)+WEEKDAY(EOMONTH(MyDate,-1)+1)-1) / 7), 0)

To figure out the offset, I have to get the weekday for the first day of the month. 为了弄清楚偏移量,我必须获取每月第一天的工作日。

So for 03-Mar-13 instead of taking 3 and dividing it by 7, I'm taking 8 and dividing it by 7. So I get 2 now instead of 1. 因此,对于13年3月3日,而不是将3除以7,我将8除以7。所以我现在得到2而不是1。

In VBA: 在VBA中:

Function WeekOfMonth(TestDate As Date) As Integer

    Dim FirstDayOfMonth As Date
    Dim Offset As Integer

    FirstDayOfMonth = TestDate - Day(TestDate) + 1
    Offset = Weekday(FirstDayOfMonth)

    WeekOfMonth = Application.WorksheetFunction.RoundUp((((Day(TestDate) + Offset) - 1) / 7), 0)

End Function

Exceljet supplies these two elegant methods for getting the first day of the month. Exceljet提供了这两种优雅的方法来获取每月的第一天。

You can also take the algorithms from the original answers and use an offset to get my results, although it's not worth it. 您也可以从原始答案中提取算法,并使用偏移量获取我的结果,尽管这样做并不值得。 The formula below does a quick offset, but fails at the end of the month, and adding more complication would only make it less useful: 下面的公式可以快速抵消费用,但在月底​​失败,添加更多的复杂性只会使它的用处不大:

=getWeekOfMonth(MyDate-WEEKDAY(EOMONTH(MyDate,-1)+1)+8)-1

And finally, if you want to do some really quick and dirty rounding up in VBA for these purposes only , here are several hacker-worthy ugly ways to do it. 最后,如果仅出于这些目的 ,您想在VBA中进行一些非常快速和肮脏的总结, 以下是几种值得黑客使用的丑陋方法。 First: 第一:

WeekOfMonth = Day(TestDate) / 7  ' divide by 7
WeekOfMonth = Fix(WeekOfMonth - 0.0001) + 1  ' ugly rounding up

The rounding part is only one line of code. 舍入部分仅是一行代码。 It truncates the decimal point part of the number, but first subtracts a little so that days 1 through 7 give the same result, and then it adds a day so that we start at 1 instead of at 0. 它会截断数字的小数点部分,但首先要减去一点,以使第1天到第7天的结果相同,然后再加上一天,使我们从1开始而不是从0开始。

An even uglier way: 甚至更丑陋的方式:

WeekOfMonth = Day(TestDate) / 7  ' divide by 7
WeekOfMonth = Day(WeekOfMonth + 1.9999)  ' uglier rounding up

That takes advantage of the fact that to VBA a day is just the date value without the decimal point part. 这利用了以下事实:在VBA中,一天只是日期值,没有小数点部分。 (And note that again, to mess with us, VBA and Excel will return different results for DAY in this case.) (再次注意,在这种情况下,VBA和Excel会在DAY返回不同的结果,这使我们感到困惑。)

As I mentioned, Int will also truncate, but another trick that could be used is that CBool and booleans treat a 0 as false but a fraction as true, and a boolean can then be converted back into a whole number -- so boolean logic could be used to convert a fraction into a whole number. 正如我所提到的,Int也会截断,但是可以使用的另一种技巧是CBool​​和boolean将0视为false,但将小数部分视为true,然后可以将boolean转换回整数-因此布尔逻辑可以用于将分数转换为整数。 Not that we're code golfing or anything. 并不是说我们在编码高尔夫或其他任何东西。

Now, for example if I use the first ugly technique, then this is a self-contained function: 现在,例如,如果我使用第一个丑陋的技术,那么这是一个自包含的功能:

Function WeekOfMonth(TestDate As Date) As Integer

    Dim TempWeekDbl As Double

    TempWeekDbl = Day(TestDate) / 7  ' divide by 7
    TempWeekDbl = Fix(TempWeekDbl - 0.0001) + 1  ' ugly rounding up

    WeekOfMonth = TempWeekDbl

End Function

For a function that works for all numbers but only rounds to the whole number, you can use: 对于适用于所有数字但仅舍入为整数的函数,可以使用:

Function RoundUpToWhole(InputDbl As Double) As Integer

    Dim TruncatedDbl As Double

    TruncatedDbl = Fix(InputDbl)

    If TruncatedDbl <> InputDbl Then
        If TruncatedDbl >= 0 Then RoundUpToWhole = TruncatedDbl + 1 Else RoundUpToWhole = TruncatedDbl - 1
    Else
        RoundUpToWhole = TruncatedDbl
    End If

End Function

That function and the original RoundUpVBA function I listed above imitate the Excel ROUNDUP function, which uses round-away-from-zero . 该函数和我上面列出的原始RoundUpVBA函数都模仿Excel ROUNDUP函数,该函数使用从零舍入 Excel's ROUND by contrast uses round-half-up . 相比之下,Excel的ROUND使用round-half-up

The quick one-liner rounding methods I mention here are not all that elegant, and work only because we know that the number is positive, and that the smallest fractional part of the number that we can get is about 0.14 (which is much greater than 0.0001). 我在这里提到的快速单线舍入方法并不十分优雅,并且之所以起作用,仅是因为我们知道该数是正数,并且我们可以获得的数的最小小数部分约为0.14(比0.0001)。 If you're separating out the round-up function to be a general purpose one, then it's better to do it right, to avoid later headaches. 如果您将舍入功能分离为通用功能,则最好正确执行此操作,以避免以后出现麻烦。 But if the algorithm is just contained within this function and is properly marked as ugly rather than as elegant and all-purpose, then it's OK, I believe. 但是,如果该算法仅包含在此函数中,并且被正确标记为丑陋,而不是优雅且用途广泛,则可以,我相信。

Famous last words. 著名遗言。

I wonder if "Famous last words" were ever anyone's famous last words. 我想知道“著名的遗言”是否曾经是任何人著名的遗言。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM