The biggest problem I had here was working out how many days there are in each month. I struggled to find the rules specific to Nepal, so in the end I used a table of known dates (taken from this
website[
^]). If you know the rules then replace the hard-coded values below with the appropriate calculations.
Note that I've used an identity column starting at 2072 to generate the year number - just laziness on my part!
if exists (select * from INFORMATION_SCHEMA.TABLES where TABLE_NAME = 'DaysInMonth')
drop table DaysInMonth;
Create table DaysInMonth
(
[NYear] int IDENTITY(2072,1),
[1] int,
[2] int,
[3] int,
[4] int,
[5] int,
[6] int,
[7] int,
[8] int,
[9] int,
[10] int,
[11] int,
[12] int
)
insert into DaysInMonth values
(31,32,31,32,31,30,30,29,30,29,30,30)
,(31,32,31,32,31,30,30,29,30,29,30,30)
,(31,32,31,32,31,30,30,30,29,29,30,31)
,(31,31,31,32,31,31,30,29,30,29,30,30)
,(31,31,32,31,31,31,30,29,30,29,30,30)
,(31,32,31,32,31,30,30,30,29,29,30,30)
,(31,32,31,32,31,30,30,30,29,30,29,31)
,(31,31,31,32,31,31,30,29,30,29,30,30)
,(31,31,32,31,31,31,30,29,30,29,30,30)
,(31,32,31,32,31,30,30,30,29,29,30,30)
,(31,31,32,32,31,30,30,30,29,30,30,30)
,(30,32,31,32,31,30,30,30,29,30,30,30)
,(31,31,32,31,31,30,30,30,30,29,30,30)
,(31,31,32,31,31,30,30,30,29,30,30,30)
,(31,32,31,32,30,31,30,30,29,30,30,30)
,(30,32,31,32,31,30,30,30,29,30,30,30)
,(31,31,32,31,31,31,30,30,29,30,30,30)
,(30,31,32,32,30,31,30,30,29,30,30,30)
,(30,32,31,32,31,30,30,30,29,30,30,30)
,(30,32,31,32,31,30,30,30,29,30,30,30)
I used a cut-down version of your table to generate my calendar - mainly because I wasn't sure what you were trying to do with some of the fields, and also because of things like - I don't know which rules you follow for "working days". My table looks like this ...
if exists (select * from INFORMATION_SCHEMA.TABLES where TABLE_NAME = 'dim_time')
drop table [dim_time];
GO
CREATE TABLE [dbo].[dim_time](
[calrownum] int IDENTITY(1,1)
,[YearLabel] int
,[QtrLabel] [nvarchar](20) NULL
,[MonthLabel] int
,[WeekLabel] int NULL
,[DayLabel] int
,[DayofWeek] [nvarchar](20) NULL
,[DayNumber] [int] NULL
,[EnglishDate] date NULL
) ON [PRIMARY]
The column
EnglishDate
was mainly for my own benefit so I could easily look things up when I was trying to find out how the days in the month were determined, but it is handy for determining the Day of the week as well.
The column
calrownum
isn't really necessary but sometimes
select * from dim_time
does not return the values in the expected order, so I use
select * from dim_time order by calrownum
to ensure that the dates are in the correct order. It's less hassle than typing
order by YearLabel, MonthLabel,DayLabel
It now just becomes an exercise of generating a row for each day of each month based on the data in
DaysInMonth
...Note I do not use a cursor to iterate through that table but a
WHILE
loop instead. Also note that I've just hard-coded some values and you may want to change them.
declare @sql nvarchar(max)
declare @iteratorMonth int
declare @iteratorYear int
declare @iteratorDay int
DECLARE @ParmDefinition nvarchar(500);
set @iteratorYear = 2072
WHILE @iteratorYear <= 2091
BEGIN
set @iteratorMonth = 1
set @iteratorDay = 0
WHILE @iteratorMonth <= 12
BEGIN
set @sql = 'SELECT @retvalOUT = [' + CAST(@iteratorMonth as varchar(2)) + '] from DaysInMonth where Nyear=' + CAST(@iteratorYear as varchar(4))
SET @ParmDefinition = N'@retvalOUT int OUTPUT';
declare @retVal int
EXEC sp_executesql @sql, @ParmDefinition, @retvalOUT=@retval OUTPUT;
WITH q AS
(
SELECT 1 AS datum
UNION ALL
SELECT datum + 1
FROM q
WHERE datum < @retVal
)
INSERT INTO [dim_time] SELECT @iteratorYear, CEILING(CAST(@iteratorMonth AS decimal(4,2)) / 3), @iteratorMonth, NULL, datum, NULL, @iteratorDay + datum, NULL
FROM q
SET @iteratorMonth = @iteratorMonth + 1
SET @iteratorDay = @iteratorDay + @retVal
END
set @iteratorYear = @iteratorYear + 1
END;
Now I revisit the table to add the English date equivalent and the Day of the Week (in English, sorry). Note that this is based on 14-Apr-2015 being equal to 1 Baisakh 2072. I.e. I do not calculate the date using a formula, I just add the appropriate number of days onto a known date.
WITH CTE AS
(
SELECT
rownum = ROW_NUMBER() OVER (ORDER BY YearLabel, MonthLabel, DayLabel), YearLabel, MonthLabel, DayLabel
FROM [dim_time]
)
UPDATE G SET EnglishDate = DATEADD(dd, rownum, '13-APR-2015'), [DayofWeek] = DATENAME(dw, DATEADD(dd, rownum, '13-APR-2015'))
FROM [dim_time] G
LEFT JOIN CTE CTE ON CTE.YearLabel = G.YearLabel AND CTE.MonthLabel = G.MonthLabel AND CTE.DayLabel = G.DayLabel
The struck out code is what I used before I added the
calrownum
column. With that column in place all I need is
UPDATE dim_time set EnglishDate = DATEADD(dd, calrownum, '13-APR-2015'), [DayofWeek] = DATENAME(dw, DATEADD(dd, calrownum, '13-APR-2015'))
Before calculating the Week number I set the first day of the week to Sunday. This is the default but I've deliberately set it to highlight that the week starts on a Sunday in Nepal
and to override anything else that might have happened earlier in the session.
set DATEFIRST 7
The calculation for the Week Number was taken from
http://en.wikipedia.org/wiki/ISO_week_date[
^]. You may need to change this depending on which week numbering system you use - for example using this method, in 2072 Chaitra 28,29,30 are week 1 (of 2073) rather than week 53.
Update [dim_time] set WeekLabel = CASE
WHEN (DayNumber - DATEPART(dw, EnglishDate) + 10) / 7 < 1 THEN 52
WHEN (DayNumber - DATEPART(dw, EnglishDate) + 10) / 7 > 52 THEN 1
ELSE (DayNumber - DATEPART(dw, EnglishDate) + 10) / 7 END
My table now contains every date between 1 Baisakh 2072 and 30 Chaitra 2091 (UK 2015-04-14 to 2035-04-13) inclusive.
I used the following query to check I had the correct number of days in each month
WITH q AS(
SELECT *
FROM (
SELECT
YearLabel, MonthLabel, COUNT(DayLabel) AS Days from dim_time GROUP BY YearLabel, MonthLabel
) as s
PIVOT
(
MAX(Days)
FOR MonthLabel IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])
)AS p)
select D.NYear,
[1] = CASE WHEN q.[1]=D.[1] THEN 'True' ELSE 'False' END,
[2] = CASE WHEN q.[2]=D.[2] THEN 'True' ELSE 'False' END,
[3] = CASE WHEN q.[3]=D.[3] THEN 'True' ELSE 'False' END,
[4] = CASE WHEN q.[4]=D.[4] THEN 'True' ELSE 'False' END,
[5] = CASE WHEN q.[5]=D.[5] THEN 'True' ELSE 'False' END,
[6] = CASE WHEN q.[6]=D.[6] THEN 'True' ELSE 'False' END,
[7] = CASE WHEN q.[7]=D.[7] THEN 'True' ELSE 'False' END,
[8] = CASE WHEN q.[8]=D.[8] THEN 'True' ELSE 'False' END,
[9] = CASE WHEN q.[9]=D.[9] THEN 'True' ELSE 'False' END,
[10] = CASE WHEN q.[10]=D.[10] THEN 'True' ELSE 'False' END,
[11] = CASE WHEN q.[11]=D.[11] THEN 'True' ELSE 'False' END,
[12] = CASE WHEN q.[12]=D.[12] THEN 'True' ELSE 'False' END
from DaysInMonth D
LEFT OUTER JOIN q ON q.YearLabel = D.NYear
and used spot checking on a few dates to confirm the other details