日期时间差异(不含周末),格式为 hh:mm:ss

问题描述 投票:0回答:1

我正在尝试获取两个 datetime2 字段(不包括周末)之间的差异。

我只能在日期上做到这一点,但很难包含时间:

WITH cte AS (
  SELECT
    start_datetime, end_datetime,
    cast(datediff(dd, start_datetime, end_datetime) - 
      (datediff(wk, start_datetime, end_datetime) * 2) -
      case when datepart(dw, start_datetime) = 1 then 1  else 0 end +
      case when datepart(dw, end_datetime) = 1 then 1  else 0 end
      as varchar(10)) as datediff_,
    DATEDIFF(SECOND, right(start_datetime, 16), right(end_datetime, 16)) timediff_
  from mytable
)
SELECT *, datediff_ + ' days, ' +
  CONVERT(VARCHAR(5), timediff_/60/60)
  + ':' + RIGHT('0' + CONVERT(VARCHAR(2), timediff_/60%60), 2)
  + ':' + RIGHT('0' + CONVERT(VARCHAR(2), timediff_ % 60), 2) durationwoweekends
FROM cte;

timediff_ 为正时,这确实计算并返回良好,但当为负时,我得到一个错误的输出:

我的目标是durationwoweekends列的输出格式是days, hh:mm:ss现在输出一个varchar,这可能是错误的,并且还强制字符串“days,”,但最终需要该字段为持续时间数据类型。

测试数据:

CREATE TABLE #temptable(
start_datetime datetime2,
end_datetime datetime2
)
Insert into #temptable(start_datetime, end_datetime) values ('2022-10-31 10:30:52', '2022-11-02 23:47:55')
Insert into #temptable(start_datetime, end_datetime) values ('2022-11-01 08:30:46', '2022-11-03 02:59:27')

select * from #temptable

我正在使用 SQL Server 2019。 任何人的任何帮助将不胜感激。

sql sql-server datetime datediff
1个回答
0
投票

忽略(或调整)周末日期,但同时不调整时间的情况是不常见的。对于以下情况,这可能会导致奇怪的结果:

  1. “周六1600”到“周日08:00”按“0天-8小时”计算。
  2. “星期六 0900”到“星期一 16:00”被计算为比“星期日 0800”到“星期一 16:00”的排序间隔。

如果忽略周末,我相信你应该完全忽略它们,包括任何时间组成部分。这可以通过将周六 00:00 到周日 24:00 之间的所有日期时间映射为相当于周一 00:00 来完成。这将使计算结果连续且一致。实际上,已用计时器从周六 00:00 到周日 24:00(周一 00:00)暂停。

至于计算显示的日期和时间分量,我建议:

  1. 如上所述调整开始和结束日期时间,以将任何周末值映射到星期一 00:00。
  2. 计算单个经过时间值(以秒为单位)。
  3. 如果为负,则分离出符号并继续求绝对值。
  4. 将经过的秒数分成日期和时间部分。
  5. 调整日值以考虑跳过的周末。
  6. 合并结果并设置格式以供显示。

以下内容将实现此目的:

--SET DATEFIRST 7 -- US
--SET DATEFIRST 1 -- Mostly everywhere else
SET DATEFIRST 6 -- Maybe

-- The weekday value from DATEPART(weekday) is locale/settings dependent
-- So we need to calculate the expected values
--   For @@DateFirst = 7, @SaturdayDW = 7 and @SundayDW = 1
--   For @@DateFirst = 1, @SaturdayDW = 6 and @SundayDW = 7
--   For @@DateFirst = 6, @SaturdayDW = 1 and  @SundayDW = 2
-- DATEDIFF(week, ...) is unaffected by the DateFirst setting
DECLARE @SaturdayDW INT = DATEPART(weekday, '20000101') -- Reference Saturday
DECLARE @SundayDW INT = DATEPART(weekday, '20000102') -- Reference Sunday
--SELECT @@DateFirst AS DateFirst, @SaturdayDW AS SaturdayDW, @SundayDW SundayDW
  
SELECT
    DATEPART(dw, T.StartDateTime) AS dw,
    DATEPART(dw, T.EndDateTime) AS edw,
    DATEDIFF(week, T.StartDateTime, T.EndDateTime) AS DiffWeek,
    T.TestSet, T.StartDateTime, T.EndDateTime,
    LEFT(DATENAME(dw, T.StartDateTime), 3) AS SDay,
    LEFT(DATENAME(dw, T.EndDateTime), 3) AS EDay,
    FMT.AdjustedStart,
    FMT.AdjustedEnd,
    FMT.Elapsed
FROM TestData T
CROSS APPLY (
    -- Adjust weekend dates forward to 00:00:00 of the following Monday
    SELECT
        CASE DATEPART(dw, T.StartDateTime)
            WHEN @SaturdayDW
                THEN CONVERT(DATETIME, DATEADD(day, 2, CONVERT(DATE, T.StartDateTime)))
            WHEN @SundayDW
                THEN CONVERT(DATETIME, DATEADD(day, 1, CONVERT(DATE, T.StartDateTime)))
            ELSE T.StartDateTime
            END AS AdjStart,
        CASE DATEPART(dw, T.EndDateTime)
            WHEN @SaturdayDW
                THEN CONVERT(DATETIME, DATEADD(day, 2, CONVERT(DATE, T.EndDateTime)))
            WHEN @SundayDW
                THEN CONVERT(DATETIME, DATEADD(day, 1, CONVERT(DATE, T.EndDateTime)))
            ELSE T.EndDateTime
            END AS AdjEnd
) A
CROSS APPLY (
    -- Calculate raw differences
    SELECT
        DATEDIFF(second, AdjStart, AdjEnd) AS Seconds,
        DATEDIFF(week, AdjStart, AdjEnd) AS Weeks
) ES
CROSS APPLY (
    -- Calculate and adjust for days, calculate separate time, handle negatives
    SELECT
        CASE WHEN ES.Seconds < 0 THEN '-' ELSE '' END AS Sign,
        ABS(ES.Seconds) / 86400 - 2 * ABS(Weeks) AS Days,
        DATEADD(second, ABS(ES.Seconds) % 86400, 0) AS Time
) EP
CROSS APPLY (
    -- Format intermediate data and results
    SELECT
       CONCAT(
           EP.Sign,
           EP.Days,
           CASE WHEN EP.Days = 1 THEN ' Day, ' ELSE  ' Days, ' END,
           CONVERT(CHAR(8), EP.Time, 108)
           ) AS Elapsed,
       CONCAT(
           LEFT(DATENAME(dw, A.AdjStart), 3),
           ', ',
           CONVERT(CHAR(8), A.AdjStart, 108)
           ) AS AdjustedStart,
       CONCAT(
           LEFT(DATENAME(dw, A.AdjEnd), 3),
           ', ',
           CONVERT(CHAR(8), A.AdjEnd, 108)
           ) AS AdjustedEnd
) FMT
ORDER BY T.TestSet, T.StartDateTime, T.EndDateTime
上面使用

CROSS APPLY
可以方便地封装中间计算,避免子表达式的重复,并减少最终选择列表中的混乱。

DATEDIFF(second, ...)
产生的整数结果有效期长达约 38 年。如果需要更长的间隔,可以使用
DATEDIFF_BIG()

结果:

测试
设置
开始
日期/时间
结束
日期/时间
纪念日 电子日 调整开始 调整结束 已过去
1 2022-10-31 10:30:52 2022-10-31 10:30:52 周一 周一 周一,10:30:52 周一,10:30:52 0 天,00:00:00
1 2022-10-31 10:30:52 2022-11-02 23:47:55 周一 周三 周一,10:30:52 周三,23:47:55 2 天,13:17:03
1 2022-11-02 23:47:55 2022-10-31 10:30:52 周三 周一 周三,23:47:55 周一,10:30:52 -2 天,13:17:03
2 2022-11-01 08:30:46 2022-11-03 02:59:27 周二 周四 周二,08:30:46 周四,02:59:27 1 天,18:28:41
2 2022-11-03 02:59:27 2022-11-01 08:30:46 周四 周二 周四,02:59:27 周二,08:30:46 -1 天,18:28:41
...
3 2023-12-01 08:00:00 2023-12-01 16:45:00 周五 周五 周五,08:00:00 周五 16:45:00 0 天,08:45:00
3 2023-12-01 08:00:00 2023-12-02 08:00:00 周五 周六 周五,08:00:00 周一,00:00:00 0 天,16:00:00
3 2023-12-01 08:00:00 2023-12-03 16:45:00 周五 太阳 周五,08:00:00 周一,00:00:00 0 天,16:00:00
3 2023-12-01 08:00:00 2023-12-04 16:45:00 周五 周一 周五,08:00:00 周一,16:45:00 1 天,08:45:00
...
3 2023-12-02 08:00:00 2023-12-01 16:45:00 周六 周五 周一,00:00:00 周五 16:45:00 -0 天,07:15:00
3 2023-12-02 08:00:00 2023-12-03 16:45:00 周六 太阳 周一,00:00:00 周一,00:00:00 0 天,00:00:00
3 2023-12-02 08:00:00 2023-12-04 08:00:00 周六 周一 周一,00:00:00 周一,08:00:00 0 天,08:00:00
3 2023-12-02 08:00:00 2023-12-04 16:45:00 周六 周一 周一,00:00:00 周一,16:45:00 0 天,16:45:00
3 2023-12-02 08:00:00 2023-12-3 75e0 1 23:59:59 周六 太阳 周一,00:00:00 周一,00:00:00 20 天,00:00:00
...
4 2023-12-01 10:00:05 2023-12-11 14:30:25 周五 周一 周五,10:00:05 周一,14:30:25 6 天,04:30:20

请参阅 this db<>fiddle 以获取包含各种测试数据的演示。

© www.soinside.com 2019 - 2024. All rights reserved.