Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Be one of the first to start using Fabric Databases. View on-demand sessions with database experts and the Microsoft product team to learn just how easy it is to get started. Watch now

Reply
Idigor
Regular Visitor

Calculate work time from punch in/out times

Hi all,

 

My organization wants to get the punch in/out data and calculate how many hours each employee works.

 

My table looks like this:

EmployeeDateAction
John01/11/2020 8:00:00In
John01/11/2020 14:00:00Out
Alex28/10/2020 7:54:23In
Alex28/10/2020 12:23:52Out
Alex28/10/2020 14:01:44In
Jane12/10/2020 8:27:27In
Jane12:10/2020 15:44:22Out

 

As you can see, Alex forgot to punch out the second time, so I want to skip that value. His working hour should be from 7:54 to 12:23.

 

The idea is to get the hours/month and hours/year worked by each employee, but only counting In-Out pairs, if there is only a In, discard it.

 

If anyone can point me in the right direction, I'm quite new to PBI. Thanks.

1 ACCEPTED SOLUTION
Smauro
Solution Sage
Solution Sage

Hi @Idigor 

 

Check out this approach:

let
    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45W8srPyFPSUTIw1Dc01DcyMDJQsLAyMAAioKBnnlKsDjYlhiZwNf6lJbgUmRJhkAWGQY45qRVArpGFvqEBRJG5lamJlZExwhxMJYZGQAVWpkZ4zQG52tDKxATJQYl5qUCeoRFcjYWVkTkQ4VMC9JcJ0D1wu2IB", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Employee = _t, Date = _t, Action = _t]),
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"Date", type datetime}}),
    

    #"Split DateTime" = Table.SplitColumn(#"Changed Type", "Date", (dt) => {DateTime.Date(dt), DateTime.Time(dt)}, {"Date", "Time"}),

    #"Group by Date" = Table.Group(#"Split DateTime", {"Employee", "Date"}, {{
        "PunchTimes",
        each
            let
                times = _[[Time], [Action]],
                ins = List.Sort(Table.SelectRows(times, each [Action] = "In")[Time]),
                outs = List.Sort(Table.SelectRows(times, each [Action] = "Out")[Time]),
                zip = List.Select(List.Zip({ins, outs}), each _{0} <> null and _{1} <> null )
            in
                zip
        , type {{time}}
    }}),
    #"Split PunchTimes" = Table.SplitColumn(Table.ExpandListColumn(#"Group by Date", "PunchTimes"), "PunchTimes", (x) => x, {"In", "Out"}),
    #"Added Worked Time" = Table.AddColumn(#"Split PunchTimes", "Hours Worked", each Duration.TotalHours([Out] - [In]), type number)
in
    #"Added Worked Time"

It first splits your [Datetime] to [Date] & [Time],
It then groups per [Employee] & [Date] to pick only the existing in-out pairs -- assuming here that i) an employee could have more than one pair per day, and ii) either in or out could be missing, hence not to be counted.
Last, it expands the pairs to be counted in new rows and columns named [In] & [Out]
As an extra step, [Hours Worked] is added which calculates the duration in hours of every pair.

 


You could test this out on your data by using the steps after #"Changed Type", renaming #"Changed Type" to your Previous Step's name.
Also, if you do have more columns in your table, they should be added in the #"Group by Date" step, alongside {"Employee", "Date"}.

 



Best,
Spyros




Feel free to connect with me:
LinkedIn

View solution in original post

6 REPLIES 6
CNENFRNL
Community Champion
Community Champion

Hi, @Idigor , you might want to try a more generalized Power Query solution addressing some more complex senarios like this,

EmployeeDateAction
Test2020-10-30 09:00Out
John2020-11-01 08:00In
John2020-11-01 12:00In
John2020-11-01 14:00Out
Alex2020-10-28 07:54In
Alex2020-10-28 12:23Out
Alex2020-10-28 14:01In
Jane2020-10-12 08:27In
Jane2020-10-12 15:44Out
Jane2020-10-11 15:23Out
Test2020-10-31 11:28In
Test2020-10-31 16:09Out
Test2020-10-31 14:20In
Jane2020-10-11 10:09In
Jane2020-10-13 09:00In

 

let
    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("hdGxDsIgGATgV2mYS3L/D7WUzVEXFzfSwYHEwdTBmvTxhTRpaRVcyZe7A5wTV/8aRS0YDEmQChU6C4Sjy3sUfe3E+XkfFkESVMHM4jRkAPE/oLcdx4efkhVsKrS20WvENwgdrIoRsYSSFbfBJ4A43oPbEqDGap0+xU5QFJsV++cMgiybteQHOFh05QhtGfmdAWCOyAC1/GkE/Qc=", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Employee = _t, Date = _t, Action = _t]),
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"Employee", type text}, {"Date", type datetime}, {"Action", type text}}, "fr"),
    
    #"Sorted Rows" = Table.Sort(#"Changed Type",{{"Employee", Order.Ascending}, {"Date", Order.Ascending}}),
    #"Grouped Rows" = Table.Group(#"Sorted Rows", {"Employee"}, 
        {{
            "All", each
                [
                    log = Table.ToRecords(Table.AddIndexColumn(_,"Index",0,1)),
                    pairs = List.Accumulate(
                        log,
                        {},
                        (s,c) => if c[Action] = "Out" then s else 
                            [
                                next_c = log{c[Index]+1}?,
                                #"added In/Out" = if next_c[Action]?="Out" then s&{[In=c[Date], Out=next_c[Date]]} else s
                            ][#"added In/Out"]
                    )
                ][pairs]
        }}),
    #"Expanded All" = Table.ExpandRecordColumn(Table.ExpandListColumn(#"Grouped Rows", "All"), "All", {"In", "Out"}, {"In", "Out"}),
    #"Added Custom" = Table.AddColumn(#"Expanded All", "Working Time", each [Out] - [In])
in
    #"Added Custom"

 

Screenshot 2020-11-02 142942.png


Thanks to the great efforts by MS engineers to simplify syntax of DAX! Most beginners are SUCCESSFULLY MISLED to think that they could easily master DAX; but it turns out that the intricacy of the most frequently used RANKX() is still way beyond their comprehension!

DAX is simple, but NOT EASY!

AnkitKukreja
Super User
Super User

Hi @Idigor 

 

What you can do is split your columns to have in and out data then you can create a calulated column and use

Datediff ( In time , Out time , hours/minute/day) whatever you want to select, it's in your hand then.

 

Thanks,

Ankit

For Power BI trainings or support dm or reach out to me on LinkedIn.
If my response has successfully addressed your question or concern, I kindly request that you mark this post as resolved. Additionally, if you found my assistance helpful, a thumbs-up would be greatly appreciated.

Thanks,
Ankit Kukreja
www.linkedin.com/in/ankit-kukreja1904
https://topmate.io/ankit_kukreja
Anonymous
Not applicable

Hi ,

I am trying this query in my avd editor

bur i got null values

please help me in this

 

code :

let
Source = Excel.Workbook(File.Contents("C:\Users\Gaini\Documents\todays.xlsx"), null, true),
Sheet1_Sheet = Source{[Item="Sheet1",Kind="Sheet"]}[Data],
#"Promoted Headers" = Table.PromoteHeaders(Sheet1_Sheet, [PromoteAllScalars=true]),
#"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers",{{"Employee", type text}, {"Date", type datetime}, {"Action", type text}}),
#"Sorted Rows" = Table.Sort(#"Changed Type",{{"Employee", Order.Ascending}, {"Date", Order.Ascending}}),
#"Grouped Rows" = Table.Group(#"Sorted Rows", {"Employee"},
{{
"All", each
[
log = Table.ToRecords(Table.AddIndexColumn(_,"Index",0,1)),
pairs = List.Accumulate(
log,
{},
(s,c) => if c[Action] = "Out" then s else
[
next_c = log{c[Index]+1}?,
#"added In/Out" = if next_c[Action]?="Out" then s&{[In=c[Date], Out=next_c[Date]]} else s
][#"added In/Out"]
)
][pairs]
}}),
#"Expanded All" = Table.ExpandRecordColumn(Table.ExpandListColumn(#"Grouped Rows", "All"), "All", {"In", "Out"}, {"In", "Out"}),
#"Added Custom" = Table.AddColumn(#"Expanded All", "Working Time", each [Out] - [In])
in
#"Added Custom"

 

please help me in this

 

thanks in advance2.PNG

Smauro
Solution Sage
Solution Sage

Hi @Idigor 

 

Check out this approach:

let
    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45W8srPyFPSUTIw1Dc01DcyMDJQsLAyMAAioKBnnlKsDjYlhiZwNf6lJbgUmRJhkAWGQY45qRVArpGFvqEBRJG5lamJlZExwhxMJYZGQAVWpkZ4zQG52tDKxATJQYl5qUCeoRFcjYWVkTkQ4VMC9JcJ0D1wu2IB", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Employee = _t, Date = _t, Action = _t]),
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"Date", type datetime}}),
    

    #"Split DateTime" = Table.SplitColumn(#"Changed Type", "Date", (dt) => {DateTime.Date(dt), DateTime.Time(dt)}, {"Date", "Time"}),

    #"Group by Date" = Table.Group(#"Split DateTime", {"Employee", "Date"}, {{
        "PunchTimes",
        each
            let
                times = _[[Time], [Action]],
                ins = List.Sort(Table.SelectRows(times, each [Action] = "In")[Time]),
                outs = List.Sort(Table.SelectRows(times, each [Action] = "Out")[Time]),
                zip = List.Select(List.Zip({ins, outs}), each _{0} <> null and _{1} <> null )
            in
                zip
        , type {{time}}
    }}),
    #"Split PunchTimes" = Table.SplitColumn(Table.ExpandListColumn(#"Group by Date", "PunchTimes"), "PunchTimes", (x) => x, {"In", "Out"}),
    #"Added Worked Time" = Table.AddColumn(#"Split PunchTimes", "Hours Worked", each Duration.TotalHours([Out] - [In]), type number)
in
    #"Added Worked Time"

It first splits your [Datetime] to [Date] & [Time],
It then groups per [Employee] & [Date] to pick only the existing in-out pairs -- assuming here that i) an employee could have more than one pair per day, and ii) either in or out could be missing, hence not to be counted.
Last, it expands the pairs to be counted in new rows and columns named [In] & [Out]
As an extra step, [Hours Worked] is added which calculates the duration in hours of every pair.

 


You could test this out on your data by using the steps after #"Changed Type", renaming #"Changed Type" to your Previous Step's name.
Also, if you do have more columns in your table, they should be added in the #"Group by Date" step, alongside {"Employee", "Date"}.

 



Best,
Spyros




Feel free to connect with me:
LinkedIn

Anonymous
Not applicable

Hi Smauro, 

 

Can you elaborate the steps using UI as am new to Power BI finding it Difficult to understand. Thank you

Anonymous
Not applicable

Good morning,

i need helping solving what i thought should be simple.  I have dates in column 1 and times in column 2.

DATEIN

12/22/20217:00:00 AM
12/22/202112:00:00 PM
12/22/202112:30:00 PM
12/22/20213:30:00 PM
12/23/20217:00:00 AM
12/23/202112:00:00 PM
12/23/202112:30:00 PM
12/23/20213:30:00 PM

 i need this to be formatted as one instance of each "date" in column 1 and times for each date in rows

 

DATEINcolumn 1column 2column 3
12/22/20217:00 AM12:00 PM12:30 PM3:30 PM
12/23/20217:00 AM12:00 PM12:30 PM3:30 PM

Helpful resources

Announcements
Las Vegas 2025

Join us at the Microsoft Fabric Community Conference

March 31 - April 2, 2025, in Las Vegas, Nevada. Use code MSCUST for a $150 discount!

Dec Fabric Community Survey

We want your feedback!

Your insights matter. That’s why we created a quick survey to learn about your experience finding answers to technical questions.

ArunFabCon

Microsoft Fabric Community Conference 2025

Arun Ulag shares exciting details about the Microsoft Fabric Conference 2025, which will be held in Las Vegas, NV.

December 2024

A Year in Review - December 2024

Find out what content was popular in the Fabric community during 2024.

Top Solution Authors