简体   繁体   中英

SQL: get first day of the current year week based

Many suggest how to get first date of the current year (month based) for instance:

SELECT DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)

this will result in:

2021-01-01 00:00:00.000

It should be 2021-01-04. (monday (sunday for US) of the first week)

What I need is similar but week based. The first week may start on 31 of December for instance, so the function should return 31 December, not 1 January.

Any ideas ?

Much better to use an inline table-valued function for this than a scalar function (some details over here ).

I created one that generates the first Monday for the year of any given date, based on the desired results shown in the comment above. If you want something for Sunday, provide sample data / desired results in your question:

CREATE FUNCTION dbo.GetFirstMonday
( 
  @date date
)
RETURNS TABLE
WITH SCHEMABINDING
AS
  RETURN 
  (
    WITH km(km) AS (SELECT CONVERT(date, '20180101')), -- known Monday
      d AS (SELECT FirstOfYear = DATEFROMPARTS(YEAR(@date),1,1)),
      w AS (SELECT FirstMonday = DATEADD(WEEK, DATEDIFF(WEEK, km, FirstOfYear) 
    + CASE WHEN ((@@DATEFIRST + DATEPART(WEEKDAY, FirstOfYear))) IN (6,13)
      THEN 1 ELSE 0 END, km) FROM d CROSS JOIN km)
    SELECT FirstMonday FROM w
  );

Usage:

SET DATEFIRST 5;

DECLARE @src TABLE(InputDate date);

INSERT @src(InputDate) VALUES
('20160608'),('20170505'),('20180405'),
('20190303'),('20200903'),('20210706');

SELECT * FROM @src AS src 
  CROSS APPLY dbo.GetFirstMonday(src.InputDate);

Results (for any DATEFIRST setting):

InputDate   FirstMonday
----------  -----------
2016-06-08  2016-01-04
2017-05-05  2017-01-02
2018-04-05  2018-01-01
2019-03-03  2018-12-31
2020-09-03  2019-12-30
2021-07-06  2021-01-04

Another way to think about it is: if the first Monday of the year is more than 2 days from the first day of the year (eg the year starts on , then take the first Monday before the first of the year, which can be at most 3 days prior. So this function takes the MIN() of the matching weekday from 7-day range from {first of year} - {3 days} => {first of year} + {3 days} - one of those days has to be the Monday you're after (and if your rule isn't three days, it's easy to shift):

CREATE FUNCTION dbo.GetFirstWeekday
(
  @date date,
  @DayName char(6)
)
RETURNS TABLE WITH SCHEMABINDING
AS
  RETURN (WITH n(n) AS (SELECT -3 UNION ALL SELECT n+1 FROM n WHERE n < 3),
    d(d) AS (SELECT DATEADD(DAY, n, DATEFROMPARTS(YEAR(@date),1,1)) FROM n)
    SELECT DayName = @DayName, FirstWeekday = MIN(d) FROM d 
      WHERE DATENAME(WEEKDAY, d) = @DayName
  );

So given:

DECLARE @src TABLE(InputDate date);

INSERT @src(InputDate) VALUES
('20160108'),('20170505'),('20180405'),
('20190303'),('20200403'),('20210506');

SELECT * FROM @src AS src 
  CROSS APPLY dbo.GetFirstWeekday(src.InputDate, 'Monday');

Results:

InputDate   FirstWeekday
----------  ------------
2016-06-08  2016-01-04
2017-05-05  2017-01-02
2018-04-05  2018-01-01
2019-03-03  2018-12-31
2020-09-03  2019-12-30
2021-07-06  2021-01-04

This also doesn't have any ties to @@DATEFIRST but it does have a dependency on @@LANGUAGE being in the English realm.

  • Example db<>fiddle - Sunday's there too, but I was guessing at your rules since you didn't supply any sample data / desired results.

But really, if it's just one Sunday or Monday for each of a handful of years and you already know the rules, why not just create a table, define those rules once, instead of coming up with wacky and overtly flexible syntax?

CREATE TABLE dbo.FirstSundayMondayRules
(
  TheYear int PRIMARY KEY,
  FirstSunday date NOT NULL,
  FirstMonday date NOT NULL
);

-- guessing at your Sunday rules here
INSERT dbo.FirstSundayMondayRules VALUES
(2016, '20160103', '20160104'),
(2019, '20181230', '20181231');

Now you can create a much simpler function:

CREATE FUNCTION dbo.GetFirstWeekday
(
  @Date date,
  @DayName char(6)
)
RETURNS TABLE WITH SCHEMABINDING
AS
  RETURN (SELECT FirstWeekday = CASE @DayName 
      WHEN 'Sunday' THEN FirstSunday 
      WHEN 'Monday' THEN FirstMonday END
    FROM dbo.FirstSundayMondayRules
    WHERE TheYear = DATEPART(YEAR, @Date)
  );

And DATEFIRST has no relevance whatsoever anymore, at least as part of any calculation:

SET DATEFIRST 5;

DECLARE @src TABLE(InputDate date);

INSERT @src(InputDate) VALUES
('20160608'),('20190303');

SELECT 'Sunday', * FROM @src AS src 
  CROSS APPLY dbo.GetFirstWeekday(src.InputDate, 'Sunday');
  
SELECT 'Monday', * FROM @src AS src 
  CROSS APPLY dbo.GetFirstWeekday(src.InputDate, 'Monday');

Results:

(No column name)  InputDate   FirstWeekday
----------------  ----------  ------------
Sunday            2016-06-08  2016-01-03
Sunday            2019-03-03  2018-12-30

(No column name)  InputDate   FirstWeekday
----------------  ----------  ------------
Monday            2016-06-08  2016-01-04
Monday            2019-03-03  2018-12-31

Or better yet, get yourself a Calendar table , and you can just have columns where you hard-code FirstSundayOfYear and FirstMondayOfYear .

so i figured out a solution , but may be there is a more elegant one


-- =============================================
-- Author:      PLANSIS
-- Create date: 2020-11-25
-- Description: first day of the current year week based (monday of the first week)
-- =============================================
  CREATE OR ALTER      FUNCTION [dbo].[A_FN_TI_FirstDayCurrentYearWeek] 
(
    @date date  = null
)
RETURNS  datetime
AS
BEGIN
    set @date = coalesce( @date , getdate() ) 
    
    DECLARE @ResultVar datetime 
    DECLARE @wk int  
    DECLARE @yr int  

    SET @yr = year(@date)
    SET @wk = 1

    SELECT @ResultVar  =  dateadd (week, @wk, dateadd (year, @yr-1900, 0)) - 4 -
         datepart(dw, dateadd (week, @wk, dateadd (year, @yr-1900, 0)) - 4) + 2 -- +2 for europe, +1 for US
    -- Return the result of the function
    RETURN @ResultVar 
END

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM