且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

T-SQL |计算日期之间的小时数,但忽略周末的小时数,仅计算8-18小时

更新时间:2023-01-30 10:58:43

我从头开始研究了一些东西,似乎可以满足您的所有需求,

I worked out something from scratch, and it seems to cover all your needs, though you can update us back if something's missing.

考虑到这是一个全新的起点,并且从不同的角度出发,您可能会发现其中的某些技术或想法。另外,对我来说似乎更简单,但这也许是因为我正在审阅自己的作品...

Considering it's a fresh start and coming from a different angle, you might discover certain techniques or ideas out of it. Also, it does seem simpler to me but maybe that's because I'm reviewing my own work...

最后一点,我将依靠一种技巧之前阅读过,以逐行方式应用 MIN MAX ,抽象示例:

One last note, I'll be relying on a trick I read before, that applies MIN and MAX in a row-wise fashion, abstract example:

SELECT MAX([value]) AS [MAX], MIN([value]) AS [MIN]
FROM (
VALUES (CURRENT_TIMESTAMP), (@Start_Date), (@End_Date), (NULL), (0)
) AS [data]([value])

首先,想一想开始和结束时间的长短。结束日期:

First off, thought of figuring the amount of time outside start & end days:

SELECT MinutesExcludingStartAndEndDays = MAX([value])
FROM (VALUES (0), ((DATEDIFF(DAY, @Start_Date, @End_Date) - 1) * 840)) AS [data]([value])

第二步,计算开始时间相对于晚上8点的时间(如果两天都匹配,则为或结束时间):

Second, figuring the time during starting day, against 8pm (or end time if both days match):

SELECT MinutesOnStartDay = DATEDIFF(MINUTE, @Start_Date, MIN([value]))
FROM (VALUES (@End_Date), (DATETIMEFROMPARTS(YEAR(@Start_Date), MONTH(@Start_Date), DAY(@Start_Date), 20, 0, 0, 0))) AS [data]([value])

第三与第二非常相似,但是请注意,如果开始日期和结束日期相同,则我们不应同时计算第二和第三天。我决定在第三行中使用 CASE 语句来处理该问题:

Third is very similar to second, however note that if start and end days were the same, we should not count both second and third. I decided to handle that with a CASE statement within third:

SELECT MinutesOnEndDayIfNotStartDay = CASE DATEDIFF(DAY, @Start_Date, @End_Date) WHEN 0 THEN 0 ELSE DATEDIFF(MINUTE, MAX([value]), @End_Date) END
FROM (VALUES (@Start_Date), (DATETIMEFROMPARTS(YEAR(@End_Date), MONTH(@End_Date), DAY(@End_Date), 6, 0, 0, 0))) AS [data]([value])

第四,如果开始日期或结束日期是在周末,则应将其从那里移开:

Fourth, if either start or end dates land on a weekend, it should be pushed away from there:

DECLARE @Mod int

SET @Mod = CONVERT(int, @Start_Date) % 7
IF @Mod IN (5, 6)
    SET @Start_Date = DATEADD(DAY, CASE @Mod WHEN 5 THEN 2 WHEN 6 THEN 1 ELSE 0/0 END, DATETIMEFROMPARTS(YEAR(@Start_Date), MONTH(@Start_Date), DAY(@Start_Date), 6, 0, 0, 0))

SET @Mod = CONVERT(int, @End_Date) % 7
IF @Mod IN (5, 6)
    SET @End_Date = DATEADD(DAY, CASE @Mod WHEN 5 THEN -1 WHEN 6 THEN -2 ELSE 0/0 END, DATETIMEFROMPARTS(YEAR(@End_Date), MONTH(@End_Date), DAY(@End_Date), 20, 0, 0, 0))

最后,在目标时期内完全包含周末的问题,为此请查看这个问题,从选票中我只能猜到他们已经解决了。

Lastly, the issue of having weekend days fully encompassed within your target period, for that have a look at this question, from the votes there I can only guess they worked it out already.