Xojo.Core.Date.WeekOfYear needed

The new framework has functions to get the day of the week and day of the year,
but I also need the week number of the date.
Since Xojo.Core.Date.WeekOfYear does not exist, can anyone give me a simple way to calculate the correct week number?
Preferably according to the ISO standard allowing for the first week to be week 53 like in 2016.

Ideally, this should be in the Xojo.Core.Date like it is in the old framework Date.

Boudewijn
I now have the same problem. Did you find a work-around for this?
Jim

Ah, found it –

be better if it was right :frowning:
try 2005,01,01 and you get week = 0 which is really wrong
its missing the last part of the paragraph “If the week number thus obtained equals 0, it means that the given date belongs to the preceding (week-based) year. If a week number of 53 is obtained, one must check that the date is not actually in week 1 of the following year.”

if that year really have 53 weeks (Years may have 53 weeks; incomplete week, but 53 weeks !).

I do not know how the number of weeks per year is coded in Xojo.

Put this in a module:

Function WeekOfYearISO(extends d as date) As integer
  
  // make a copy of the date to avoid modifying the original
  dim dd as new Date(d)
  
  // ISO week date weeks start on monday
  // so correct the day number
  dim dayNr as integer = ((dd.dayOfWeek-1) + 6) mod 7
  
  // ISO 8601 states that week 1 is the week
  // with the first thursday of that year.
  // Set the target date to the thursday in the target week
  dd.day = dd.day - dayNr + 3
  
  // Store the totalseconds value of the target date
  dim firstThursday as Double = dd.TotalSeconds
  
  // Set the target to the first thursday of the year
  // First set the target to january first
  dd.Month = 1
  dd.Day = 1
  // Not a thursday? Correct the date to the next thursday
  if dd.dayOfWeek <> 5 then
    dd.Day = 1 + ((4 - (dd.dayOfWeek-1)) + 7) mod 7
  end if
  
  // The weeknumber is the number of weeks between the
  // first thursday of the year and the thursday in the target week
  return 1 + ceil((firstThursday - dd.totalSeconds) / 604800) // 604800 = 7 * 24 * 3600
  
End Function

ummm … nice but this isn’t for Xojo.Core.Date as the OP & Subject of the thread are looking for

This extends a Xojo.Core.Date, though it uses internally a Date object.
One can change it to only use Xojo.Core.Date with some code refactor.

Function WeekOfYearISO(extends d as Xojo.Core.Date) As integer
  
  // make a copy of the date to avoid modifying the original
  dim dd as new Date(d.year, d.month, d.day)
  
  // ISO week date weeks start on monday
  // so correct the day number
  dim dayNr as integer = ((dd.dayOfWeek-1) + 6) mod 7
  
  // ISO 8601 states that week 1 is the week
  // with the first thursday of that year.
  // Set the target date to the thursday in the target week
  dd.day = dd.day - dayNr + 3
  
  // Store the totalseconds value of the target date
  dim firstThursday as Double = dd.TotalSeconds
  
  // Set the target to the first thursday of the year
  // First set the target to january first
  dd.Month = 1
  dd.Day = 1
  // Not a thursday? Correct the date to the next thursday
  if dd.dayOfWeek <> 5 then
    dd.Day = 1 + ((4 - (dd.dayOfWeek-1)) + 7) mod 7
  end if
  
  // The weeknumber is the number of weeks between the
  // first thursday of the year and the thursday in the target week
  return 1 + ceil((firstThursday - dd.totalSeconds) / 604800) // 604800 = 7 * 24 * 3600
  
End Function

I must be missing something here:

dim dayNr as integer = ((dd.dayOfWeek-1) + 6) mod 7

Let’s say we’re talking about a Wednesday. That would be day 4 normally, so this formula would do this:

((4 - 1) + 6) mod 7 = 2

But ISO day 2 is Tuesday.

Changed it to not be an extends (which really doesn’t affect things)
The ran it through all the tests on the wikipedia page like

  dim woy as integer 
  // Examples of contemporary dates around New Year’s Day
  // Date    Notes
  // Gregorian    ISO
  // Sat 1 Jan 2005    2005-01-01    2004-W53-6    
  woy = WeekOfYearISO( new xojo.Core.Date(2005,01,01,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 53 then break
  
  // Sun 2 Jan 2005    2005-01-02    2004-W53-7
  woy = WeekOfYearISO( new xojo.Core.Date(2005,01,02,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 53 then break
  
  // Sat 31 Dec 2005    2005-12-31    2005-W52-6
  woy = WeekOfYearISO( new xojo.Core.Date(2005,12,31,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 52 then break
  
  // Mon 1 Jan 2007    2007-01-01    2007-W01-1    Both years 2007 start with the same day.
  woy = WeekOfYearISO( new xojo.Core.Date(2007,01,01,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 1 then break
  
  // Sun 30 Dec 2007    2007-12-30    2007-W52-7    
  woy = WeekOfYearISO( new xojo.Core.Date(2007,12,30,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 53 then break
  
  // Mon 31 Dec 2007    2007-12-31    2008-W01-1
  woy = WeekOfYearISO( new xojo.Core.Date(2007,12,31,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 1 then break
  
  // Tue 1 Jan 2008    2008-01-01    2008-W01-2    Gregorian year 2008 is a leap year. ISO year 2008 is 2 days shorter: 1 day longer at the start, 3 days shorter at the end.
  woy = WeekOfYearISO( new xojo.Core.Date(2008,1,1,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 1 then break
  
  // Sun 28 Dec 2008    2008-12-28    2008-W52-7    ISO year 2009 begins three days before the end of Gregorian 2008.
  woy = WeekOfYearISO( new xojo.Core.Date(2008,12,28,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 52 then break
  
  // Mon 29 Dec 2008    2008-12-29    2009-W01-1
  woy = WeekOfYearISO( new xojo.Core.Date(2008,12,29,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 1 then break
  
  // Tue 30 Dec 2008    2008-12-30    2009-W01-2
  woy = WeekOfYearISO( new xojo.Core.Date(2008,12,30,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 1 then break
  
  // Wed 31 Dec 2008    2008-12-31    2009-W01-3
  woy = WeekOfYearISO( new xojo.Core.Date(2008,12,31,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 1 then break
  
  // Thu 1 Jan 2009    2009-01-01    2009-W01-4
  woy = WeekOfYearISO( new xojo.Core.Date(2009,1,1,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 1 then break
  
  // Thu 31 Dec 2009    2009-12-31    2009-W53-4    ISO year 2009 has 53 weeks and ends three days into Gregorian year 2010.
  woy = WeekOfYearISO( new xojo.Core.Date(2009,12,31,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 53 then break
  
  // Fri 1 Jan 2010    2010-01-01    2009-W53-5
  woy = WeekOfYearISO( new xojo.Core.Date(2010,1,1,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 53 then break
  
  // Sat 2 Jan 2010    2010-01-02    2009-W53-6
  woy = WeekOfYearISO( new xojo.Core.Date(2010,1,2,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 53 then break
  
  // Sun 3 Jan 2010    2010-01-03    2009-W53-7
  woy = WeekOfYearISO( new xojo.Core.Date(2010,1,3,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 53 then break

it fails several as well :frowning:

Hmm something is probably wrong…
Thanks for testing it, I will recheck :frowning:

Norman, for me it fails the Sun 30 Dec 2007 2007-12-30 2007-W52-7 only of the above tests.
Which one fails for you please?

It fails here as well. Maybe it’s a typo?

Less sophisticated and not with xojo.Core.Date but I use these:

[code]Function getWeekOneISO(iYear As Integer) As Date
// Returns the start Date of the First Week Number

// Find the first Thursday of the year
Dim dFirstThu As New Date
dFirstThu.Year = iYear
dFirstThu.Month = 1
dFirstThu.Day = 1
While dFirstThu.DayOfWeek <> 5
dFirstThu.Day = dFirstThu.Day + 1
Wend

// Week one starts the Monday before
Dim dWeekOne As New Date(dFirstThu)
While dWeekOne.DayOfWeek <> 2
dWeekOne.Day = dWeekOne.Day - 1
Wend

Return dWeekOne
End Function
[/code]

[code]Function getWeekOfYearISO(dDate As date) As Integer
// Returns ISO week number
Dim dWeekOne As Date
Dim iYear As Integer = dDate.Year

If dDate > New Date(iYear, 12, 29) Then
dWeekOne = getWeekOneISO(iYear + 1)
If dDate < dWeekOne Then
dWeekOne = getWeekOneISO(iYear)
Else
iYear = iYear + 1
End If
Else
dWeekOne = getWeekOneISO(iYear)
If dDate < dWeekOne Then
dWeekOne = getWeekOneISO(iYear -1)
End If
End If

Return ((dDate.TotalSeconds - dWeekOne.TotalSeconds) / 86400 ) / 7 + 1
End Function
[/code]

Norman the only failing test seems like to be a typo.
In fact, even your code states

// Sun 30 Dec 2007    2007-12-30    2007-W52-7    
  woy = WeekOfYearISO( new xojo.Core.Date(2007,12,30,0,0,0,0, Xojo.Core.TimeZone.Current) )
  if woy <> 53 then break

so you wrongly expect 53.

You can also confirm this here: http://myweb.ecu.edu/mccartyr/isowdcal.html

Yup
Typo on my part