从VBA上的给定日期获取该月的周数

时间:2014-02-10 23:29:07

标签: date excel-vba week-number vba excel

我有一组日期,我必须获得本月的一周,有很多关于如何使用VBA代码获取一周中的文献,但没有关于如何获得周数的文献这个月。例如03-Mar-13将给出第3周的第1周,而不是第10周的结果 所有帮助apreciated

4 个答案:

答案 0 :(得分:7)

这不是最优雅的代码,但它对我有用。

一些假设:

  1. 这是一个UDF,可以在电子表格或代码中使用
  2. 周日从星期日开始
  3. 第1周可能是不完整的一周
  4. =====

    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
    

答案 1 :(得分:7)

我知道这是旧的,但我遇到了这个,并认为我会添加我的代码。 (根据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

答案 2 :(得分:3)

这里有一些聪明的答案,但是问题确实说:“例如13年3月3日将给3月的第1周。”此处提供的答案将返回该日期的第2周,因为3月1日是一周的第6天,因此3月3日又是第1天,因此也就是第2周的一部分。

换句话说,guitarthrower,rvalerio和user3496574给出的方法等效于对每月日历上的行进行计数,而不是对从第一天开始的整周进行计数。因此,结果最多可以增加到6,而如果按整周计算,则最多只能增加5(而2月显然只有4个整周)。

这取决于您要如何计算周数。我不知道这是对还是错。但是从技术上讲,如果您希望将周数从7月1日的第一天开始算起-这就是我想要做的-那么您需要获取月中的某天,除以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函数使用Banker的舍入( round-half-even ),而Excel的舍入不是-CInt也不会,{ {3}}来自Int(和Fix),它截断了小数部分。

我不认为使用Banker的取整是明智的工程决策,但这是他们决定使用的方法。这种类型的舍入可以向上或向下舍入.5部分,这使我想起了当今汽车如何超越我们。例如,如果我的挡风玻璃刮水器打开并且我倒车,则后刮水器继续打开,起初看起来像是电子故障,而不是功能。银行家四舍五入的原因是为了消除在尝试对一整堆数字进行四舍五入时积累的向上偏差。

现在,如果您只是取消一天,您也可以使用我的方法来获得以前的答案提供的结果。

因此在Excel中:

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

要弄清偏移量,我必须获取每月第一天的工作日。

因此,对于13年3月3日,而不是将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提供works differently来获取每月的第一天。

您也可以从原始答案中提取算法,并使用偏移量获取我的结果,尽管这样做并不值得。下面的公式可以快速补偿,但在月底​​失败,添加更多的复杂性只会使其用处不大:

=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​​和boolean将0视为false,但将小数部分视为true,然后可以将boolean转换回整数-因此布尔逻辑可用于将小数转换为整数。并不是说我们是these two elegant methods还是其他任何人。

现在,例如,如果我使用第一个丑陋的技巧,那么这是一个自包含的功能:

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函数,该函数使用 code golfing 。相比之下,Excel的ROUND使用四舍五入

我在这里提到的快速单线舍入方法并不十分优雅,并且之所以起作用,仅是因为我们知道该数字为正数,并且我们可以获得的数字的最小小数部分约为0.14(即远大于0.0001)。如果您将舍入功能分离为通用功能,则最好正确执行此操作,以避免以后出现麻烦。但是,如果该算法仅包含在此函数中,并且被正确标记为丑陋而不是优雅且用途广泛,那么就可以了。

著名的遗言。

我想知道“著名的遗言”是否曾经是任何人著名的遗言。

答案 3 :(得分:2)

你也许可以这样计算:

  • 计算第1周= MONTH第1年的一周
  • 计算第2周=每月第N周的一周
  • 期望结果=第2周 - 第1周+ 1

这有帮助吗?