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
Related
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.
Need a bit of help, if you'd be so kind.
In MS Excel, I'm trying to work out the number of months that a student attends university based off of the academic year.
For instance, a student enrolls & finishes university on the following dates:
23/06/2018 - 23/06/2020
and there are multiple academic years which the student attends:
01/08/2017 - 31/07/2018
01/08/2018 - 31/07/2019
01/08/2018 - 31/07/2020
I want to calculate how many months the student attends in each academic year:
01/08/2017 - 31/07/2018 (1 month)
01/08/2018 - 31/07/2019 (12 months)
01/08/2018 - 31/07/2020 (11 months)
I've managed to calculate the days using this function:
=MAX(MIN(term_end_date, student_,end_date) - MAX(term_start_date, student_start_date)+1,0)
But, I need months, if possible.
Appreciate the help in advance.
=DATEDIF(A1,B1,”M”) will give the number of whole months between 2 dates (i.e., excluding start and end months) where dates are entered in A1 and B1. The “M” signifies that you’d like the information reported in months.
For a more accurate result, try:
=(DATEDIF(A1,B1,”D”) / 365) * 12. If the dates span multiple years - including leap years - try dividing by 365.25 vs 365.
Please try this UDF.
Function MonthsOverlap(Rng As Range, _
AccYear As Integer) As String
' 016
Const Ststart As Long = 1 ' index of array 'Study'
Const Stend As Long = 2 ' index of array 'Study'
Dim Fun As Integer ' Months of overlap
Dim YearStart As Date, YearEnd As Date
Dim Study As Variant
YearStart = DateSerial(AccYear, 8, 1) ' modify year and day as required
YearEnd = DateAdd("yyyy", 1, YearStart)
Study = Rng.Value ' Rng columns must be adjacent
With Application.WorksheetFunction
If Study(1, Ststart) <= YearStart Then
Fun = DateDiff("m", .Max(Study(1, Ststart), YearStart), .Min(Study(1, Stend), YearEnd))
ElseIf Study(1, Ststart) <= YearEnd Then
Fun = DateDiff("m", .Max(Study(1, Ststart), YearStart), YearEnd)
End If
Fun = .Max(Fun, 0)
End With
MonthsOverlap = Fun & " month" & IIf(Fun = 1, "", "s")
End Function
I tested it in a sheet like the one pictured below. Please observe the formula in the Formula Bar.
As you see, the function returns unexpected results for the years 2017 and 2019. I don't think they are wrong. The formula just uses rounding of months different from your system. I think the function can be modified to accommodate your rule once you make it known. Meanwhile it follows Microsoft's rule which, frankly, I also don't know. However, observe that the total time of study is 24 months which is correct and therefore sufficient, in conjunction with a look at the clock, to convince me to let good be good enough for today :-)
I found a solution which works a treat:
Thanks for all your help everyone.
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.
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!
I have transaction data which includes a trade date. I want to be able to match the trade date of the transaction with the matching time period.
I have a table with 5 different time periods like so:
Period 1: 1/1/2000 - 3/31/2000
Period 2: 4/4/2001 - 6/6/2001
Period 3: 10/10/2002 - 12/31/2002
etc.
I want to be able to match the dates from the transaction data with their matching period for further calculation purposes.
The only solution I could thing of was nested if formulas but those are always ugly and depending on how many different periods I have not usable.
Thanks for the help!
Try this:
Public Sub RangeChecker()
Dim date1, date2, myDate As Date
Dim myDateStatus As String
date1 = CDate("January 1, 2000")
date2 = CDate("June 1, 2000")
myDate = CDate("May 1, 2000")
If (myDate > date1 And myDate < date2) Then
myDateStatus = "The date is inside the range"
Else
myDateStatus = "The date is outside the range"
End If
MsgBox (myDateStatus)
End Sub
You should only need one if statement per date range.
It's difficult to answer when you don't specify enough the layout of your data, which thing is in which column etc. Supposing the periods are defined in columns A, B and C, and the date for which you want to find the period is F2, you can use this CSE formula:
=INDEX($A$2:$A$7,MATCH(1,($B$2:$B$7<=F2)*(F2<=$C$2:$C$7),0))
' enter then press Ctrl+Shift+Enter