Calculating total daily time in a spreadsheet with time ranges per cell - google-sheets

I have a basic spreadsheet to keep track of time spent on an activity.
The idea is to capture a time range per cell. E.g. 10:00 - 12:30 means 2 hours and 30 minutes.
Breaks can be taken during the day. On resuming, a new time range is entered in a new cell.
I want to calculate the total time per day. E.g. Mon Jun 15 is 2.5 + 4.5 = 7 hours.
The algorithm I'm thinking of is more or less
for each cell that contains time ranges for the given day
start, end = split(cell, " - ")
diff_decimal = (end - start) * 24
total += diff_decimal
But I'm not sure how to do this with spreadsheet functions.
The starting point I have is using =SPLIT(B2," - ") but I'm already blocked since I'm not sure how to handle the return value.
P.S. The problem could be simplified by having multiple "start" and "end" columns with one value per cell. But I want to try the given format, which I prefer, before trying another approach.

This is Google Sheets solution, as Excel does not have regex formulas.
Try this formula (place it in G1, column G:G formatted as number):
={
"Total";
ARRAYFORMULA(
IF(
A2:A = "",
"",
MMULT(
IFERROR(
( TIMEVALUE(REGEXEXTRACT(B2:F, "-\s+(\S+)"))
- TIMEVALUE(REGEXEXTRACT(B2:F, "(\S+)\s+-"))) * 24,
0
),
SEQUENCE(COLUMNS(B2:F), 1, 1, 0)
)
)
)
}
If you format G:G as Duration then you can remove * 24 part.
UPD
Added ignoring of the wrong formatted cells with IFERROR - just 0 in this case.
Better add this custom conditional formatting rule for B2:F to mark (in red on the screenshot) time periods in the wrong format and go fix them:
=AND(B2 <> "", NOT(IFERROR(REGEXMATCH(B2, "\b(\d|[01]\d|2[0-3]):[0-5]\d\s*-\s*(\d|[01]\d|2[0-3]):[0-5]\d\b"), False)))

Related

How to count groups of non-blank cells and blank cells

I am creating a calendar/tracker where a user inputs values into the "Work" column and the spreadsheet should essentially count those cells and the blank cells afterward in order to output values into the "Start Day, End Day, Work Length, and Cycle Length" columns. I'm looking for formulas that can calculate each value in these 4 columns.
Spreadsheet Image
Start Day (Green) corresponds with the first non-blank cell in the column.
End Day (Red) corresponds with the last non-blank cell after the Start Day.
Work Length (Blue) should count from the Start Day to the End Day.
Cycle Length (Purple, Orange, Yellow, Pink) is the length of days from the start day to the day before the start day of the next month.
My formulas only work in certain scenarios, like if there is only 1 group of values in a column (January and February). They doesn't work for when there are 2 groups of values in a month (March) or when one group starts at the end of the month and continues into the beginning of the next month (March/April).
The April values should be:
Start Day: Apr 24
End Day: Apr 27
Work Length: 4 Days
Cycle Length: *This can't be determined unless there was a May month (User will input this cycle length)
Spreadsheet link with current formulas: https://docs.google.com/spreadsheets/d/1WceKBNbyrb2rSCiIfVVpdt9l1fKIpPkqqxPataNHgoY/edit?usp=sharing
Alternative Method
You can also try using a Google Sheet Custom Function formula made possible by the Sheet's built-in scripting via Apps Script for a cleaner usage of the sheet formula. Here are the details:
CUSTOM_FUNC(month,array)
month will be the sheet cell that contains the month name
The array will be the range that contains the data of every Work columns
This custom formula will return the Start Day, End Day & Work Length automatically. E.g. on your March data, where you have two ranges of Work:
Note: This will also dynamically work on any months & ranges you define. E.g. you can also use =CUSTOM_FUNC(LEFT(H1,3),I3:I33) for the February month on cell J3.
To add the script below as a custom bound script in your spreadsheet file, you may follow the official guide here.
Script
/**
* Computes the "Start Day", "End Day" & "Work Length".
*
* #param {month,array} input The month value, range.
* #return the "Start Day", "Last Day" & "Work Length".
* #customfunction
*/
function CUSTOM_FUNC(month,array){
var out = [];
for (var arr, i = 0; i < array.length; i++) {
if (array[i] == "") {
arr = null;
} else {
if (!arr) out.push(arr = []);
arr.push([array[i],i+1]);
}
}
var res = out.map(x => x.toString().split(","))
return res.map(y => { return [month+" "+y[1],month+" "+y[y.length-1],(y[y.length-1]-y[1])+1+" days"]});
}
This script was derived from this existing answer to process the range values.
Demonstration
References
Custom Functions in Google Sheets
try X3:
=ARRAYFORMULA(LEFT(V1, 3)&" "&REGEXEXTRACT(REGEXEXTRACT(QUERY(
IF((W3:W="")*(V3:V<>""), "×", V3:V),,9^9), "(.+ \d+)"), "× (\d+)"))
Y3:
=ARRAYFORMULA(LEFT(V1, 3)&" "&REGEXEXTRACT(REGEXEXTRACT(QUERY(
IF((W3:W="")*(V3:V<>""), "×", V3:V),,9^9), "(.+ \d+)"), "\d+$"))
Z3:
=ARRAYFORMULA(COLUMNS(SPLIT(REGEXEXTRACT(REGEXEXTRACT(QUERY(
IF((W3:W="")*(V3:V<>""), "×", V3:V),,9^9), "(.+ \d+)"), "× (\d+.+)"), " "))&" days")

Sum values for the same date in google spreadsheet

I have a google spreadsheet that's tracking the duration of a particular activity over time. Imagine it looks something like this:
date | start time | end time | duration
5/21/18 10:00 AM 10:30AM 30
5/21/18 11:30 AM 12:30PM 60
5/21/18 2:00 PM 2:30PM 30
5/23/18 11:30 AM 12:30PM 60
5/24/18 9:30 AM 11:30AM 120
I want to make a chart with the dates along the X axis and the duration along the Y axis. However, I want each date to just be shown ONCE, with the TOTAL SUM duration for that date. So, the bar for 5/21/18 should be 120 min (i.e. the total of the three 5/21/18 entries), the bar for 5/22 should be 0 minutes (since there is no entry), and the bar for 5/23 should be 60 min.
If I make a chart as is, it treats each row as a separate entry (so there are three separate bars for each of the 5/21/18 entries), which doesn't work for my purposes since I want to combine entries with the same date, and have entries for in-between dates that have no entries.
Thank you!
You need to add any missing dates in the series.
In this case there is only one day but it could just as easily be two, three or a month's worth of dates.
Credit for missing dates in series: "--Hyde", Google Docs Help Forum Automatically Insert Missing Rows And Fill Values.
Assumption: the range shown in the question starts in cell A1 on a sheet named 'so_53684341'.
1) Create a new data series, including any missing values
a) Create a new sheet.
b) Cell A1, insert =arrayformula( { "Date"; datevalue("21/05/2018") + row(1:4) - 1 } ).
c) Cell B1, insert =arrayformula( {"Start time","End time","Duration"; if( len(A2:A), iferror( vlookup( A2:A, {datevalue(left(substitute(so_53684341!A2:A, " ", "-"), 10 ) ), so_53684341!B2:D }, column(so_53684341!B2:D2), false ), { "", "", 0 } ), iferror(1/0) ) } )
This creates data in columns B, C and D. It duplicates the existing data on the original range. and puts a zero value in the Duration column for any non-existing date(s).
You can format the date column as appropriate.
2) Create a query to be used as the basis for the chart
a) Cell F1, insert =query(A2:D5,"Select A, sum(D) group by A label (A) 'Date', sum(D) 'Duration' ")
This creates a range F1:G5
3) Create a chart using the range F1:G5
Something like this

How to autofill dates using arrayformula

I'm using Google sheets for data entry that auto-populates data from my website whenever someone submits to a form. The user's data imports into my sheet with a timestamp (column A).
Using the Arrayformula function, I'd like a column to autofill all the dates of a timestamp within that month. For example, if 1/5/2016 is entered as a timestamp, I'd like the formula to autofill in the dates 1/1/2016 - 1/31/2016.
Additionally, I'd like other months added in the Arrayformula column. For example, if both 1/5/2016 and 2/3/2016 are entered in column A, I'd like the formula to fill in the dates from 1/1/2016 - 2/29/2016.
I know I can manually write in the dates and drag them down the column, but I have a lot of sheets, and using an Arrayformula will save me a lot of time. I've tried a similar formula in column B, but it doesn't autofill in the date gaps. Is what I'm looking for possible?
Here's a copy of the editable spreadsheet I'm referring to: https://docs.google.com/a/flyingfx.com/spreadsheets/d/1Ka3cZfeXlIKfNzXwNCOWV15o74Bqp-4zaj_twC3v1KA/edit?usp=sharing
Short answer
Cell A1
1/1/2016
Cell A2
=ArrayFormula(ADD(A1,row(INDIRECT("A1:A"&30))))
Explanation
In Google Sheets dates are serialized numbers where integers are days and fractions are hours, minutes and so on. Once to have this in mind, the next is to find a useful construct.
INDIRECT(reference_string,use_A1_notation) is used to calculate a range of the desired size by given the height as a hardcoded constant, in this case 30. You should not worry about circular references in this construct.
ROW(reference) returns an array of consecutive numbers.
A1 is the starting date.
ADD(value1,value2). It's the same as using +. As the first argument is a scalar value and second argument is an array of values, it returns an array of the same size of the second argument.
ArrayFormula(array_formula) displays the values returned by array_formula
As A1 is a date, by default the returned values will be formatted as date too.
Increment by Month
If anyone wants to be able to increment by month, here's a way I've been able to accomplish that. Your solution #ptim got me on the right track, thanks.
Formula
Placed in B1
First_Month = 2020-11-01 [named range]
=ARRAYFORMULA(
IF(
ROW(A:A) = 1,
"Date",
IF(
LEN(A:A),
EDATE( First_Month, ROW( A:A ) -2 ),
""
)
)
)
Result
ID Month
1 2020-11-01
2 2020-12-01
3 2021-01-01
4 2021-02-01
5 2021-03-01
I have an alternative to the above, which allows you to edit only the first row, then add protection (as I like to do with the entire first row where I use this approach for other formulas):
=ARRAYFORMULA(
IF(
ROW(A1:A) = 1,
"Date",
IF(
ROW(A1:A) = 2,
DATE(2020, 1, 1),
DATE(2020, 1, 1) + (ROW(A1:A) - 2)
)
)
)
// pseudo code!
const START_DATE = 2020-01-01
if (currentRow == 1)
print "Date"
else if (currentRow == 2)
print START_DATE
else
print START_DATE + (currentRow - 2)
Notes:
the initial date is hard-coded (ensure that the two instances match!)
ROW(A1:1) returns the current row number, so the first if statement evaluates as "if this is Row 1, then render Date"
"if this is row 2, render the hard-coded date"
(nB: adding an integer to a date adds a day)
"else increment the date in A2 by the (adjusted) number of rows" (the minus two accounts for the two rows handled by the first two ifs (A1 and A2). Eg: in row 3, we want to add 1 to the date in row 2, so current:3 - 2 = 1.
Here's a live example (I added conditional formatting to even months to assist sanity checking that the last day of month is correct):
https://docs.google.com/spreadsheets/d/1seS00_w6kTazSNtrxTrGzuqzDpeG1VtFCKpiT_5C8QI/view#gid=0
Also - I find the following VScode extension handy for syntax highlighting Google Sheets formulas: https://github.com/leonidasIIV/vsc_sheets_formula_extension
The Row1 header trick is courtesy of Randy via https://www.tillerhq.com/what-are-your-favorite-google-spreadsheet-party-tricks/
nice. thanks.
To get the list length to adapt to the number of days in the selected month simply replace the static 30 by eomonth(A1;0)-A1. This accommodates for months with 31 days, and for February which can have either 28 or 29 days.
=ArrayFormula(ADD(A1,row(INDIRECT("A1:A"&eomonth(A1;0)-A1))))
Updated for 2022:
This can now be done pretty easily with the SEQUENCE function, it's also a bit more adaptable.
Below will list all of the days in columns but you can swap the first 2 values to place in rows instead:
=SEQUENCE(1,7,today()-7,1)
More specific to your example, below will take the date entered (via cell, formula, or named cell) and give you the full month in columns:
=SEQUENCE(1,day(EOMONTH("2016-1-5",0)),EOMONTH("2016-1-5",-1)+1,1)

Repeating time windows in Google Spreadsheets?

I'd like to insert time windows repeatedly in a column, like this:
10:00-10:20
10:20-10:40
10:40-11:00
11:00-11:20
11:20-11:40
12:00-12:20
Is there a way to achieve this?
Put data in cells:
B1 = 10:00 (start time)
B2 = 12:20 (end time)
B3 = 20 (interval in minutes)
Here's single arrayFormula, that will generate your column:
=ARRAYFORMULA(TEXT(B1+B3*1/24/60*(row(OFFSET(B8,,,(B2-B1)/(B3*1/24/60)))-row(B7)-1),"HH:MM")&"-"&TEXT(B1+B3*1/24/60*(row(OFFSET(B8,,,(B2-B1)/(B3*1/24/60)))-row(B7)),"HH:MM"))
Explanations
Look at sample file to explore more about this formula. Pay attention on some details:
any kind of logical sequence could be done with help of series 1,2,3... Formula like =ARRAYFORMULA(row(OFFSET(B8,,,7))-row(B7)) gives us column from 1 to 7.
Time treated like numbers in sheets: 1 day is 1, 1 hour is 1/24, 1 minute is 1/24/60 and so on
Time can't be properly converted into text as it's number. So you have to use text(time, "HH:MM") formula to convert time into text.
This will repeat your time window. The formula assumes the time range is in A2:A6.
The 3 in the formula is the number of repeats (change to you need). You might want
to consider placing A2:A6 on another sheet and referencing it in the formula.
=TRANSPOSE(SPLIT(JOIN(",", ARRAYFORMULA(SPLIT(transpose(rept(join(",",A2:A6)&",",3)),",")&",")), ","))

How to convert a number into hours in Google Sheets

1950 minutes is 32.5 hours. How do I calculate this using a formula in Google Sheets?
I am logging the number of minutes that I take on completing tasks and need a way of converting these numbers into the number of hours in Google Spreadsheets.
I have a column that contains rows of minutes, 1 row per task. This sum total of this column then needs to be added together, then converted into hours.
Here is a function that converts minutes in hour:minutes,
=if( A1 = 0 ; "" ; concatenate( quotient( A1 ; 60 ) ; ":" ;MOD(A1 ; 60) ))
62 --> 1:2
Use convert function to convert a numeric value to a different unit of measure
=CONVERT(A1; "mn"; "hr")
Documentation
Here is a function that converts minutes in hour:minutes:
=CONVERT(A1,"mn","hr")/24
Where A1 is the section you want to convert
Add up all the values using the function =SUM(number1,number2,number3,number4,...).
Example: =SUM(A1:A200) adds up all the numbers from A1, A2, A3 ... until A200.
Divide the total number of hours (it's located in the field with the =SUM function) by 60 (60 minutes in 1 hour, of course) with =DIVIDE(number1,number2)
Example: =DIVIDE(A201,60). In case you put the =SUM function in field A201, you divide the result of that field by 60.
Pierre's answer is mostly correct. However, it does not properly output the time if the number of minutes is below 0. If you need to handle a negative time amount, use the following formula instead:
=CONCATENATE(IF(AND(A1 < 0, A1 > -60), "-", ""), QUOTIENT(A1, 60), ":", TEXT(MOD(ABS(A1), 60), "00"))
I've improved it:
123 to proper 2h 3min
Do not show insignificant 0.
not show "0 mins" (number is completely divisible by 60) e.g. 120mins to 2h and NOT 2h 0min
not show "0h" (number is less than 60) e.g. 40min to 40min and NOT 0h 40min
To use just replace A1 with the cell number of the cell that contains mins.
=IF (A1=0, "", CONCATENATE ( IF(QUOTIENT(A1,60)=0, "", CONCATENATE (QUOTIENT(A1,60),"h ") ), IF(MOD(A1,60)=0,"",CONCATENATE (TEXT(MOD(A1,60),0),"min"))))

Resources