Condividi tramite


Le funzioni Format o DatePart possono restituire un numero di settimana errato per l'ultimo lunedì nell'anno

Avvertimento

Si è verificato un problema con l'uso di questa funzione. L'ultimo lunedì in alcuni anni di calendario può essere restituito come settimana 53 quando dovrebbe essere settimana 1. Per altre informazioni e una soluzione alternativa, vedere Le funzioni Format o DatePart possono restituire un numero di settimana errato per l'ultimo lunedì dell'anno.

Sintomi

Quando si usa la funzione Format o DatePart per determinare il numero di settimana per le date utilizzando la sintassi seguente:

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

L'ultimo lunedì in alcuni anni di calendario viene restituito come settimana 53 quando dovrebbe essere settimana 1.

Motivo

Quando si determina il numero di settimana di una data in base allo standard ISO 8601, la chiamata di funzione sottostante al file Oleaut32.dll restituisce erroneamente settimana 53 anziché settimana 1 per l'ultimo lunedì in determinati anni.

Risoluzione

Usare una funzione definita dall'utente per restituire il numero settimana in base alle regole per lo standard ISO 8601. Un esempio è incluso in questo articolo.

Altre informazioni

Lo standard ISO 8601 viene ampiamente usato in Europa e include:

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."

Può essere implementato applicando queste regole per le settimane di calendario:

  • Un anno è suddiviso in 52 o 53 settimane di calendario.
  • Una settimana di calendario ha sette giorni. Lunedì è il giorno 1 e domenica è il giorno 7.
  • La prima settimana di calendario di un anno è quella contenente almeno quattro giorni.
  • Se un anno non viene concluso la domenica, i suoi ultimi 1-3 giorni appartengono alla prima settimana di calendario dell'anno successivo o i primi 1-3 giorni dell'anno prossimo appartengono all'ultima settimana del calendario dell'anno corrente.
  • Solo un anno a partire o concludendo il giovedì ha 53 settimane di calendario.

In Visual Basic e Visual Basic, Applications Edition, tutte le funzionalità di data, ad eccezione della funzione DateSerial, provengono dalle chiamate al file Oleaut32.dll. Poiché entrambe le funzioni Format() e DatePart() possono restituire il numero di settimana del calendario per una determinata data, entrambi sono interessati da questo bug. Per evitare questo problema, è necessario usare il codice alternativo fornito da questo articolo.

Passaggi per riprodurre il comportamento

  1. Aprire il progetto Visual Basic all'interno di un'applicazione di Office (ALT + F11).

  2. Dal menu Progetto aggiungere un nuovo modulo.

  3. Incollare il codice seguente nel modulo:

    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. Usare (CTRL+G) per aprire la finestra immediata se non è già aperta.

  5. Digitare ? Test1 nella finestra Immediata e premere INVIO. Si notino i risultati seguenti nella finestra Immediata:

    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 questo formato, tutte le settimane iniziano con lunedì, in modo che il 29 dicembre 2003 deve essere considerato l'inizio della settimana 1 e non parte della settimana 53.

  6. Digitare ? Test2 nella finestra Immediata e premere INVIO per visualizzare un elenco di date nell'intervallo specificato che riscontrano questo problema. L'elenco include la data, il giorno della settimana (sempre lunedì), il numero settimana restituito dal formato (53) e il numero settimana che deve restituire (1.) Per esempio:

    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
    ...
    

Soluzioni alternative

Se si usano le funzioni Format o DatePart, è necessario controllare il valore restituito. Quando è 53, eseguire un altro controllo e forzare un ritorno di 1, se necessario. Questo esempio di codice illustra un modo per eseguire questa operazione:

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

È possibile evitare di usare queste funzioni per determinare il numero settimana scrivendo codice che implementa le regole ISO 8601 descritte in precedenza. Nell'esempio seguente viene illustrata una funzione di sostituzione per restituire il numero settimana.

Esempio dettagliato

  1. Aprire il progetto Visual Basic all'interno di un'applicazione di Office (ALT + F11).

  2. Dal menu Progetto aggiungere un nuovo modulo.

  3. Incollare il codice seguente nel modulo:

    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. Usare (CTRL+G) per aprire la finestra immediata se non è già aperta.

  5. Digitare ? Test3 nella finestra Immediata e premere INVIO. Si notino i risultati seguenti nella finestra Immediata:

    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
    

    Lunedì è considerato settimana 1 come dovrebbe essere.