© J R Stockton, ≥ 2011-09-13

VBScript Date and Time 2.

Links within this site :-

! ! This page expects VBScript ! !

It seems unlikely that code on this page will work in many browsers other than Microsoft Internet Explorer.

Its methods can be used in Windows Script Host and in other Microsoft applications, such as Excel.

VBScript is invoked (with Option Explicit) as the first scripting on this page → ←.

Analogous code, and more, can be found via JavaScript Date and Time.

Weeks

VBScript days are numbered Sun=1 to Sat=7 (by default). ISO 8601 specifies Mon=1 to Sun=7, for which use WeekDay(D, 2).

Day-of-Week

Note that the Day-of-Week can always be determined as (DC+X) mod 7 + Y where DC is any daycount and X in 0..6 and Y in 0,1 depend on the day numberings.

Next Monday

Next Monday is seven days ahead, less 0..6 days depending on the current Day Number.

D = Date  : D = D + 7 - (D+5) mod 7 '' Next Monday
document.write D

Calculating Week Numbers

A US Method

  DatePart("ww", D) ' Weeks Sun..Sat, Week 1 usually partial

Given what follows, I'd check even that rather carefully before using it.

International Standard ISO 8601 Week Numbers

By ISO 8601, every week has seven days, numbered Monday=1 to Sunday=7. The first Thursday of a Gregorian Calendar Year has Week Number 01 of that Year Number, and weeks are numbered 01 to 52 or 53. A week numbering date should be written yyyy-Www-d.

Year Month Day to/from Year Week Day

This firstly, using function YearWeekDay(Y, M, D), gives the correct ISO 8601:2004 Year, Week and Day Numbers of a date, as a string yyyy-Www-d, and secondly, using function YearMonthDay(YWD), converts the (editable) YWD string to a CDate for display :-

,   ,     (YYYY>=1900)
     

To see my code, use View Source. Only basic date functions are used; the result does not depend on what the authors of VBScript think Week Numbers should be.

For a discussion of appropriate algorithms, see Week Calculation, including Week Number to/from DayCount.

First Date for an ISO Week Number

For ISO 8601 YYYY-WW, start at WW-1 weeks after YYYY-Jan-01, then move by ±3 days to the nearest Monday.

  YYYY = 2004 : WW = 44
  D = DateSerial(YYYY, 1, 1+7*(WW-1))
  document.write D + 3 - (D+1) mod 7

ISO Week Number Using DatePart

Testing your VBScript if any :

Note that the corresponding Year Number is also needed; it can differ from that of the Calendar Year, and there seems to be no direct provision for that. So if December and WN=1 then increment the year; and if January and WN≥52 then decrement the year.

I understand that VBScript and WSH share a DLL to provide the script engine; therefore they should behave in the same way.

PowerShell may have an equivalent indicated by Get-Date "12/31/2007" -uFormat %V with the same flaw. It also has Calendar.GetWeekOfYear(), apparently defective.

NEW 2007-03-27

I've discovered today that Microsoft have known some of the following for years! Web page BUG: Format or DatePart Functions Can Return Wrong Week Number for Last Monday in Year Article ID : 200299 / Last Review : June 24, 2004 / Revision : 3.0 refers - but only to the error which occurs three times per 28 years.

Their workaround Function WeekNumber seems unduly long and slow.


P.S. 2007-08-21 : Windows Script 5.7, recently released, has the same behaviour as before.

P.S. 2010-02- : So does Windows Script 5.8 (in WinXP sp3?, Windows 7).

Windows' Wrong Week Information by Egbert Zijlema is also interesting.
Web VBS WN Error

VBS DatePart Week Number errors occur in WSH and in MSIE 4, 6, 7, 8, when using function DatePart(,"ww",,). They have been seen in Windows 95, 98, 2000, XP, Vista, and Windows 7. They have been seen in WSH 5.1, 5.6, and 5.8. One type occurs three times per 28 years, another once per 400 years. Those whose browsers do not have VBScript enabled can see a list below in WSH VBS WN Error.

MS TechNet - DatePart : Retrieving Specific Portions of a Date and Time Value, used to imply that the following source line was intended to, or should, give an ISO 8601 Week Number.

W = DatePart("ww", CDate("Dec 29 2003"), vbMonday, vbFirstFourDays)

If so, DatePart is buggy in systems which I have tried, and in Vista and Windows 7.

MS DatePart Function (Visual Basic) does claim compliance with ISO 8601.

The presence of a discrepancy for those parameters suggests that there may well be errors with other combinations of parameters.

Using that code line,

*** If nothing follows in this box, VBScript is not available. ***

I have VBScript Version 5.8, build 23000 or later.

Below, yyyy-Www-d shows the correct Week Number and WN uses the DatePart of your browser.

The first test should be extended to all 14 types of year, at least; I find one similar error per few years. The second tests only Day 1 of Week 1, for a cycle of year-types.

There are errors for 2003-12-29, 2007-12-31, and 2019-12-30, in IE4, IE6, IE7 & IE8 - intervals 4, 12, 12 years recurring, except across missing Leap Years.

It is reported that Vista and Windows 7 do the same.

Test every day from Day 0 to 2555/12/31 and report on a new page those for which MS DatePart does not give ISO Week Number :   .
My XP sp2 IE6 showed corresponding errors generally repeating every 28 years PLUS an error for 2101-01-02 repeating each 400 years. My XP sp2 & sp3 updated to IE7 and IE8 did the same.
Microsoft Workaround

Function DoFix tests function WeekNumber from "Workarounds" in the MS BUG page, but slightly adapted for VBScript.

NEW:
Test every day from Day 0 to 2456/12/31 and report on a new page any for which MS WeekNumber does not give ISO Week Number :   .
My XP sp2 IE6 showed no errors.

Their workaround code looks, and is, slower than my code.

Time for every day from Day 0 to 2201/12/31 :     P4 3GHz XP sp2 IE6 : ~ 4 s.

Note that WeekNumJRS does a little more.
WSH VBS WN Error

This detects non-Mondays for which WN does not equal that of the day before (cf. in Windows Script Host).

WScript.echo getRuntimeInfo() '' More or less as in source of this page
WScript.echo "   Date      DoW   WN   XWN"
XWN = 52
for CD = #1/1/2000# to #1/1/2200#
  WN = DatePart("ww", CD, vbMonday, vbFirstFourDays)
  WD = WeekDayName(Weekday(CD), true)
  if (WD<>"Mon") and (WN<>XWN) then _
    WScript.echo CD, " ", WD, " ", Right(WN+100, 2), "  ", XWN
  XWN = WN
  Next

With the above being in file.vbs, this shows output copied from a "Command Prompt" screen of WinXP sp2, following command CSCRIPT //nologo file.vbs. :-

VBScript Version 5.6.8820
   Date      DoW   WN   XWN
2003-12-30   Tue   01    53
2008-01-01   Tue   01    53
2019-12-31   Tue   01    53
2031-12-30   Tue   01    53
2036-01-01   Tue   01    53
2047-12-31   Tue   01    53
2059-12-30   Tue   01    53
2064-01-01   Tue   01    53
2075-12-31   Tue   01    53
2087-12-30   Tue   01    53
2092-01-01   Tue   01    53
2101-01-02   Sun   53    52
2104-01-01   Tue   01    53
2115-12-31   Tue   01    53
2127-12-30   Tue   01    53
2132-01-01   Tue   01    53
2143-12-31   Tue   01    53
2155-12-30   Tue   01    53
2160-01-01   Tue   01    53
2171-12-31   Tue   01    53
2183-12-30   Tue   01    53
2188-01-01   Tue   01    53
2199-12-31   Tue   01    53

That revised test now also shows the 2101 error.

There is also WSH VBS test code in Windows Script Host, under "Behaviour in DatePart".

Office VBS WN Error
Sub wn()
MsgBox _
 DatePart("ww", #12/30/2007#, vbMonday, vbFirstFourDays) & " " & _
 DatePart("ww", #12/31/2007#, vbMonday, vbFirstFourDays) & " " & _
 DatePart("ww", #1/1/2008#, vbMonday, vbFirstFourDays)
End Sub

That, as a MS Office 2003 Word Macro, shows 52 53 1 , indicating that the same happens in Word. Likewise in Excel; and, I suppose, also in other parts of Office.

Visual Basic WN Error

I am told : VB6+SP5 on XP Pro+SP2 returns 53 for the code line that you posted in the OP.

Weeks in a Month

With a similar definition,

Weekday Arithmetic

For Weekday differences, taking Weekdays as being Mon-Fri, subtract absolute weekday counts (see source). These should start with an arbitrary early Monday being Weekday 0; 1900-01-01 seems suitable :-

Weekdays Difference is now just the difference in WDC; Weekdays Ahead requires CDW, the inverse function to WDC.

Working Days/Hours

Use a similar approach, allowing for holidays/time.

HM Revenue and Customs Tax Weeks

UK HMRC (HM Revenue and Customs; was HM Inland Revenue) Tax Weeks are based on April 6th being Week 01 Day 1. See in Calendar Weeks. This tests two similar routines, with different types of input and output, and one reformatted output.

Months

There is no need to include a month-length table, or leap-year rules; type CDate can do all that.

Length of Month

If Year and Month are known,  Day(DateSerial(Yr, Mn+1, 0)) .

If a Date variable within the month is known :-

function LoM(ByVal D) '' Length of Month containing Date D
  LoM = 32 - Day(D-Day(D)+32)
  end function

for KK = 1 to 15 : D0 = DateSerial(2004, KK, 17)
  document.write "<br>  ",  D0, "   ", LoM(D0) : next

Is it the Last SomeDay of the Month?

The last SomeDay of a month is invariably seven days before a SomeDay whose day-of-month number is less than 8. Testing for the last Sunday of the current month :-

F = Int(GNow) + 7 : OK = Weekday(F)=1 and Day(F)<8
document.write " Date+7 : ", F, ";  DayOfWeek : ", Weekday(F), _
  ";  DayOfMonth : ", Day(F),  ";  OK : ", Cstr(OK)

For the last-but-one, add 14, etc.; for the third, subtract 14, etc.

To test whether the date is within the N'th week of the month, omit the Weekday test.

Find the Final Tuesday of Next Month

For the final Tuesday of next month :-

document.writeln "New Method : "
W = DateSerial(2009, 12+2, 1) '' Two months ahead of Dec 2009
D = W - 1 - (WeekDay(W)+3) mod 7
document.writeln Day(D), " ", Ucase(MonthName(Month(D),1)), " ", Year(D)
W = DateSerial(Year(GNow), Month(GNow)+2, 1) '' Two months ahead of now
D = W - 1 - (WeekDay(W)+3) mod 7 '' 3=Tue, Back off, answer as a Date
document.write Day(D), " ", Ucase(MonthName(Month(D),1)), " ", Year(D)

Find the N'th SomeDay of an Offset Month

More generally, the following gives the N'th SomeDay (Sun=1 .. Sat=7) of the month A months Away; A can be zero or negative. Note that the last SomeDay of A is the zeroth of A+1.

, , :

Stepping Weekdays Monthly

To step D, the first X-day of a month, add 35 days to D, and if the day-of-month is greater than 7 then go back a week.

To step D, the last X-day of a month, which is the zeroth of the next month, set NM = Month(D) mod 12 + 1, D = D + 35, and if Month(D)<>NM then D = D - 7.

For other days, step the first or last X-day, and offset the result by weeks.

Events don't get scheduled for the 5th X-day of a month.

This Month

Any Valid Month

   
ISO - Multi-
nationalised
Terminology?

To exceed VBScript's limited date range, adjust the year used for calculation by a multiple of 400 into the range 2000-2399.

2011-03-23 ~18:00 GMT: The above calendars work by writing an innerHTML string. I might try doing a year's calendar by DOM methods, perhaps.

Years

Leap Years

Check the Day of Feb 29, which will be Feb 29 or Mar 1. But (Year and 3) = 0 suffices until 2100.

Easter Sunday

Functions for the date of Easter Sunday :-

The Gregorian Easter pattern repeats after 5,700,000 years.

To convert Day-of-March to CDate, use DateSerial(YR, 3, DoM).

Pascha & JRSEaster
         

Subroutine Pascha was largely taken from a News post of the last third of May 2003; partly tested by me. It gives Julian Easter before 1583, otherwise Gregorian. (window.status shows progress, if enabled.)

Function ZEG1885 was adapted from the corresponding JavaScript function; see via The Calendrical Works of Rektor Chr. Zeller : The Day-of-Week and Easter, and seems likely to be fast.

Function BCPEaster was adapted from The Calculation of Easter Sunday after the Book of Common Prayer of the Church of England; it is valid for 1900-2199 only.

Function JRSEaster was adapted from the full routine in The Calculation of Easter Sunday after the Book of Common Prayer of the Church of England.

Function CPnEaster was adapted from an Excel routine in Holidays by Pearson Software Consulting, LLC; it is valid for 1900-2099 only.

There is spreadsheet Easter material in PCW March 2006 pp.152-3, which I may look into; though BCPEaster seems better.

Authority

BCPEaster is fully traceable, and JRSEaster is virtually traceable, through the Book of Common Prayer of the Church of England to the Calendar (New Style) Act (1750 c. 23) (but see Pages Annexed to the Calendar Act about the UK Statute Law Database).

Zeller is known to be reliable.

Pascha is a recognised algorithm.

PCW code

In the March 2006 issue of (UK) Personal Computer World (PCW), pages 152-3, there is code for Easter Sunday said to work in Excel from 1900 to 2058. Tested in this page in MS IE4, IE6 & IE7, and with command-prompt CScript, the code agrees with mine for the full CDate range 0100-9999. In fact, it seems full-range in Excel too.

PCWEaster & JRSEaster
         

window.status shows progress, if enabled.

LastModified

For Last Modified in general, including the transmitted format, see in JavaScript Date and Time 2 : Demonstrations; the HTTP header is fully unambiguous, and uses explicit GMT. JavaScript page Test Page for JavaScript lastModified will LATER show reading the HTTP header by an XMLHttpRequest call.

In some systems, such as my Win98/MSIE4 and WinXP/MSIE6 VBScript, the document.LastModified string is in an alien format, and would be misleading if displayed as a string.

The string gives (as far as I've seen) no indication of the zone of the time; my MSIE4 gave GMT, and an IE6 (SP1) in Indianapolis (UTC-5) gave local time.

For this Web page, document.LastModified represents the time of upload to the server, by the server's clock.

Its possible formats are too various for my previous approach to be tenable.

I now postulate that it is always a string LM that can be read correctly, including the century, by CDate(LM) .

That will fail if a 2-digit year is incorrectly windowed.

It would also fail if LastModified were M/D/Y and preference of D/M/Y was honoured, or vice versa.

I also postulate that it will usually be in Local Time.

To determine your Time Zone Offset, see in VBScript Date and Time 1. To determine my time, which is GMT or GMT+1, see Summer Time.

Testing in Your System

This is from JavaScript, which sets NowOffset :-

and the rest is VBScript :-

Compare that with the date at the head of this page, which is the UK Civil Date of editing the master copy.

I can only properly test the offset from GMT during UK Summer Time.

Basic Test

If this section has given a Script Error or other fault, please send me details.

Viewed locally, without HTTP, the VBScript string in my system is in GMT. The time zone of the string should be considered uncertain.

New Tests

Results shown locally may differ in nature from those shown over the Web. To be continued ... ?

VBScript Date and Time Index, VBScript Date and Time 1,
VBScript General and Maths, with VBS Pages Introduction
JavaScript Date and Time
Home Page
Mail: no HTML
© Dr J R Stockton, near London, UK.
All Rights Reserved.
These pages are tested mainly with MS IE 8 and Firefox 3.0 and W3's Tidy.
This site, , is maintained by me.
Head.