Dynamic Work Schedule Based on Day of Week - excel

I'm trying to create a work schedule that adjusts the start and end date of a task (in this case a 'lesson') based on whether it is a weekday and/or a weekend. I have assigned a value in terms of complete days to each 'lesson', based on the day of the week. My hope is that if a lesson takes 2 days to complete during the week, and the range 'start.date:start.date + 2' doesn't contain a weekend day, then the end date would be 'start.date + 2' (e.g. Monday + 2). Equally, if that same lesson would take 1 day to complete on the weekend, and the range 'start.date:start.date + 1' doesn't contain a weekday, then the end date would be 'start.date + 1' (e.g. Saturday + 1).
However, the tricky part is when that range contains a mix of weekday and weekend. In that situation I'd like it to switch between the two lengths. For example, if all lessons take 2 days during the week and 1 day on the weekend, if:
start.date(1) = beginning of Friday, end.date(1) = halfway through Saturday (1 weekday + 0.5 weekend).
start.date(2) = halfway through Saturday, end.date(2) = halfway through Sunday.
start.date(3) = halfway through Sunday, end.date(3) = end of Monday (0.5 weekend + 1 weekday).
I've attached a spreadsheet along with images showing the formulas that I currently have. It works OK until the end date in cell H11. It should read Tuesday (as J11 should = 1, and K11 should = 0.5)
https://1drv.ms/x/s!ApoCMYBhswHzhttuLQmKNVw7G6pHSw
If this would be better suited to Python or R, or even VBA, then I'm more than happy to hear suggestions for those (also including relevant things to read so that I can start writing the necessary code), but I just don't have the required knowledge in them to make a decent start at the moment.
Thanks for your help.

You have to insert this macro into your workbook, I have mentioned the steps but if you find it difficult you can let me know.
Click Alt+F11 in your excel window
The VBA editor will open in a new window, in this window there is a
left side pane named as project window
Right click this workbook in project window and insert module.
Paste the below code on the newly opened text editor
.
Function calcEndDate(start_date, weekday_duration, weekend_duration)
ratio = weekday_duration / weekend_duration
temp_date = start_date
day_name = ""
For i = 1 To weekday_duration
day_name = Format(temp_date, "dddd")
If (day_name = "Saturday" Or day_name = "Sunday") Then
temp_date = temp_date + 1 / ratio
Else
temp_date = temp_date + 1
End If
Next
calcEndDate = temp_date
End Function
Now paste this formula in any cell, it will calculate the end date using start date and the duration
calcEndDate(start_date, weekday_duration, weekend_duration)
After that you have to save your workbook in xlsm (macro enabled excel workbook) format
For example for your first row it would be =calcEndDate(F3,B3,C3)
Then change the format of that column to mm/dd/yyyy hh:mm, so that you can know if a lesson is ending in half day.

Related

VBA - How can I get all Mondays in a month then multiply to working hours?

I'm trying to figure out how to get the total of Mondays in a month then multiply by working hours.
This is my example code it works but it counts wrong output:
If UCase(val) Like "EVERY MONDAY" Then
Dim numString As Integer
Dim strDays() As String
Dim wordCount As Long
numString = 2
strDays = VBA.Split(val, " ")
wordCount = UBound(strDays)
strWhEveryDay = ThisWorkbook.Sheets("Input").Cells(X, 4).Value
strWhEveryDay = strWhEveryDay * var_month
Debug.Print "Every = " & strWhEveryDay
Explanation:
It depends on the user if what they like to input in a TEXTBOX. However, the CALCULATION it depends on the date where the user input in TEXTBOX.
I have Textbox which is the target month where the user input the format of date like this:
**Jan-2023 or Feb-2023 **
I have a table like this:
Place this text in a table start in Column B Row 2:
**every Monday**
Place this text in a table Column D Row 4:
**1.2**
All I need is to get all the total of Mondays based on the given month and year. The calculation of the days in a table of "every Monday" once I change the text from "every Monday" to "every Tuesday" so the calculation will adjust or automatically knows where the calculation days start to end:
Example of expected calculation: every Monday (January 2023 = 5 Days) * 1.2 so, the expected result will be 5.
Note: use Debug.Print to see the result or output
So, using networkdays.intl() as suggested in my comment:
NETWORKDAYS.INTL($A$3,$A$33,"0111111",)
The result shown is 5, which is correct by inspection, for cells A3:A33 the long date format was used.
So multiplying by 1.2 is:
NETWORKDAYS.INTL($A$3,$A$33,"0111111",)*1.2
and 5 * 1.2 = 6
Also, the string "0111111" can be put in cell F5 and referred to so it is easier to edit.
The easiest way I know to recognise any day of the week is the following:
=WEEKDAY(B2,2)
The "2" means that weekdays are counted, starting with "Monday" as 1, "Tuesday" as 2, ...
So, if you want to know if your date is a Monday, you can use this formula:
=IF(WEEKDAY(B2,2)=1,...)
This can easily be translated into VBA, using a standard IF-clause.

How to handle dates across midnight in Excel

I have data with the following structure:
Date
Time
01-01-2021
0800-1600
01-01-2021
2000-0400
Each line is an employee and their worked hours. Meaning the first line employee #1 meets 0800 and leaves at 1600 the same day. However employee #2 meets 2000 and leaves 0400 the following day.
My issue is that I'm working showing no. of employees present at specific times. The first employee is easy to do as meeting and leaving is the same day. However the second is a bit more problematic, as in my current setup the employee is shown on work the same day.
The data is updated automatically into excel so I don't want to make any manual adjustments.
In order to show it correctly, and from my point of view, I need to make an additional line with the hours for the following day. I could make this with VBA, but I'm not sure if this is the easiest and best way to do it.
So any ideas on how to handle an issue like this?
Thanks!
This formula will give your the hours worked:
=LET(DateValue, A2,
TimeValues,FILTERXML("<t><c>"&SUBSTITUTE(B2,"-","</c><c>")&"</c></t>","//c"),
Start,INDEX(TimeValues,1),
End,INDEX(TimeValues,2),
StartTime, SUM(DateValue, Start/2400),
EndTime, IF(Start<End,SUM(DateValue,End/2400),SUM(DateValue+1,End/2400)),
EndTime - StartTime)
This will give you the hours worked in day one or day two (change the last variable to Day1Hours):
=LET(DateValue, A2,
TimeValues,FILTERXML("<t><c>"&SUBSTITUTE(B2,"-","</c><c>")&"</c></t>","//c"),
Start,INDEX(TimeValues,1),
End,INDEX(TimeValues,2),
StartTime, SUM(DateValue, Start/2400),
EndTime, IF(Start<End,SUM(DateValue,End/2400),SUM(DateValue+1,End/2400)),
Day1Hours, IF(Start<End,EndTime-StartTime,(DateValue+1)-StartTime),
Day2Hours,IF(End<Start, EndTime-(DateValue+1),0),
Day2Hours)
This I was hoping would return both results, but FILTERXML didn't seem to work within the LET function when returning the results:
=LET(DateValue, A2,
TimeValues,FILTERXML("<t><c>"&SUBSTITUTE(B2,"-","</c><c>")&"</c></t>","//c"),
Start,INDEX(TimeValues,1),
End,INDEX(TimeValues,2),
StartTime, SUM(DateValue, Start/2400),
EndTime, IF(Start<End,SUM(DateValue,End/2400),SUM(DateValue+1,End/2400)),
Day1Hours, IF(Start<End,EndTime-StartTime,(DateValue+1)-StartTime),
Day2Hours,IF(End<Start, DateValue+1-EndTime,0),
TRANSPOSE(FILTERXML("<t><c>" & Day1Hours & "</c><c>" & Day2Hours & "</c></t>","//c")))
Create a User Defined Function (UDF) to return a datetime in cols C and D from the values in col A and B. Put the UDF code in a module. For example the formula in C2 would be =date_time($A2,$B2,0) and in D2 =date_time($A2,$B2,1)
Option Explicit
' i=0 for start , i=1 for end
Function date_time(dt As Date, hrs As String, i As Integer) As Date
Dim ar
ar = Split(hrs, "-")
If ar(1) < ar(0) Then ar(1) = ar(1) + 2400 ' next day
' add minutes
date_time = DateAdd("n", Left(ar(i), 2) * 60 + Right(ar(i), 2), dt)
End Function

Need to change date into current work week (1-52), but skip to the next week if the day is Wed - Sat in VBA

I'm having trouble writing code in VBA that would allow me to input any given date then have an output of the current work week. I need a restriction of if the date is Sunday through Tuesday, it will keep that current work week and year but if the date is Wednesday through Saturday, then the next work week and year will show. For example, I'm looking to input (5/28/19) and have an output of 201922 or an input of (5/29/19) with an output of 201923 even though its technically the same work week.
Before getting too in depth, I do have a working function that provides the year and work week, but I'm trying to adapt the function or add a separate function that will change in to the next work week according to the given date.
I'm new to VBA but have tried to do a little research over the last few days. I was thinking that I could somehow have one input of the date then have two outputs where one would be the year and work week then the other would be the number associated with that date (1 for Sunday, 2 for Monday, and so on). I tried to create an if then statement that says if the number associated with that date is 1, 2, or 3 then the workweek would stay the same. If it was any other number then 1 would be added to that work week so it would move to the next one. I'm having trouble with trying to make two outputs and have them connected, if that makes any sense.
This is the code that I tried to create, but continuously failed at making. The function that gives the correct work week (without the adaptation of the work week based on the date weekday) is WWV1
Function WWV2(WeekdayName As Integer)
Dim WWV1 As Integer
If WeekdayName(Date) = 1 Or 2 Or 3 Then WWV1 = WWV1
Else: WWV1 = WWV1 + 1
End Function
This provides the cell with #NUM! when I use the function in that cell, which I assume is because I need to somehow connect the two functions.
how about this:
=YEAR(A1) & TEXT(WEEKNUM(A1,13),"00")
WeekNum returns the weeknumber with 13 saying it starts on Wednesday:
VBA
wkcd = Year(Range("A1")) & Format(Application.WeekNum(Range("A1")),"00")

Need help making sense of varying time values in Excel

I routinely get data in a time field column on an Excel spreadsheet that I need to convert into basic numbers such as 6, 7, 8.
Here's an example of the mess I get:
(Note: I cannot change how the data is received)
Time
-------------------------------
8am
10am
9:00 am (no early birds please)
8:00
9 a.m Sharp
7:00 am
8am-2pm
9 am
8AM TO 3PM
08-10-2018 9am
7:30am, 8:30 am
Any
9am
8AM TO 3PM
Today thru sun
10:00 a.m.
I recently ran some code that would pull out all numbers from the cell, then remove everything except the first number and that's somewhat close but...
The perfect scenario would be code to output the desired numbers and if cell data wouldn't comply with the code intent (error), a message box would open allowing edits, or, the cell would be flagged with a color so I knew where to look. I end up with about 500 lines of these cells to ferret through. Any suggestions would be appreciated.
While you are considering text comprehension, here's a cheap and dirty VBA function that might get a fair chunk of your cases. I would implement this in combination with conditional formatting that highlights cells greater than or equal to one or zero. (My example screenshot has such formatting in place.)
Public Function GetDate(DateVal As String) As Date
Dim returnDate As Date ' Date to be returned
Dim cleanedDate As String ' Working string containing the candidate date
Dim wordIndex As Integer ' Counter for walking through the word array
Dim wordArray() As String ' Working string broken into words
' Initialize to zero date
returnDate = CDate(0)
If IsNumeric(DateVal) Then
' Handle dates already converted
returnDate = CDate(DateVal)
Else
' Eliminate spurious characters
cleanedDate = UCase(Replace(Replace(Replace(DateVal, ".", ""), ",", " "), "-", " "))
If IsDate(cleanedDate) Then
' If CDate can fix it, just do that.
returnDate = TimeValue(CDate(cleanedDate))
Else
wordArray() = Split(cleanedDate, " ")
If UBound(wordArray) > 0 Then
wordIndex = 0
' Look through each word and return the first time found
Do Until returnDate > 0 Or wordIndex = UBound(wordArray)
If IsDate(wordArray(wordIndex)) Then
returnDate = CDate(wordArray(wordIndex))
End If
wordIndex = wordIndex + 1
Loop
End If
End If
End If
GetDate = returnDate
End Function
Here is what I get with your sample data in column A and =PERSONAL.XLSB!GetDate(A1) (or equivalent) in column B:
Note that rows 4, 6, and 8 show time values. This is because Excel converted the values when I pasted your sample data in place. The function handles values that Excel automatically converts.
Fixing Row 5 in code could get tricky. You'll have to weigh that coding effort against the simple manual fix of deleting the space between 9 and a.m.
The code gets about 80% of your example cases. If the ratio holds for your overall data, you're looking at manually adjusting roughly 100 cells. If you're doing this regularly, consider further tweaking of the code. One last ditch effort might be to walk through the string from left to right character by character to see if you can find and build a time string.
...or, of course, you can use text comprehension as mentioned above. :-)
Good Luck!

Handling TimeRecording-files in Excel (formatting cells)

Excel's assumptions about cells are confusing the heck out of me. I'm on Office 365 - Excel for Mac, Version 15.28.
I'm TimeRecording on a lot of things, I would like to calculate relations and tendencies on the different things. I've exported my log-files, and have opened it in excel. A simple version looks like this:
In the real sheet, then I have 40+ tasks and 50+ dates. I would like to be able to do some calculations on these data. But Excel doesn't 'know what it is' (time durations) and therefore can't add them up or do anything.
So one question would be, to how to let Excel know, that this is time durations? I tried doing what this question suggests. But when I format the cells as [h]:mm then it gives me this error:
FYI: In the big sheet, then there's so many times, so the total amounts up to something along the lines of 633:33.
I would just like to be able to do simple calculations, such as:
=SUM(B1, C4, D5)
or
=SUM(B1, C4, D5)/COUNT(B1, C4, D5)
And maybe also make some charts and graphs.
Another attempt I've done is to try to get all the cells to have the format hh.mm instead of hh:mm, but this gave me problems. My approach was this:
Convert all the cells to 'Text' to tell Excel: 'Hey... Don't do any auto-converting/guessing here, and don't turn any of the cells into dates or decimal numbers or fricking origami swans!'
After that then I make a simple 'Replace all' of : to .
But after the 'Replace all', then 633:33 turns into 633.36.00 (even though the cell was a 'Text' cell).
And if I then simply double-click on the cell to edit it, then the numbers 'magically' turns from 633.36.00 to 27/01/1900 15.36.00 ... What the hell!? I need a procedure that doesn't require me to go through all my thousands of numbers and edit any of them (or ensure that Excel have turned the numbers into flying unicorns.
EDIT1
Here's an example of the total sheet I'm working on in Google Sheets.
EDIT2
If I format the cells as [h]:mm, then I get an error (see above). But if I format it as [t]:mm, then I don't get an error (thanks to Axel Richter for pointing that out). It may have something to do with the initial language of my Excel-installation (danish).
However... If I then try to sum up a bunch of cell, after doing this formatting to everything, then it sums up to 0:00.
If I format all the cells to Time (well-knowing that it's the wrong format, but hoping that Excel can see it and fix it) - and thereafter trying to sum up a couple of cells, then it sums up to 00.00.00 (even though it wasn't empty cells).
Is it also important, that when I sum up some numbers, that I do it from a General-cell - or does Excel know, that if I start with the =-sign, that it's going to be a calculation (and therefore the cell-format doesn't matter)?
Excel will store date-time values as floating point double values in following form:
1 day = 1
1 hour = 1/24 = 0.0416666666666667
1 minute = 1/24/60 = 0.000694444444444444
So formatted as time all values greater than or equal 0 but lower than 1 will be from 00:00 to 23:59. Values greater than 1 will be dates with 1 = 01/01/1900 00:00:00. But if you are formatting such values as time only using hh:mm for example, then only the time is shown. The date is simply hidden.
For example 1.25 formatted using hh:mm will show 06:00 although it is 1 1/4 day which is 01/01/1900 06:00:00. To see hours from multiple days the format [h]:mm can be used. For example 1.25 formatted using [h]:mm will show 30:00 which is 1 day (24:00) + 1/4 day (06:00).
Although Excel will do this independent of locale settings, the user defined format codes used and the kind of input values which Excel will take as time values are dependent of locale settings.
For example with your locale Danish (Greenland) the format codes are different. See Formatere tal som datoer eller klokkeslæt .
So your format code will be [t]:mm instead of [h]:mm.
And also with your locale Danish (Greenland) the time separator is . instead of :. So values which Excel will take as time values are 123.45 (123 hours, 45 minutes) instead of 123:45.
In your last comment you say: "whereafter it weirdly looked the same, such as: 123:45 and not 123.45". Yes that is because your user defined format [t]:mm contains the time separator : also. But that is different from your locale settings where . is the time separator. While inputting values Excel respects the locale settings and so expects 123.45 as time value for 123 hours and 45 minutes. But after the input Excel applies the cell formatting [t]:mm and so shows 123:45.
In your last comment you say that it confuses you that 17:24 * 24 equals 417:36. But that is exactly what it should.
17:24 is 17 hours and 24 minutes. That multiplied by 24 is 17 hours * 24 = 408 hours and 24 minutes * 24 = 576 minutes. 576 minutes are 9 hours and 36 minutes. So we get (408 hours + 9 hours) and 36 minutes = 417 hours and 36 minutes = 417:36.
I cannot edit the sheet so I copied it. As you can see in column AS and row 43, Google provides 'duration' format. You don't have to manipulate something. Just change the cell format.
In Excel, duration format is [h]:mm. Hit ctrl + 1 at the cell and choose Custom and type [h]:mm at Type and hit enter.
If SO answer is too difficult to follow, try this.
I apologize in advance for how rough this is, but I mostly slapped this code together to fit the task and didn't want to waste time on it. The principles are there though, so at the least it should point you in the direction you need to be heading.
Sub Time_Summarization()
Dim i As Long
Dim j As Long
Dim cell As Range
Dim sHolder As String
Dim vHolder As Variant
Dim arrHolder() As Double
Dim bAdd As Boolean
Dim dHolder_Whole As Double
Dim dHolder_Remainder As Double
Dim sOutput As String
ReDim arrHolder(0 To 2)
' Use a set range. Selection here is just for testing
' Ideally there should be data validation in this loop to ensure that the input
' values are numeric time values.
For Each cell In Selection
' Convert the cell value to a date to permit splitting.
' The value is then split into a 1-d array with 3 positions (H, M, S)
vHolder = Split(CDate(cell.value), ":")
' Loop through the split values from first to last, and trim off the AM/PM.
' If it is a PM date, set the flag to add 12 (13:00:00 gets displayed as 1:00:00 PM)
For j = LBound(vHolder) To UBound(vHolder)
' If PM, set the flag.
If InStr(vHolder(j), "PM") Then bAdd = True
' Remove "AM" and "PM"
vHolder(j) = Replace(vHolder(j), " AM", vbNullString)
vHolder(j) = Replace(vHolder(j), " PM", vbNullString)
' Add the values into the array in the same order.
arrHolder(j) = arrHolder(j) + vHolder(j)
Next
' Add 12 hours if needed
If bAdd Then arrHolder(0) = arrHolder(0) + 12
' Reset the flag for the next loop
bAdd = False
Next
' Step backwards through the array to round up increments of 60.
For i = UBound(arrHolder) To LBound(arrHolder) + 1 Step -1
' This will return the number of times the value goes into 60.
dHolder_Whole = arrHolder(i) \ 60
' This will return the remainder of the value divided by 60.
dHolder_Remainder = arrHolder(i) Mod 60
' Round up seconds to minutes, and minutes to hours.
arrHolder(i - 1) = arrHolder(i - 1) + dHolder_Whole
' Overwrite the remainder
arrHolder(i) = dHolder_Remainder
Next
' Combine the separate values into a string.
sHolder = arrHolder(0) & ":" & arrHolder(1) & ":" & arrHolder(2)
' Just for testing, do with the values whatever you wish.
Debug.Print sHolder
End Sub
Again, this is mostly a model that will work, but will need to be adapted to suit your needs.
Zeth, I downloaded your file and I can make some calculation with your time data. I juss selected all cell with time duration and change the format of cell to "time". Seemingly you should change all cells format, incluiding the empty cells.
If it does not work, find the "More format of numbers" ate the "Numbers" menu. Then, select the option "Hour" and chose the format closest to the format of your data. Also pay attenction to the option "locality" at the bottom of this menu. The option of hour format deppends on the region selected. (Each region in the world have some convenctions about it and Excel reconize much of then.
Formatting the numbers does not change the way Excel does calculations.
So a cell (c3) formatted as time and showing 01:28:00 actually contains 0.061111 because Excel treats time as fractions of a 24 hour day.
When you add up a lot of times and they add up to more than 24 hours the underlying number is more than 1 day so you get number of days before the decimal point and after the decimal point is the fraction of 24 hours remaining. So to convert a duration or time to hours you just multiply it by 24 and format it as a number or general (and the numbers after the decimal point are fractions of an hour). If you just want to format the result as hours and minutes use a format of [h]:mm and do not multiply by 24 - on your system look at Format Cells - Custom to see what the equivalent of [h:mm] is.

Resources