从给定日期获取每月的星期数
Posted
技术标签:
【中文标题】从给定日期获取每月的星期数【英文标题】:Get the number of the week of the month from a given date 【发布时间】:2014-03-08 13:03:21 【问题描述】:我有一组日期,我必须从中获取当月的星期几。
关于如何使用 VBA 代码获取一年中的星期而不是月份的星期数的文献很多。
例如 03-Mar-13 会给出 3 月的第 1 周,而不是我最终得到第 10 周的结果。
【问题讨论】:
【参考方案1】:这里有一些聪明的答案,但是问题确实说:“例如,2013 年 3 月 3 日将给出 3 月的第 1 周。”此处提供的答案将返回该日期的第 2 周,因为 3 月 1 日是一周的第 6 天,这使得 3 月 3 日再次成为第 1 天,因此是第 2 周的一部分。
换句话说,guitarthrower、rvalerio 和 user3496574 给出的方法相当于计算月历上的行数,而不是从第一天开始计算整周。所以结果最多可以达到 6 个,而如果按整周计算,则最多只能达到 5 个(而 2 月显然只有 4 个整周)。
这取决于您要如何计算周数。我不知道这两种方法是对还是错。但是从技术上讲,如果您想将周数计为从每月 1 日开始的 7 天块——这是我想做的——那么你需要得到当月的日期,除以 7,然后四舍五入起来。
所以在 Excel 中是这样的:
=ROUNDUP(DAY(MyDate)/7,0)
VBA 没有内置的四舍五入函数,但显然这会起作用:
Function WeekOfMonth(TestDate As Date) As Integer
WeekOfMonth = Application.WorksheetFunction.RoundUp(Day(TestDate) / 7, 0)
End Function
为了尽可能完整,这里有一个稍长但不访问 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
另外,这里是 user3496574 的答案的 Excel 公式,它以另一种方式计算周数:
=WEEKNUM(MyDate) - WEEKNUM(EOMONTH(MyDate,-1)+1)+1
在某些情况下,这可能比 VBA 版本更快。
这里的其余部分只是关于算法、舍入和优化的额外讨论,如果您有兴趣可以阅读,但主要答案在上面。
VBA 中一些用户贡献的舍入函数的堆栈溢出provides a discussion,但答案不一致。还有一个不难理解的C++ discussion of rounding up only。不过我觉得上面的方法还是很中肯的。
如果您确实想在 VBA 中四舍五入,请注意不要尝试对已经四舍五入的整数进行四舍五入(可能不是您想要的方式)。
另外请注意,可能是为了让我们感到困惑,VBA 的 Round 函数使用银行家的四舍五入 (round-half-even),而 Excel 的四舍五入没有 -- 也没有 CInt,这也是 works differently来自 Int(和 Fix),它会截断小数部分。
我不认为使用银行家的四舍五入是一个明智的工程决策,但这是他们决定使用的。这种类型的舍入可以向上或向下舍入 0.5 部分,这让我想起了今天的汽车如何试图超越我们。例如,如果我的挡风玻璃刮水器打开并且我换到倒车档,那么后刮水器就会继续,这乍一看似乎是一个电气故障,而不是一个功能。银行家四舍五入的原因是为了消除如果您尝试对一大堆数字进行四舍五入而累积的向上偏差。
现在,如果您只是抵消一天,您也可以使用我的方法在此处获得先前答案提供的结果。
所以在 Excel 中:
=ROUNDUP(((DAY(MyDate)+WEEKDAY(EOMONTH(MyDate,-1)+1)-1) / 7), 0)
要计算出偏移量,我必须得到该月第一天的工作日。
所以对于 03-Mar-13,而不是取 3 除以 7,我取 8 除以 7。所以我现在得到 2 而不是 1。
在 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 提供 these two elegant methods 用于获取当月的第一天。
您也可以从原始答案中获取算法并使用偏移量来获得我的结果,尽管这不值得。下面的公式可以快速抵消,但在月底失败,增加更多的复杂性只会降低它的用处:
=getWeekOfMonth(MyDate-WEEKDAY(EOMONTH(MyDate,-1)+1)+8)-1
最后,如果你想在 VBA 中进行一些非常快速和肮脏的四舍五入仅出于这些目的,这里有几个黑客值得做的丑陋方法。第一:
WeekOfMonth = Day(TestDate) / 7 ' divide by 7
WeekOfMonth = Fix(WeekOfMonth - 0.0001) + 1 ' ugly rounding up
取整部分只有一行代码。它会截断数字的小数点部分,但首先会减去一点,以便第 1 天到第 7 天给出相同的结果,然后添加一天,以便我们从 1 开始而不是从 0 开始。
更丑的方式:
WeekOfMonth = Day(TestDate) / 7 ' divide by 7
WeekOfMonth = Day(WeekOfMonth + 1.9999) ' uglier rounding up
这利用了这样一个事实,即对于 VBA,一天只是没有小数点部分的日期值。 (请再次注意,在这种情况下,VBA 和 Excel 会为 DAY 返回不同的结果。)
正如我所提到的,Int 也会截断,但可以使用的另一个技巧是 CBool 和布尔值将 0 视为假,而将分数视为真,然后可以将布尔值转换回整数 - 所以布尔逻辑可用于将分数转换为整数。并不是说我们是code golfing 或其他任何人。
现在,例如,如果我使用第一个丑陋的技术,那么这是一个自包含的函数:
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
对于适用于所有数字但只能四舍五入的函数,您可以使用:
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
该函数和我上面列出的原始 RoundUpVBA 函数模仿 Excel ROUNDUP 函数,它使用 round-away-from-zero。相比之下,Excel 的 ROUND 使用 round-half-up。
我在这里提到的快速单行四舍五入方法并不是那么优雅,而且之所以有效,只是因为我们知道这个数字是正数,并且我们可以得到的数字的最小小数部分大约是 0.14(即远大于 0.0001)。如果您要将汇总功能分离为通用功能,那么最好正确执行,以避免以后的麻烦。但是,如果算法只是包含在这个函数中,并且被正确地标记为丑陋而不是优雅和通用的,那么它就可以了,我相信。
著名的遗言。
我想知道“著名的遗言”是否曾经是任何人的著名遗言。
【讨论】:
【参考方案2】:我知道这是旧的,但我遇到了这个并认为我会添加我的代码。 (根据 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
【讨论】:
【参考方案3】:这不是最优雅的代码,但它对我有用。
一些假设:
-
这是一个 UDF,可用于电子表格或代码中
每周从星期日开始
第 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
【讨论】:
太棒了,对此代码投赞成票。我什至在 SQL 查询和表单中的过滤器中实现了它。完美运行。见查询:SELECT WeekOfMonth([table1]![date_field]) AS WeekNumber FROM table1;
【参考方案4】:
你也许可以这样计算:
计算 week1 = MONTH 1st of the year 中的一周 计算 week2 = 第 N 个月的一年中的第几周 期望的结果 = week2 - week1 + 1这有帮助吗?
【讨论】:
以上是关于从给定日期获取每月的星期数的主要内容,如果未能解决你的问题,请参考以下文章
javascript获取当天日期,计算出该天所属周,列出本周从周一至周日的所有日期,求大神帮助!
在 Hive 或 Pyspark 中查询以获取每个星期日和星期六的日期