Requests for "a function to return the week number of a date" are
common. It is essential
• to check which definition is to be used;
• and, for some definitions, to return also the
proper year number, because of the overlaps.
Such a function can also return a Day-of-Week number.
Here, when using JavaScript, Week Number Dates are commonly held in
arrays [y,w,d] and years are assumed to be AD 100 or later. The
possibility of a Date Object representing a time other than 00:00h has
largely been disregarded. JavaScript's Math.floor(X/Y) and
((X/Y)|0) are equivalent, the latter being faster, and match
VBScript/C's X\Y amd Pascal's X div Y.
Each function giving [y, w, d] here should have a
corresponding reverse function.
It should generally be easy enough to convert a week-of-year
algorithm to a corresponding week-of-month one.
Refer to the corresponding parts of
Calendar Weeks
for the associated descriptive material.
It is possible that some code
may inadvertently assume either that local time is GMT or that local
time is less than 12 hours away from GMT. RSVP if you see signs of that
here, unless specific warning is given.
The conversion routines are believed to give correct results, and
the JavaScript ones can be tested on this page.
The underlying algorithms are reasonably efficient, They go directly
to the result, without looping; except that some have a single
recursion.
The implementation of some of the JavaScript algorithms that run on
this page may not be fully optimum. In particular, I have noticed that
using UTC date object functions internally is much faster, and have been
converting more routines. Also, code converting viaDayCount is comparatively fast. See also in JavaScript Date and Time 1 : Date
Arithmetic.
Pascal/Delphi code in my dateprox.pas etc. is mostly older,
and may be less efficient.
Day-of-Week of a given date in early January of a known Gregorian
year number is often needed; that can be rapidly determined with
expressions such as used by Zeller.
For 1901-2099, to get Day-of-Week of January 1st, using Sun=0 :-
(5*(y-1) div 4) mod 7 or Trunc(1.25*(y-1)) mod 7
Use Back to return.
An Error
Note that Appendix B of a draft Internet standard, dated in the
1990's, has a C routine (based on Zeller's) for
getting day-of-week from Y M D in which the left operand of a
mod operator goes negative for some dates early in a century,
giving a wrong result. Uncorrected copies are still on the Net.
All ISO weeks are Monday = 1 to Sunday = 7, and week 01 of
the numbering year is the one containing the first Thursday of the
Gregorian year. Thus up to three days of the first and last weeks for a
year number can be in the adjacent Gregorian year; December 29 to
January 03 can have a numbering year differing from the Gregorian.
A complete ISO Week Number function must also return the Year Number.
It is easy enough also to return the Day-of-Week number, DoW.
Some methods give the Year directly. Otherwise, if the Calendar Month
Number is large and the ISO Week Number is small then increment the Year,
and vice versa, or otherwise.
String forms such as "yyyy-Www" or "yyyy-Www-d" or "yyyy-ww" or
"yyyy-ww-d" can be used, depending on context; or arrays [Y, W, D].
Common Functions
This returns a string "yyyy-Www-d" from an array [y, w, d],
to represent ISO 8601 week numbers.
Ordinal Date (YYYY-DDD)
and Sunday Letter must suffice to
determine Y-W-D. For a Leap Year, use the Letter for the start of the
year, which is not the one for Easter Sunday. But if the Nearest
Thursday is not in the original YYYY, it will be necessary to
reconsider.
In practice, the best method of implementation will depend on the
nature and speed of the available primitive operations, and on whether
the date is represented as a string, as Y-M-D, as Y-D, or as a count of
time or whatever.
If using a [milli-]seconds-based method for DayCount, consider carefully Summer Time, remembering that it can span a New
Year. So rounding the time from January 4th is better than truncating
the time from January 1st.
It can be useful to know that
CJD 0 was a Monday, and that
1970-01-01 was a Thursday.
Date to Week Number and Day-of-Week
When calculating ISO 8601 Week Number, giving the Year Number is
essential. The Day Number will normally be easy to find en
passant.
Absolute Week Number and Day-of-Week
If a DayCount is known, then
(DayCount+K) div 7 gives an Absolute Week
Number (AWN),
1 + (DayCount+K) mod 7 gives the Day-of-Week (DoW).
where K allows for the Day-of-Week of DayCount zero, and
ensures that the arguments of mod & div are
never negative.
Start of Given Week Numbering Year
Round Year-Jan-1st, or truncate Year-Jan-4th, to Monday.
Start of Current Week Numbering Year
Truncate the date to Monday and add 3, truncate to Jan 1
and add 3, truncate to Monday.
Week Number, A
The Week of any day is that of its nearest Thursday :-
to get that Thursday, one can use either of :-
Date = Date + 4 - DoW days
DayCount = DayCount + 3 - (DayCount+K) mod 7 days,
in the course of which Day-of-Week is determined.
The Year is the Calendar Year of that Thursday
The First Thursday of a Calendar Year is in Week Number 1
The Week Number of that nearest Thursday is thus its Zero-Based
Day-of-Calendar-Year, divided by 7, truncated, plus one
Actually finding the First Thursday is, therefore, not necessary
Coded in DobToYWDarr(DArg) using a JavaScript Date Object
Coded in ???(Y, M, D) using only explicit arithmetic
- TO BE ADDED
Week Number, B
Go ahead 3 days, as this method offsets weeks by 3 days
Get the DayCount from 0 = any Thursday (e.g. 1970-01-01)
Get the AWN, from Week 0 starting on DayCount 0, by div 7
Get the Gregorian Year, Wyr, of the start of week AWN
Decrement the AWN by that of Wyr-01-00
Return [Wyr, AWN, DoW]
Coded in YMD2YWD(y, m, d)
Week Number and Day-of-Week to Date
Determine the date of the start of Week 1, Monday Year-01-1,
using one of :-
Find the Day-of-Week of January 4th of the Year,
and the Offset (-6..0 days) from there to the Monday of that Week
Find the Day-of-Week of January 1st of the Year,
and the Offset (-3..+3 days) from there to the nearest Monday;
Increment January 1st/4th by Offset + 7*(Week-1) + (DoW-1) days.
Here, JavaScript Date Objects are used only in testing. The methods
therefore can be implemented in any language.
First Method
The following determines ISO Week Number by explicit arithmetic from
IsLeapYear, Month, Day, and Day-of-Week, and vice versa,
without going explicitly via a day-count.
It shows the possibility of calculating ISO Week Number Www-d
from IsLeapYear, Month, Day, and day-of-week-of-January-1st, since
those suffice to determine the current Day-of-Week, and vice
versa.
Some languages handle dates as DayCounts,
often with Day 0 being 1899-12-30 and Day 25569 being 1970-01-01.
Conversion of DayCount to/from Y W D is easier than conversion to/from Y
M D, and does not need system date routines.
In January 2008 I derived, now in unit
programs/dateutys.pas,
efficient Pascal/Delphi
routines for DateTime to/from YWD, covering the (extendable)
range AD 0001 to 9999. The following JavaScript routines mimic them.
Comparison results which can be used by testers,
showing Weeks 1, 52 & 53 for over 28 years,
are in wknotest.txt.
The following checks should suffice - all weeks have 7 days, Monday
is Day 1 of 1..7, January 4th is in Week Number 1 of 1..52/53, Year
usually matches, numbers YYYYWWD are in increasing order, testing over
28 years with quadrennial Leap Years (or at least over all 14 year
types). That does not fully test the effects of the 100 and 400 year
rules introduced by Pope Gregory XIII.
Test cases should include those tabled in
Calendar Weeks.
These should be evidently the same algorithms as demonstrated.
It shows errors in Opera 9; see in
JavaScript Date and Time Troubles :-
Test DobToYWDarr and YWDarrToDob
Accepts moderately unreasonable dates
Some errors in Opera 9,
e.g. 2117-10-29 ->
The next routine is in include3.js; YMD2YWD uses a different
method and parameters to the above.
NOTE the change of 2007-04-03 in YMD2YWD,
to allow for a problem with the browser Firefox 2.0.0.3.
By YMDtoYWDtest() : Today is 2025-03-11 Tue
so it is ISO 8601 year-week-day 2025-W11-2
YYYY-MM-DD to YYYY-WW-D, in two ways
First Validate. Then save Day-of-Week, get nearest Thursday, save
Time; go to January 4th, subtract from Time, round the Week difference.
YYYY-WW-D to YYYY-MM-DD, Array or Object
Basically, put Year Jan 4th into a date object, getDay(), then
use setDate() to move back 0..6 days to Monday and to add (WW-1)*7 days
for the start of the week.
Week Number without Date Object
Contents moved upwards.
DayCount Routines
A direct routine for CMJD to
Year-Week-Day, and a new direct reverse routine, with CMJD to/from
Year-Month-Day and Year-Month-Day to/from Year-Week-Day using them :-
Note that routines using CMJD are comparatively fast.
Try Above JavaScript ISO Week Number Conversions
Given the straightforward handling of the Week and Day components
in conversions from YWD to ordinary date, it seemed sufficient to test
just over one day per year for over 400 years.
I have some more JavaScript for date
conversions, and a little VBScript.
In unaided DOS..Win98/ME, date calculations cannot reasonably be
done.
My NOWMINUS and ENVICALC are batch-enhancing programs for date/time
work and for integer arithmetic; get Pascal source and executable viaprograms.
NOWMINUS F5 J0 L6 Eisoweekno ; set env. ISOWEEKNO to ISO YYYYWW
NOWMINUS F33 J0 L6 Etaxweekno ; set env. TAXWEEKNO to UK HMRC YYYYWW
For an absolute week count, from BC 4713,
NOWMINUS F9 EDN ; set CJD in env. DN, then
ENVICALC CDN 3 - 7 / B6 EWN ; set Local AbsWkNo in env. WN
Adjust the value of 3 by up to ±3 to get the desired first day of the
week (..., Mon, ...); put N - after / to alter the zero week by N.
In Windows NT, XP, etc., batch, it should be possible to code most or
all common date/time work. In particular,
CMD.EXE FAQ ZIP
#149. But it will often be easier to use WSH with VBS
or JS.
Batch file iso-wkno.bat contains
and tests CMD.EXE code for obtaining y w d and
yyyy-Www-d from yyyy mm dd, for 1900-03-01 to
2100-02-28 inclusive.
My Pascal unit dateprox.pas,
tested by program mjd_date.pas
with included version.pas
(BP7; Delphi 3 at least) can calculate week number
(ISO, Tax & otherwise) from date, by first principles.
Program mjd_date.exe
was used for :-
Page week-cal.txt showing
ISO 8601 weeks, with numbers, for 1970-2030.
Page uktaxcal.txt showing HM
Revenue and Customs Tax weeks, with numbers, for 1970-2030.
Recent Delphi has WeekOfTheYear to give ISO Week Numbers
(NOTE: verified over full 9999 years; wrong within Mondays before 1900).
It is longer than necessary, as I can do the job correctly , better and
slightly faster, in five commented one-line statements. I can do the
reverse in four one-line statements. Routines are now in
dateprox.pas and demonstrated in
mjd_date.pas; versions in unit
programs/dateutys.pas
may be even better.
GAWK code for ISO week number, valid for 1970-2099, independent of
strftime(), is tested in
programs/iso-wkno.awk. Where
different, the result of strftime (which is then expected to be
53) is appended.
"Truncate(date, 'IW') truncates an input date to the nearest
Monday (ISO Week start), Truncate(date, 'YEAR') returns calendar year
start, and '+ 3' means 'plus 3 days'". That amounts to "go to
the nearest Thursday, go to January 4th of that calendar year, go to the
start of that week", which is appropriate.
Revised Perl code contributed by Robert Urban
(2002-08-13),
which I have not been able to test, is in
programs/what-cw.pl.
It uses only basic arithmetic operations. Check it before use.
However, Harry Broomhall wrote (2002-08-18)
:-
There are many time/date-based modules/libraries for Perl,
but, IMHO, the one known as Date::Calc is the most comprehensive,
and the most mature. Quoting this name is generally the standard
response to questions about date-handling in newsgroups/lists.
Date::Calc can be retrieved from CPAN (the major repository of
Perl libraries). The module includes a *lot* of utilities to cover
most requirements for 'normal' civil uses (i.e. it doesn't
include astro requirements, or odd calendars such as Mayan).
A working example (weekno.pl) to get week-numbers from
a date (passed on the command line) is as follows :-
#!/usr/bin/perl -w
use strict;
use Date::Calc qw(Week_of_Year);
my ($wn, $yr);
($wn, $yr) = Week_of_Year($ARGV[0], $ARGV[1], $ARGV[2]);
print "$wn of $yr\n";
The author of Date::Calc clearly recognises ISO 8601 as the
standard to use.
HTML 5 introduced various input control types to handle dates/times.
So far, I have only seen these implemented in Opera 9.64+, where they do
not yet work well.
UK HMRC (HM Revenue and Customs; was HM Inland Revenue)
Tax Weeks are a particular case of Type 3.
See in Calendar Weeks.
These string functions are in include3.js,
take different parameters, and return text.
By TaxTest() : Today is 2025-03-11 Tue ; thus it is :
UK Revenue & Customs: Year 2024 week 49 day 4; Year 2024 month 12.
OddWkNo3(D, 4, 6) : 2024wk49d4; OffWkNo3 : 2025-03-11
Those can easily be generalised for Month 1 and Week 1 starting on
any date of the calendar year; in the code, 134 is just 4×32+6 for Month
4 Day 6.
These start the week count on Monday, Julian BC 4713-01-01, 00:00:00
GMT or local time (which corresponds with
CJD).
Modification for other starts is easy. Subtracting two such numbers
gives what might be called the difference in ISO Week Number.
Check the definitions carefully, and test the implementation well
too. Consider the TZ environment
variable, and determine whether it is
fully used, including the transition
dates. Check what day and what number begin each week, and what happens
around the change in year number.
Any Week Number program whuch may use strftime .. %V for
ISO 8601 Week Number should be tested for 2001-12-30 2001-12-31
2002-01-01 to see whether it shows an erroneous Week 53 for Mon
2001-12-31 only; cf.ISO Week Number
Using DatePart.
Function strftime()
A 1999 man page in
unixhelp
(*) includes ISO
formats %G %g for the year of %V the week number,
%u for Mon=1..Sun=7. Also, it has other versions of week data.
An older GAWK man page (FSF, 1994-11-24) implied that the
corresponding GAWK strftime() followed the ANSI C standard; and
tests on a particular older GAWK show that it includes non-ISO week
numbering. Beware.
2002-07-12 ff.
But Markus Kuhn has written :-
- ANSI C is obsolete. The current C standard is ISO C (1999),
which has %V in strftime() for
ISO 8601 week numbers.
Tests show that my old copy of GAWK (Gnu Awk (gawk) 2.15,
patchlevel 6) does have %V but that it is implemented
incorrectly for many year rollovers; and does not know %G which
should give the proper year. I have code to
fix this.