ISO weeknumber

Just starting out? Need help? Post your questions and find answers here.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6161
Joined: Sat May 17, 2003 11:31 am
Contact:

ISO weeknumber

Post by blueznl »

I'm looking for a good example how to calculate the ISO weeknumber. I've been looking in the forum, as well as checking out the internet, and thus far I've seen many examples, but none I could understand or convert.

Would anyone have a snippet handy of sample code?
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ISO weeknumber

Post by wilbert »

The week that contains january 4 is week 1.
ISO weeks start on monday.

So maybe you could use DayOfWeek(Date) to check what kind of day january 4 of a year was (monday, tuesday etc.), and use that to determine the start of week 1.
One you know the start of week 1, you could determine the week of another date in that year be taking the difference in time stamp to see how many weeks have passed.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
TI-994A
Addict
Addict
Posts: 2512
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: ISO weeknumber

Post by TI-994A »

blueznl wrote:I'm looking for a good example how to calculate the ISO weeknumber.
Hello blueznl. Here's a simple implementation of the ISO 8601 week calculator:

Code: Select all

Procedure ISOWeek(date)
  year = Year(date)
  firstDay = DayOfWeek(Date(year, 1, 1, 0, 0, 0))
  If firstDay = 0
    firstDay = 7
  EndIf
  If firstDay <= 4
    ISOday = DayOfYear(date) + (firstDay - 1)
  Else
    ISOday = DayOfYear(date) - (8 - firstDay)
  EndIf
  ISOwk = Round(ISOday / 7, #PB_Round_Up)
  If Not ISOwk
    ISOwk = ISOWeek(Date(year - 1, 12, 31, 0, 0, 0))
  EndIf
  ;------
  If ISOwk = 53 And Month(date) = 12 And
     DayOfWeek(Date(year + 1, 1, 1, 0, 0, 0)) <= 4
    ISOwk = 1
  EndIf  
  ;------
  ProcedureReturn ISOWk
EndProcedure

Debug ISOWeek(Date())
Debug ISOWeek(Date(1997, 12, 29, 0, 0, 0)) ;week #1
Debug ISOWeek(Date(2012, 12, 31, 0, 0, 0)) ;week #1
Debug ISOWeek(Date(2012, 1, 1, 0, 0, 0))   ;week #52
Debug ISOWeek(Date(2016, 1, 1, 0, 0, 0))   ;week #53
It's a first attempt, so please do test it exhaustively before using. :wink:


EDIT: Code fixed to include a missing 53rd-week check, thanks to testing by Little John. :)
Last edited by TI-994A on Mon Aug 12, 2013 4:43 pm, edited 1 time in total.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ISO weeknumber

Post by wilbert »

Here's my attempt after some thinking

Code: Select all

Procedure.i WeekNumberISO(year, month, day)
  
  Protected week1_prev, week1_this, week1_next
  Protected specified_date = Date(year, month, day, 0, 0, 0)
  
  week1_prev = Date(year - 1, 1, 4, 0, 0, 0)
  week1_this = Date(year    , 1, 4, 0, 0, 0)
  week1_next = Date(year + 1, 1, 4, 0, 0, 0)
  week1_prev - ((DayOfWeek(week1_prev) + 6) % 7) * 86400
  week1_this - ((DayOfWeek(week1_this) + 6) % 7) * 86400
  week1_next - ((DayOfWeek(week1_next) + 6) % 7) * 86400
  
  If specified_date < week1_this
    ; still in last week of previous year
    ProcedureReturn (specified_date - week1_prev) / 604800 + 1
  ElseIf specified_date >= week1_next
    ; already in week 1 of next year
    ProcedureReturn 1
  Else  
    ProcedureReturn (specified_date - week1_this) / 604800 + 1
  EndIf
  
EndProcedure
  
MessageRequester("", Str(WeekNumberISO(2013, 08, 11)))
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
falsam
Enthusiast
Enthusiast
Posts: 630
Joined: Wed Sep 21, 2011 9:11 am
Location: France
Contact:

Re: ISO weeknumber

Post by falsam »

@TI-994A & @wilbert : Interesting code. Thanks for sharing. :)

➽ Windows 11 64-bit - PB 6.0 x64 - AMD Ryzen 7 - NVIDIA GeForce GTX 1650 Ti

Sorry for my bad english and the Dunning–Kruger effect.
Little John
Addict
Addict
Posts: 4527
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: ISO weeknumber

Post by Little John »

Here is my version.

//edit: Code improved.

Code: Select all

EnableExplicit

Procedure.i ISOdayOfWeek (date.i)
   Protected d.i
   
   d = DayOfWeek(date)
   If d = 0
      d = 7             ; for Sunday, return 7 instead of 0
   EndIf
   ProcedureReturn d
EndProcedure


Procedure.i ISOweek (date.i)
   ; The calculations are based on the fact that the first week of a year
   ; always contains January 4.
   ; [according to http://en.wikipedia.org/wiki/Seven-day_week#Week_numbering
   ;  or better    http://de.wikipedia.org/wiki/Woche#Kalenderwoche]
   
   Protected doy.i=DayOfYear(date), year.i=Year(date)
   Protected lastPrev.i  ; last day of last week of previous year
   Protected lastThis.i  ; last day of last week of current  year
   
   lastPrev = 4 - ISOdayOfWeek(Date(year, 1, 4, 0,0,0))
   lastThis = 4 - ISOdayOfWeek(Date(year,12,28, 0,0,0)) + DayOfYear(Date(year,12,31, 0,0,0))
   
   If doy <= lastThis
      If doy <= lastPrev
         ; The given day is in the last week of the previous year.
         doy + DayOfYear(Date(year-1,12,31, 0,0,0))
         lastPrev = 4 - ISOdayOfWeek(Date(year-1,1,4, 0,0,0))
      EndIf
      ProcedureReturn Round((doy-lastPrev)/7, #PB_Round_Up)
   Else
      ; The given day is in the first week of the next year.
      ProcedureReturn 1
   EndIf
EndProcedure


; -- Demo
Debug ISOweek(Date(2009,12,31, 0,0,0))  ; 53

Debug ISOweek(Date(2010, 1, 1, 0,0,0))  ; 53
Debug ISOweek(Date(2010, 1, 2, 0,0,0))  ; 53
Debug ISOweek(Date(2010, 1, 3, 0,0,0))  ; 53
Debug ISOweek(Date(2010, 1, 4, 0,0,0))  ;  1
Debug ISOweek(Date(2010,12,31, 0,0,0))  ; 52

Debug ISOweek(Date(2011, 1, 1, 0,0,0))  ; 52
Debug ISOweek(Date(2011, 1, 2, 0,0,0))  ; 52
Debug ISOweek(Date(2011, 1, 3, 0,0,0))  ;  1
Debug ISOweek(Date(2011,12,31, 0,0,0))  ; 52

Debug ISOweek(Date(2012, 1, 1, 0,0,0))  ; 52
Debug ISOweek(Date(2012, 1, 2, 0,0,0))  ;  1
Debug ISOweek(Date(2012,12,30, 0,0,0))  ; 52
Debug ISOweek(Date(2012,12,31, 0,0,0))  ;  1
Last edited by Little John on Mon Aug 12, 2013 11:38 am, edited 1 time in total.
Little John
Addict
Addict
Posts: 4527
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: ISO weeknumber

Post by Little John »

Hi TI-994A,

I think I've found a bug in your code. :-)

Try

Code: Select all

Debug ISOWeek(Date(2012,12,31, 0,0,0))
That yields 53, but it should be 1 (according to my paper calendar, or e.g. to this webpage).
User avatar
TI-994A
Addict
Addict
Posts: 2512
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: ISO weeknumber

Post by TI-994A »

Little John wrote:I think I've found a bug in your code. :-)
Hello Little John. You're right; thank you for the catch. I've modified the code in my earlier post, and it now includes a check to validate the 53rd week. :)
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6161
Joined: Sat May 17, 2003 11:31 am
Contact:

Re: ISO weeknumber

Post by blueznl »

The brilliance in this forum never fails to amaze me... thanks all! (Weeknumber made it into my little graphical experiment.)
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
Michael Vogel
Addict
Addict
Posts: 2677
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: ISO weeknumber

Post by Michael Vogel »

I did a short one including some prime numbers...

Code: Select all

Procedure ISOWeek(date)
	date=date/86400+3
	ProcedureReturn (date-(Date(Year((date-date%7)*86400),1,date%7+5,0,0,0)/86400-11))/7
EndProcedure

Debug ISOWeek(Date())
Debug ISOWeek(Date(1997, 12, 29, 0, 0, 0)) ;week #1
Debug ISOWeek(Date(2012, 12, 31, 0, 0, 0)) ;week #1
Debug ISOWeek(Date(2012, 1, 1, 0, 0, 0))   ;week #52
Debug ISOWeek(Date(2016, 1, 1, 0, 0, 0))   ;week #53
User avatar
Fig
Enthusiast
Enthusiast
Posts: 351
Joined: Thu Apr 30, 2009 5:23 pm
Location: Côtes d'Azur, France

Re: ISO weeknumber

Post by Fig »

Sorry, Mickael, your code is wrong, I just discovered it today (I was using your snipet in a software ^^)
Today we are in the 35 week and your snipet gives 36...

Too bad, your solution was pretty elegant... :wink:
There are 2 methods to program bugless.
But only the third works fine.

Win10, Pb x64 5.71 LTS
Post Reply