sobota, 26 grudnia 2015

• Data tygodniowa ISO 8601

Konwersja daty do formatu Daty tygodniowej, zgodnie z normą ISO 8601.

ISO 8601 to międzynarodowa norma ISO określająca sposób zapisu daty i czasu. W standardzie tym używa się sformatowanych ciągów znaków ustawionych w porządku od najbardziej znaczących (rok) do najmniej znaczących (dni, sekundy lub ich części). Norma określa numeryczny format daty i czasu (bez używania nazw miesięcy, dni, tygodni i literowych oznaczeń czasu przed lub po południu).
Na stronie Głównego Urzędu Miar Numerowanie tygodni w roku czytamy, że:

  1. Tydzień jest to okres 7 dni,
  2. Poniedziałek jest pierwszym dniem tygodnia kalendarzowego (ze względu na łatwiejsze rozliczenia handlowe i finansowe)
  3. Pierwszym tygodniem kalendarzowym w obrębie danego roku jest tydzień zawierający pierwszy czwartek tego roku
Oznacza to, że:
  • Tygodnie są numerowane kolejno w obrębie danego roku,
  • Tydzień posiada numer porządkowy w obrębie tego roku kalendarzowego, w którym zawierają się przynajmniej 4 dni tego tygodnia,
  • Ostatnim tygodniem kalendarzowym w obrębie danego roku jest tydzień zawierający ostatni czwartek tego roku,
  • Końcowe i początkowe dni danego roku kalendarzowego mogą należeć odpowiednio do pierwszego tygodnia roku następnego lub do ostatniego tygodnia roku poprzedniego,
  • Przykładowo:
    dni 1 – 3 stycznia 2010 roku należą jeszcze do 53 tygodnia 2009 roku,
    a dni 4 – 10 stycznia 2010 roku należą do 1 tygodnia 2010 roku.

Data tygodniowa

Data tygodniowa zapisywana jest w formacie YYYY-Www-D
  • YYYY - rok zapisywany jest za pomocą czterech cyfr
  • Www - numer tygodnia poprzedzony jest zazwyczaj prefiksem W. Oznaczany jest zawsze dwiema cyframi: od 01 do 52 lub 53
  • Dzień oznaczany jest jedną cyfrą: od 1 (poniedziałek) do 7 (niedziela).
' Pobiera:
'  • datData - data do przekonwertowania na format ISO
'  • strSeparatorDaty - separator daty, domyślnie [-] myślnik
'  • strPrefixTygodnia - prefix przed dwucyfrowym numerem tygodnia, domyślnie [W]
' Zwraca:
'  Przy powodzeniu zwraca sformatowaną datę tygodniową (ISO-8601 )
'  w postaci "YYYY-Www-D"
'  Poszczególne elementy daty opcjonalnie są rozdzielone separatorem daty
'  przekazanym w argumencie strSeparatorDaty
'  Przy niepowodzeniu zwraca ciąg zerowej długości.

Public Function funDataTygodniowaISO8601(datData As Date, _
                  Optional strSeparatorDaty As String = "-", _
                  Optional strPrefixTygodnia As String = "W") As String

Dim intRok As Integer
Dim intTydzien As Integer
Dim intTydzienNastepny As Integer
Dim intDzien As Integer

   ' pobierz rok
   intRok = Year(datData)
   
   ' pobierz numer tygodnia:
   ' vbMonday - tydzień zaczyna się od Poniedziałku,
   ' vbFirstFourDays - pierwszy tydzień roku musi zawierać co najmniej cztery dni
   intTydzien = DatePart("ww", datData, vbMonday, vbFirstFourDays)
   
   ' pobierz numer dnia w tygodniu (licząc od poniedziałku)
   intDzien = WeekDay(datData, vbMonday)
   
   If Month(datData) = 12 And intTydzien = 1 Then
      intRok = intRok + 1
   ElseIf (Month(datData) = 1 And (intTydzien = 52 Or intTydzien = 53)) Then
      intRok = intRok - 1
   End If

   funDataTygodniowaISO8601 = CStr(intRok) & strSeparatorDaty & _
               strPrefixTygodnia & Format$(intTydzien, "00") & _
               strSeparatorDaty & CStr(intDzien)

   MsgBox "UWAGA." & vbNewLine & _
          "Funkcja funDataTygodniowaISO8601 działa nieprawidłowo !" & vbNewLine & _
          "W tej postaci NIE należy jej używać do jakichkolwiek obliczeń!", vbCritical
   
End Function


' poniżej przykład jak wywołać funkcję: funDataTygodniowaISO8601 (...)
Private Sub cmdTest_Click()
Dim strDataISO As String
Dim dtData As Date

   dtData = CDate("3 stycznia 2010")
   'prawidłowo powinniśmy przekazać datę po "amerykańsku" #1/3/2010#
   strDataISO = funDataTygodniowaISO8601(dtData, "-")
   MsgBox "3 stycznia 2010" & " => " & strDataISO


   dtData = CDate("31 grudnia 2007")
   strDataISO = funDataTygodniowaISO8601(dtData, "-")
   MsgBox "31 grudnia 2007" & " => " & strDataISO

End Sub
Po wywołaniu funkcji funDataTygodniowaISO8601 (...), dla dwóch nieprzypadkowych dat, na ekranie w oknach komunikatu widzimy zwracane przez funkcję wyniki:
 
Wyświetlany format daty tygodniowej jest poprawny, ale zwracany wynik przez funkcję funDataTygodniowaISO8601 (...) jest prawidłowy dla daty 3 stycznia 2010 r. (pierwsze okienko komunikatu). Dla daty 31 grudnia 2007 roku, zwracany wynik jest nieprawidłowy.
Po sprawdzeniu na kalendarzu daty dla której zwracany jest nieprawidłowy wynik daty tygodniowej ISO-8601, od razu widzimy, że dzień 31-grudnia 2007 r. powinien być 1-szym dniem, 1-szego tygodnia 2008 roku, ponieważ 1-szy tydzień roku 2008 ma co najmniej 4 dni (zawiera czwartek). Zapis daty tygodniowej powinien mieć postać: 2008-W01-1 tak jak widać w oknie komunikatu po prawej stronie.
Ku przestrodze, w kodzie umieściłem funkcję MsgBox wyświetlającą okno komunikatu z ostrzeżeniem, by funkcji funDataTygodniowaISO8601 w tej postaci nie stosować do jakichkolwiek obliczeń.