问题
I'm looking for a definitive, standard (i.e. certified as being correct or tested reasonably thoroughly) for calculating the ISO week from a date, in Visual Basic (i.e. to run in a Visual Studio Express 2010 Visual Basic project)
(Unsuccessful) Research so far:
I found some routines that were Visual Basic for Applications oriented; reliant on Excel environment, some things not present in Visual Studio Express 2010 Visual Basic:
- http://www.xtremevbtalk.com/showthread.php?t=180642
- http://www.ozgrid.com/forum/showthread.php?t=41999
- http://www.ozgrid.com/forum/showthread.php?t=50982
- http://www.vbaexpress.com/kb/getarticle.php?kb_id=744
DatePart Visual Basic library function may be useful: http://msdn.microsoft.com/en-us/library/20ee97hz(v=vs.80).aspx - BUT apparently there is a bug: "BUG: Format or DatePart Functions Can Return Wrong Week Number for Last Monday in Year": http://support.microsoft.com/kb/200299 (has this been solved? It's rather old, 2004 so perhaps?)
Other routines: http://forums.devx.com/showthread.php?t=130782 But doesn't state how it is based on the ISO standard
Also, a search on Stack Overflow gave me results that weren't quite what I was looking for: https://stackoverflow.com/search?q=iso+week
回答1:
Thanks @assylias - an answer in that question is pretty close, if not exactly what I'm looking for: https://stackoverflow.com/a/1226248/227926
Note to use this solution I should add that you need to import the Calendar abstract class defintion:
Imports System.Globalization
AND then instantiate an Instance of Calendar, I chose Gregorian Calendar as it is the defacto standard, but there are other (cultural) options:
Dim myCalendar As Calendar = New GregorianCalendar
Then, to quote the solution ( https://stackoverflow.com/a/1226248/227926 ):
You can use the Calendar.GetWeekOfYear method to get the week number of a date, with the CalendarWeekRule.FirstFourDayWeek value to specify how the weeks are determined, and DayOfWeek.Monday to specify the first weekday. That follows the ISO specification.
Example:
int week = myCalendar.GetWeekOfYear(DateTime.Today, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
I will not mark my question as a duplicate, because their question title "Calculate the start and end date of a week given the week number and year in C# (based on the ISO specification)" is distinctly different from mine and they mention C# (not Visual Basic although they do want Visual Basic as well).
Keeping my question present should help others in their search for the same.
Thanks!
回答2:
I don't know about VS Express, but I just wrote this (in full VS) based on the wikipedia article:
Public Function GetIsoYearAndWeek(day As Date) As Integer()
Dim g As New System.Globalization.GregorianCalendar()
Dim year = g.GetYear(day)
Dim dow = g.GetDayOfWeek(day)
'Per MSDN, dow is 0 for sunday, per ISO, this should be 7
If (dow = 0) Then dow = 7
'usually, this calculation gives the week number
'http://en.wikipedia.org/wiki/ISO_week_date#Calculation
Dim week = Math.Floor((g.GetDayOfYear(day) - dow + 10) / 7)
'If the week is 0 or 53, we should do some checks
If (week = 0) Then
'if we get week=0, the week is actually in last year
year -= 1
'A week has 53 weeks only if it starts on a thursday
'or is a leap year starting on a wednesday
Dim jan1st = New Date(year, 1, 1, g)
If (jan1st.DayOfWeek = DayOfWeek.Thursday _
OrElse jan1st.DayOfWeek = DayOfWeek.Wednesday _
AndAlso g.IsLeapYear(year)) Then
week = 53
Else
week = 52
End If
ElseIf (week = 53) Then
'determine if this week's thursday is in this year, if
'it's not, this week is also in the next year
Dim thursday = day.AddDays(4 - dow)
If (g.GetYear(thursday) > year) Then
Return {year + 1, 1}
End If
End If
Return {year, week}
End Function
回答3:
[EDIT] I just noticed the last response to this thread was also made by myself, talk about reinventing the wheel. However, I do think the following class is a nice wrapper for the method explained above, so I modified it to incorporate the calculation method I used there.
I'm not sure about VSExpress, but in full VB, I'd just extend GregorianCalendar to add functionality to get the week year and simplify access to the correct week number:
Public Class IsoWeekCalendar
Inherits Globalization.GregorianCalendar
'Get the ISO week number
Public Overloads Function GetWeekOfYear(d As DateTime) As Integer
Dim Week As Integer
'year is passed byref
GetYearAndWeek(d, Week:=Week)
Return Week
End Function
'Get the year that belongs with the week
Public Function GetWeekYear(d As DateTime) As Integer
Dim Year As Integer
'year is passed byref
GetYearAndWeek(d, Year:=Year)
Return Year
End Function
'Get both the week and year by ref
Public Sub GetYearAndWeek(d As DateTime, Optional ByRef Year As Integer = 0, Optional ByRef Week As Integer = 0)
Dim yearweek = GetYearAndWeek(d)
Year = yearweek.Item1
Week = yearweek.Item2
End Sub
'Get both the week and year
Public Function GetYearAndWeek(d As DateTime) As Tuple(Of Integer, Integer)
Dim year = Me.GetYear(d)
Dim dow = Me.GetDayOfWeek(d)
'Per MSDN, dow is 0 for sunday, per ISO, this should be 7
If (dow = 0) Then dow = 7
'usually, this calculation gives the week number
'http://en.wikipedia.org/wiki/ISO_week_date#Calculation
Dim week As Integer = Math.Floor((Me.GetDayOfYear(d) - dow + 10) / 7)
'If the week is 0 or 53, we should do some checks
If (week = 0) Then
'if we get week=0, the week is actually in last year
year -= 1
'A week has 53 weeks only if it starts on a thursday
'or is a leap year starting on a wednesday
Dim jan1st = New Date(year, 1, 1, Me)
If (jan1st.DayOfWeek = DayOfWeek.Thursday _
OrElse jan1st.DayOfWeek = DayOfWeek.Wednesday _
AndAlso Me.IsLeapYear(year)) Then
week = 53
Else
week = 52
End If
ElseIf (week = 53) Then
'determine if this week's thursday is in this year, if
'it's not, this week is also in the next year
Dim thursday = d.AddDays(4 - dow)
If (Me.GetYear(thursday) > year) Then
year += 1
week = 1
End If
End If
Return Tuple.Create(year, week)
End Function
'Get the date from the year, week and DoW, again, using calculation from
'http://en.wikipedia.org/wiki/ISO_week_date#Calculation
Public Function GetDate(year As Integer, week As Integer, Optional day As DayOfWeek = DayOfWeek.Monday) As DateTime
Dim ordinal = (week * 7) + IsoDayOfWeek(day) - (GetIsoDayOfWeek(New Date(year, 1, 4, Me)) + 3)
Return New Date(year, 1, 1, Me).AddDays(ordinal - 1)
End Function
'Get the previous date with the supplied DoW
Public Function GetPrev(d As DateTime, day As DayOfWeek) As DateTime
If (d.DayOfWeek <> day) Then
Return GetPrev(d.AddDays(-1), day)
End If
Return d
End Function
'Get the next date with the supplied DoW
Public Function GetNext(d As DateTime, day As DayOfWeek) As DateTime
If (d.DayOfWeek <> day) Then
Return GetPrev(d.AddDays(1), day)
End If
Return d
End Function
'Get the ISO day of week
Public Function GetIsoDayOfWeek(time As Date) As Integer
Return IsoDayOfWeek(MyBase.GetDayOfWeek(time))
End Function
'Translate .NET DoW to ISO DoW
Public Function IsoDayOfWeek(d As DayOfWeek) As Integer
If (d = DayOfWeek.Sunday) Then Return 7
Return d
End Function
End Class
来源:https://stackoverflow.com/questions/9741424/looking-for-definitive-iso-week-calculator-in-visual-basic-in-visual-studio-exp