Compartir a través de


Las funciones Format o DatePart pueden devolver un número de semana incorrecto para el último lunes del año

Advertencia

Hay un problema con el uso de esta función. El último lunes en algunos años naturales se puede devolver como la semana 53 cuando debería ser la semana 1. Para obtener más información y una solución alternativa, vea Funciones de Formato o DatePart que pueden devolver un número de semana incorrecto para el último lunes del año.

Síntomas

Cuando se usa la función Format o DatePart para determinar el número de semana de las fechas mediante la sintaxis siguiente:

  • Format(AnyDate, "ww", vbMonday, vbFirstFourDays)
  • DatePart("ww", AnyDate, vbMonday, vbFirstFourDays)

El último lunes en algunos años naturales se devuelve como la semana 53, cuando debería ser la semana 1.

Causa

Al determinar el número de semana de una fecha según el estándar ISO 8601, la llamada de función subyacente al archivo Oleaut32.dll devuelve erróneamente la semana 53 en lugar de la semana 1 del último lunes de determinados años.

Solución

Use una función definida por el usuario para devolver el número de semana en función de las reglas del estándar ISO 8601. En este artículo se incluye un ejemplo.

Más información

La norma ISO 8601 se usa ampliamente en Europa e incluye lo siguiente:

ISO 8601 "Data elements and interchange formats - Information interchange   - Representation of dates and times"
ISO 8601 : 1988 (E) paragraph 3.17:
"week, calendar: A seven day period within a calendar year, starting on a Monday and identified by its ordinal number within the year; the first calendar week of the year is the one that includes the first Thursday of that year. In the Gregorian calendar, this is equivalent to the week which includes 4 January."

Esto se puede implementar aplicando estas reglas para las semanas del calendario:

  • Un año se divide en 52 o 53 semanas naturales.
  • Una semana natural tiene siete días. El lunes es el día 1 y el domingo es el día 7.
  • La primera semana natural de un año es la que contiene al menos cuatro días.
  • Si un año no finaliza un domingo, sus 1-3 últimos días pertenecen a la primera semana natural del año próximo o los primeros 1-3 días del año siguiente pertenecen a la última semana natural del año actual.
  • Sólo un año a partir o concluyendo un jueves tiene 53 semanas naturales.

En Visual Basic y Visual Basic para Aplicaciones, todas las funciones de fecha, excepto la función DateSerial, proceden de llamadas al archivo Oleaut32.dll. Dado que las funciones Format() y DatePart() pueden devolver el número de semana natural de una fecha determinada, ambos se ven afectados por este error. Para evitar este problema, debe usar el código alternativo que proporciona este artículo.

Pasos para reproducir el comportamiento

  1. Abra el proyecto de Visual Basic dentro de una aplicación de Office (Alt + F11).

  2. En el menú Proyecto , agregue un nuevo módulo.

  3. Pegue el código siguiente en el módulo:

    Option Explicit
    
    Public Function Test1()
    ' This code tests a "problem" date and the days around it
    Dim DateValue As Date
    Dim i As Integer
    
    Debug.Print "   Format function:"
    DateValue = #12/27/2003#
    For i = 1 To 4   ' examine the last 4 days of the year
     DateValue = DateAdd("d", 1, DateValue)
     Debug.Print "Date: " & DateValue & "   Day: " & _
     Format(DateValue, "ddd") & "   Week: " & _
     Format(DateValue, "ww", vbMonday, vbFirstFourDays)
    Next i
    End Function
    
    Public Function Test2()
    ' This code lists all "Problem" dates within a specified range
     Dim MyDate As Date
     Dim Years As Long
     Dim days As Long
     Dim woy1 As Long
     Dim woy2 As Long
     Dim ToPrint As String
    
     For Years = 1850 To 2050
     For days = 0 To 3
     MyDate = DateSerial(Years, 12, 28 + days)
     woy1 = Format(MyDate, "ww", vbMonday, vbFirstFourDays)
     woy2 = Format(MyDate, "ww", vbMonday, vbFirstFourDays)
     If woy2 > 52 Then
     If Format(MyDate + 7, "ww", vbMonday, vbFirstFourDays) = 2 Then _
     woy2 = 1
     End If
     If woy1 <> woy2 Then
     ToPrint = MyDate & String(13 - Len(CStr(MyDate)), " ")
     ToPrint = ToPrint & Format(MyDate, "dddd") & _
     String(10 - Len(Format(MyDate, "dddd")), " ")
     ToPrint = ToPrint & woy1 & String(5 - Len(CStr(woy1)), " ")
     ToPrint = ToPrint & woy2
     Debug.Print ToPrint
     End If
     Next days
    Next Years
    End Function
    
  4. Use (Ctrl + G) para abrir la ventana Inmediato si aún no está abierta.

  5. ¿Tipo ? Test1 en la ventana Inmediato y presione Entrar, tenga en cuenta los siguientes resultados en la ventana Inmediato:

    Format function:
    Date: 12/28/03   Day: Sun   Week: 52
    Date: 12/29/03   Day: Mon   Week: 53
    Date: 12/30/03   Day: Tue   Week: 1
    Date: 12/31/03   Day: Wed   Week: 1
    

    Con este formato, todas las semanas comienzan con el lunes, por lo que el 29 de diciembre de 2003 debe considerarse el inicio de la semana 1 y no parte de la semana 53.

  6. ¿Tipo ? Test2 en la ventana Inmediato y presione Entrar para ver una lista de fechas en el intervalo especificado que experimentan este problema. La lista incluye la fecha, el día de la semana (siempre lunes), el número de semana devuelto por formato (53) y el número de semana que debe devolver (1). Por ejemplo:

    12/29/1851   Monday    53   1
    12/31/1855   Monday    53   1
    12/30/1867   Monday    53   1
    12/29/1879   Monday    53   1
    12/31/1883   Monday    53   1
    12/30/1895   Monday    53   1
    ...
    

Soluciones alternativas

Si usa las funciones Format o DatePart, debe comprobar el valor devuelto. Cuando sea 53, ejecute otra comprobación y fuerce una devolución de 1, si es necesario. En este ejemplo de código se muestra una manera de hacerlo:

Function WOY (MyDate As Date) As Integer   ' Week Of Year
  WOY = Format(MyDate, "ww", vbMonday, vbFirstFourDays)
  If WOY > 52 Then
    If Format(MyDate + 7, "ww", vbMonday, vbFirstFourDays) = 2 Then WOY = 1
  End If
End Function

Puede evitar usar estas funciones para determinar el número de semana escribiendo código que implemente las reglas ISO 8601 descritas anteriormente. En el ejemplo siguiente se muestra una función de reemplazo para devolver el número de semana.

Ejemplo paso a paso

  1. Abra el proyecto de Visual Basic dentro de una aplicación de Office (Alt + F11).

  2. En el menú Proyecto , agregue un nuevo módulo.

  3. Pegue el código siguiente en el módulo:

    Option Explicit
    
    Function WeekNumber(InDate As Date) As Integer
     Dim DayNo As Integer
     Dim StartDays As Integer
     Dim StopDays As Integer
     Dim StartDay As Integer
     Dim StopDay As Integer
     Dim VNumber As Integer
     Dim ThurFlag As Boolean
    
     DayNo = Days(InDate)
     StartDay = Weekday(DateSerial(Year(InDate), 1, 1)) - 1
     StopDay = Weekday(DateSerial(Year(InDate), 12, 31)) - 1
     ' Number of days belonging to first calendar week
     StartDays = 7 - (StartDay - 1)
     ' Number of days belonging to last calendar week
     StopDays = 7 - (StopDay - 1)
     ' Test to see if the year will have 53 weeks or not
     If StartDay = 4 Or StopDay = 4 Then ThurFlag = True Else ThurFlag = False
     VNumber = (DayNo - StartDays - 4) / 7
     ' If first week has 4 or more days, it will be calendar week 1
     ' If first week has less than 4 days, it will belong to last year's
     ' last calendar week
     If StartDays >= 4 Then 
     WeekNumber = Fix(VNumber) + 2 
     Else 
     WeekNumber = Fix(VNumber) + 1
     End If
     ' Handle years whose last days will belong to coming year's first
     ' calendar week
     If WeekNumber > 52 And ThurFlag = False Then WeekNumber = 1
     ' Handle years whose first days will belong to the last year's 
     ' last calendar week
     If WeekNumber = 0 Then
     WeekNumber = WeekNumber(DateSerial(Year(InDate) - 1, 12, 31))
     End If
    End Function
    
    Function Days(DayNo As Date) As Integer
     Days = DayNo - DateSerial(Year(DayNo), 1, 0)
    End Function
    
    Public Function Test3()
     Dim DateValue As Date, i As Integer
    
     Debug.Print "   WeekNumber function:"
     DateValue = #12/27/2003#
     For i = 1 To 4   ' examine the last 4 days of the year
     DateValue = DateAdd("d", 1, DateValue)
     Debug.Print "Date: " & DateValue & "   Day: " & _
          Format(DateValue, "ddd") & "   Week: " & WeekNumber(DateValue)
     Next i
    End Function
    
  4. Use (Ctrl + G) para abrir la ventana Inmediato si aún no está abierta.

  5. ¿Tipo ? Test3 en la ventana Inmediato y presione Entrar, tenga en cuenta los resultados siguientes en la ventana Inmediato:

    WeekNumber function:
    Date: 12/28/03   Day: Sun   Week: 52
    Date: 12/29/03   Day: Mon   Week: 1
    Date: 12/30/03   Day: Tue   Week: 1
    Date: 12/31/03   Day: Wed   Week: 1
    

    El lunes se considera la semana 1 como debería ser.