Home » Split Time into Weekly Blocks

Split Time into Weekly Blocks

Calculate Start and Finish date times along-with duration in hours for all weeks. A week starts on 6 AM of every Monday. The dates are in MDY format. So basically, you will need to split the rows into weeks where week starts on 6AM and finishes on 6AM after 7 days.

📌 Challenge Details and Links
ExcelBI Power Query Challenge Number: 123
Challenge Difficulty: ⭐️⭐️⭐️⭐️⭐️⭐️
📥Download Sample File
📥Link to the solutions on LinkedIn

Solving the challenge of Split Time into Weekly Blocks with Power Query

Power Query solution 1 for Split Time into Weekly Blocks, proposed by Zoran Milokanović:
let
  Source = Excel.CurrentWorkbook(){[Name = "Input"]}[Content], 
  T = each DateTime.From(Date.From(_)), 
  U = Duration.From, 
  S = Table.Combine(
    Table.TransformRows(
      Source, 
      each 
        let
          s = [Start], 
          f = [Finish], 
          m = [Machine], 
          l = {s}
            & List.Select(
              List.DateTimes(T(s) + U(.25) + U(1), Duration.Days(T(f) - T(s)), U(1)), 
              (d) => d < f and Date.DayOfWeek(d) = 0
            )
            & {f}
        in
          Table.FromRows(
            List.Transform(
              List.Positions(List.RemoveLastN(l)), 
              each {m, l{_}, l{_ + 1}, Duration.TotalHours(l{_ + 1} - l{_})}
            ), 
            let
              c = Table.ColumnNames(Source)
            in
              {c{2}, c{0}, c{1}} & {"Duration"}
          )
    )
  )
in
  S
Power Query solution 2 for Split Time into Weekly Blocks, proposed by Kris Jaganah:
let
 Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
 Expand = Table.ExpandListColumn(Table.AddColumn(Source, "Days", each List.Numbers(Date.Day([Start]), Date.Day([Finish])-Date.Day([Start])+1)), "Days"),
 Start = Table.AddColumn(Expand, "Date", each let 
 a = hashtag#date(Date.Year([Start]),Date.Month([Start]),[Days]),
 b = if Date.From([Start]) = a then [Start] else if Date.From([Finish]) = a then [Finish] else a ,
 c = if Date.DayOfWeek(b) = 0 then hashtag#datetime(Date.Year(b),Date.Month(b),Date.Day(b),6,0,0) else b in c),
 Filter = Table.SelectRows(Start, each (Number.Mod( Number.From([Date]),1)>0)),
 Index0 = Table.AddIndexColumn(Filter,"Idx0",0),
 Index1 = Table.AddIndexColumn(Index0,"Idx1",1),
 Merge = Table.NestedJoin(Index1, {"Machine", "Idx1"}, Index1, {"Machine", "Idx0"}, "Expanded Idx1", JoinKind.LeftOuter),
 Remove = Table.SelectColumns(Merge,{"Machine", "Date", "Expanded Idx1"}),
 Xpand = Table.ExpandTableColumn(Remove, "Expanded Idx1", {"Date"}, {"Finish"}),
 Filter1 = Table.SelectRows(Xpand, each ([Finish] <> null)),
 Rename = Table.RenameColumns(Filter1,{{"Date", "Start"}}),
 Duration = Table.AddColumn(Rename, "Duration", each Number.Round( Number.From(([Finish]-[Start])*24),2))
in
 Duration
 
                    
                  
          
Power Query solution 3 for Split Time into Weekly Blocks, proposed by Rick de Groot:
let
 Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("TYxBCoAwDAS/IjkLTXYrtPmDLyg9iAh68f9HC4LxOsNMa6KWtCQoOCkdlFls+ZDR8zLQuu3ndR8mfW4CDQ9HHR6MS3VaJHiTHEl1w/D8XYrnEgml9wc=", BinaryEncoding.Base64), Compression.Deflate)), {"Start", "Finish", "Machine"} ),
 ChType = Table.TransformColumnTypes(Source, {{"Start", type datetime}, {"Finish", type datetime}}),
 Times = Table.AddColumn(ChType, "Times", (x)=> 
 [a = List.Generate( 
 ()=> [ Start = x[Start], Finish = List.Min({x[Finish], Date.EndOfWeek(x[Start], 1) + hashtag#duration(0,6,0,1)} )] ,
 each Date.From( [Start]) <> Date.From( x[Finish] ),
 each [ Start = [Finish], 
 Finish = let MyDuration = List.Min( { Duration.Days( x[Finish] - [Finish] ), 7  })  in 
 if MyDuration <> 7 then x[Finish] else Date.AddDays( [Finish], 7 )
 ] ) ,
 b = Table.FromRecords(a)][b] ),
 SelCols = Times[[Machine],[Times]],
 ExpTable = Table.ExpandTableColumn(SelCols, "Times", {"Start", "Finish"}),
 Duration = Table.AddColumn(ExpTable, "Duration", each Number.Round( Duration.TotalHours( [Finish] - [Start] ), 2 ) ) 
in
 Duration
                    
                  
          
Power Query solution 4 for Split Time into Weekly Blocks, proposed by Alejandro Simón 🇵🇦 🇪🇸:
let
  Source = Excel.CurrentWorkbook(){[Name = "Table1"]}[Content], 
  Machines = Table.Group(
    Source, 
    {"Machine"}, 
    {
      {
        "All", 
        each 
          let
            a = _, 
            b = Number.From(Date.From([Start]{0})), 
            c = Number.From(Date.From([Finish]{0})), 
            d = List.Transform({b .. c}, each DateTime.From(_ + .25)), 
            e = {[Start]{0}} & List.Select(d, each Date.DayOfWeek(_) = 1), 
            f = List.RemoveFirstN(e) & {[Finish]{0}}, 
            g = List.Zip({e, f}), 
            h = List.Transform(g, each Duration.TotalHours(_{1} - _{0})), 
            i = Table.FromColumns({e, f, h}, List.RemoveLastN(Table.ColumnNames(a)) & {"Duration"})
          in
            i
      }
    }
  ), 
  Sol = Table.ExpandTableColumn(Machines, "All", Table.ColumnNames(Machines[All]{0}))
in
  Sol
Power Query solution 5 for Split Time into Weekly Blocks, proposed by Luan Rodrigues:
let
  Fonte = Tabela1, 
  add = Table.AddColumn(
    Fonte, 
    "Personalizar", 
    each [
      a = {Number.From(Date.From([Start])) .. Number.From(Date.From([Finish]))}, 
      b = List.Transform(a, each {_} & {Date.WeekOfMonth(Date.From(_), 2)}), 
      c = List.Distinct(List.Transform(b, (x) => List.Select(b, each _{1} = x{1}))), 
      d = Table.Combine(
        List.Transform(
          c, 
          (x) =>
            [
              o = List.Transform(x, each _{0}), 
              p = DateTime.From(Text.From(Date.From(List.Min(o) - 1)) & " 06:00"), 
              q = DateTime.From(Text.From(Date.From(List.Max(o))) & " 06:00"), 
              r = Table.FromRows({{p} & {q}})
            ][r]
        )
      )
    ][d]
  ), 
  exp = Table.ExpandTableColumn(add, "Personalizar", Table.ColumnNames(add[Personalizar]{0})), 
  rec = Table.AddColumn(
    exp, 
    "Personalizar", 
    each [
      s = 
        if Number.From(Date.From([Start])) = Number.From(Date.From([Column1])) + 1 then
          [Start]
        else
          [Column1], 
      f = if Date.From([Finish]) = Date.From([Column2]) then [Finish] else [Column2], 
      Duration = Duration.TotalHours(DateTime.From(f) - DateTime.From(s))
    ]
  )[[Machine], [Personalizar]], 
  res = Table.ExpandRecordColumn(rec, "Personalizar", Record.FieldNames(rec[Personalizar]{0}))
in
  res
Power Query solution 6 for Split Time into Weekly Blocks, proposed by Eric Laforce:
let
 Source = Excel.CurrentWorkbook(){[Name="tData123"]}[Content],
 Transform = Table.TransformRows(Source, each let
 _R = _,
 _L = List.Generate( ()=>[s=_R[Start], r=[]], each [s]<=_R[Finish],
 each let 
 _NextW = DateTime.From(DateTime.Date([s])) + hashtag#duration(7 - Date.DayOfWeek([s], Day.Monday),6,0,0),
 _NextS = if (_NextW < _R[Finish]) then _NextW else _R[Finish],
 _D = Duration.TotalHours(_NextS-[s]),
 _NewR = [Machine=_R[Machine], Start=[s], Finish=_NextS, Duration=_D]
 in [s=if ([s]<>_R[Finish]) then _NextS else _R[Finish]+hashtag#duration(0,0,0,1), r=_NewR],
 each [r] )
 in Table.FromRecords(List.Skip(_L)) ),
 Combine = Table.Combine(Transform)
in 
 Combine
                    
                  
          
Power Query solution 7 for Split Time into Weekly Blocks, proposed by Luke Jarych:
let 
 FirstDayOfMonth = hashtag#datetime(Date.Year(start), Date.Month(start), 1, 6, 0, 0), 
 WeeksToAdd = Number.RoundDown(Duration.From(start - FirstDayOfMonth) / hashtag#duration(7, 0, 0, 0)) +1,
 WeeksToAddNot0 = if WeeksToAdd = 0 then 1 else WeeksToAdd,
 NextWeekStart = DateTime.From(FirstDayOfMonth - hashtag#duration(1, 0, 0, 0) + hashtag#duration(WeeksToAddNot0 * 7, 0, 0, 0)),
 EndBiggerThanBoundaryWeek = NextWeekStart < end, 
 WeekRecords = List.Generate(
 () => [WeekNum = 1, Machine = machine, Start = start, Finish = if EndBiggerThanBoundaryWeek then NextWeekStart else DateTime.From(Date.From(start) + hashtag#duration(WeekNum * 6, 0, 0, 0)) + hashtag#duration(0, 6, 0, 0)],
 each [Start] < end,
 each if [Finish] + hashtag#duration(7, 0, 0, 0) < DateTime.From(end) then
 [Machine = machine, WeekNum = [WeekNum] + 1, Start = [Finish], Finish = [Finish] + hashtag#duration(7, 0, 0, 0)]
 else
 [Machine = machine, WeekNum = [WeekNum] + 1, Start = [Finish], Finish = DateTime.From(end)]
 ) in WeekRecords,
                    
                  
          
Power Query solution 9 for Split Time into Weekly Blocks, proposed by Obi E, MPH:
let Source = Excel.CurrentWorkbook(){[Name="Table4"]}[Content], #"Inse - Pastebin.com
          Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.

Solving the challenge of Split Time into Weekly Blocks with Excel

Excel solution 1 for Split Time into Weekly Blocks, proposed by Bo Rydobon 🇹🇭:
=REDUCE(HSTACK(G1,E1:F1,"Duration"),G2:G4,LAMBDA(a,v,LET(n,ROWS(G2:v),s,INDEX(E2:E4,n),f,INDEX(F2:F4,n),b,9/4,c,INT((s-b)/7),
w,SEQUENCE(INT((f+7-b)/7)-c,,c*7+b,7),d,IF(w>s,w,s),e,IF(w+7>f,f,w+7),
VSTACK(a,HSTACK(IF(w,v),d,e,(e-d)*24)))))
Excel solution 2 for Split Time into Weekly Blocks, proposed by محمد حلمي:
=REDUCE(HSTACK(C1,A1:B1,"Duration"),A2:A4,
LAMBDA(a,d,LET(
r,OFFSET(d,,1),
w,OFFSET(d,,2),
x,SEQUENCE(r-d+1,,INT(d)),
v,FILTER(x,WEEKDAY(x)=2)+1/4,
i,VSTACK(d,v),m,VSTACK(v,r),
IFNA(VSTACK(a,HSTACK(w,i,m,24*(m-i))),w))))
Excel solution 3 for Split Time into Weekly Blocks, proposed by 🇰🇷 Taeyong Shin:
=LET(
 s, A2:A4, f, B2:B4,
 w, WORKDAY.INTL(N(s) - 1, SEQUENCE(6), "0111111") + TIME(6, , ),
 wt, FILTER(w, w <= MAX(f)),
 sc, SORT(VSTACK(s, wt)), fc, SORT(VSTACK(f, wt)),
 HSTACK(XLOOKUP(fc, s, C2:C4, , -1), sc, fc, (fc - sc) * 24)
)
Excel solution 4 for Split Time into Weekly Blocks, proposed by Kris Jaganah:
=LET(p,E2:E4,q,F2:F4,r,G2:G4,s,--TEXTSPLIT(TEXTJOIN("#",,MAP(p,q,LAMBDA(x,y,LET(a,INT(x),b,VSTACK(x,SEQUENCE(y-a-1,,a+1),y),c,IF(WEEKDAY(b)=2,b+0.25,b),d,VSTACK(@c,DROP(c,-1)),e,SCAN(1,--(WEEKDAY(d)=2),LAMBDA(x,y,x+y)),f,UNIQUE(e),g,MAP(f,LAMBDA(z,ROUND(SUM((e=z)*(c-d)*24),2))),TEXTJOIN("#",,XLOOKUP(f,e,d)&"-"&XLOOKUP(f,e,c,,,-1)&"-"&g))))),"-","#"),VSTACK(HSTACK(G1,E1:F1,"Duration"),HSTACK(XLOOKUP(CHOOSECOLS(s,2),p,r,,-1),s)))

Solving the challenge of Split Time into Weekly Blocks with Python in Excel

Python in Excel solution 1 for Split Time into Weekly Blocks, proposed by 🇰🇷 Taeyong Shin:
df = xl("A1:C4", headers=True)
s = df["Start"][0].date()
e = df["Finish"].max().date()
week_mon = pd.date_range(s, e, freq="W-Mon") + pd.DateOffset(hours=6)
start_df = (
 pd.concat([week_mon.to_series(), df["Start"]])
 .sort_values()
 .reset_index(drop=True)
 .to_frame(name="Start")
)
finish_df = (
 pd.concat([week_mon.to_series(), df["Finish"]])
 .sort_values()
 .reset_index(drop=True)
 .to_frame(name="Finish")
)
duration = (finish_df["Finish"] - start_df["Start"]).dt.total_seconds() / 3600
merged_s = pd.merge_asof(start_df, df.sort_values("Start"), on="Start", direction="backward")
merged_f = pd.merge_asof(finish_df, df.sort_values("Finish"), on="Finish", direction="forward")
pd.DataFrame(
 {
 "Machine": merged_s["Machine"],
 "Start": start_df["Start"],
 "Finish": merged_f["Finish"],
 "Duration": duration,
 }
).reset_index(drop=True)
                    
                  

&&&

Leave a Reply