I need to recreate some of the functionality from Excel's EOMONTH function -- specifically the ability to enter a date and an integer representing a number of months in the future or past, and from those inputs calculate a new date that is the last day of the target month. I have been using Excel formulas for some simple testing and I can get close but cannot get it exactly right. So I would like to do this in Excel but without using the EOMONTH function or any of the other fancy date-related functions like EDATE or DATE or similar (because some of these automatically handle / correct some kinds of invalid data ... like months larger than 12 for example). TRUNC and MOD are fine ... and basic math operators ... but that's about it. This needs to be done in excel only -- no VBA code. We can assume the date entered is a month-end date, if that helps.
Say I have the following data in cells:
A1: 12/31/2021
A2: 1
The result in A3 or wherever should be 1/31/2022
With A1: 12/31/2021
A2: 0
A3 should be 12/31/21
A1: 12/31/2021
A2: -1
A3 should be 11/30/2021
A1: 12/31/2021
A2: -12
A3 should be 12/31/2020
I need to go up to 60 months forward or backward from any valid end-of-month date. I have been able to do what I want with a positive number (or zero) in A2, but negative numbers cause issues which I have not been able to figure out. I am using MOD + TRUNC + some basic math inside IF statements -- pretty ugly and doesn't handle negative numbers so I'm hoping there is a better way. Oh ... and I have actually been deriving Day 1 of the month following my "target month" ... and then subtracting 1 from that Day 1 date, to arrive at my target End Of Month date. This seems to me like a good way to work around the (lack of) EOMONTH function ... but I am happy to entertain any and all ideas / options.
--- New Info added 12/23 ---
I cannot simply use the EOMONTH formula because my target language (a virtual attribute in Uniquery) does not support it. So I am prototyping the solution in Excel but will implement it in the other system.
I did not include the detail below initially because A) I think there must be a more elegant solution so I am a little embarrassed about this ugliness and B) it is a lot of detail which I thought might be unnecessary. But since you asked :) here is some data from my worksheet showing what I have done and the results (and the issue).
Input:
A3 (input date): 10/15/2020
E3 (Months +/-): *** see comments below ***
Formulas:
B3 (input month): =MONTH(A3)
D3 (input Year): =YEAR(A3)
G3 (Interim New Month): =B3+E3+1 (+1 to give me the month AFTER my target month)
H3 (Month Adjustment): =MOD(G3,12)
I3 (Year Adjustment): =TRUNC(G3/12,0)
J3 (New Month): =IF(H3=0,12,H3)
K3 (New Year): =IF(H3=0,D3+I3-1,D3+I3)
L3 (Interim New Date): =J3&"/1/"&K3 (This will be day after my target date)
M3 (New / Target Date): DATEVALUE(L3)-1
With a couple dozen dates in cells A3:A27, first date = 10/15/2020, second date = 11/15/202, etc. (each date in the following month) ... and the formulas above entered on row 3 then copy/pasted down to row 27 ... and trying various values in cell E3 ... I can see the formulas work for numbers from -2 up through +60, but fail for certain months when E3 contains -3 or smaller. Specifically, with E3=-3 and input date = 1/15/21 my logic fails because the Interim New Month (column G) calculates a value of -1 which causes problems for the downstream formulas. I have addressed some of these problems with the MOD and TRUNC logic ... and the IF H3=0 logic ... but the one thing I cannot figure out is how to get the year right. With the current formulas when E3 is -3 and the base date is 1/15/21 the final year comes out 2021 which is incorrect. It should be 2020. And when E3 = -4 the base dates of 1/15/21 and 2/15/21 both produce incorrect values for Year -- 2021 instead of 2020. Smaller and Smaller E3 values produce more incorrect results.
Surely there is a relatively easy solution to this but I just cannot see it!?!?
Thanks!
Related
I have two columns in excel, one with a date and one with a rating 'low, medium, high'.
I'm trying to write a formula to put in a third column that checks:
If A2 = Low and B2 (date) is older than 12 months from today(), output Overdue
If A2 = Medium and B2 (date) is older than 6 months from today(), output Overdue
If A2 = High and B2 (date) is older than 3 months from today(), output Overdue
If these parameters aren't met, output "Current".
This is what I have so far but I'm well aware its not right :)
Could someone please point me in the right direction?
Thanks,
=IF(AND(A2="Low",LOOKUP(DATEDIF(B2,TODAY(),"m"),{0,3,6,12},{"Current", "Current" "Current","Overdue"}),(A2="Medium",LOOKUP(DATEDIF(B2,TODAY(),"m"),{0,3,6,12},{"Current","Current","Overdue","Overdue"}),(A2="High",LOOKUP(DATEDIF(B2,TODAY(),"m"),{0,3,6,12},{"Current","Overdue","Overdue","Overdue"})
If you can use the excel 365 then I’d suggest creating an excel lambda function:
=LAMBDA(level, date, LET(months, IFS(level="Low",12,level="Medium",6,level="High",3,TRUE,0), IF(date < EDATE(Today(), -months),"Overdue", "Current")))
so to break it down, this is a custom function that expects 2 parameters :
level is the value/reference that has low/medium/high. this is used to determine the number of months for the threshold - 12/6/3 respectively and 0 for anything else though you could change that as needed.
date is the date to check if its beyond the threshold. this is compared against today minus the number of months calculated from the level. if this is before that date then this function will return “Overdue”. otherwise “Current”.
You would create a workbook level named reference with this function as the value.
Here I named it IsCurrent. Then you just use that function where you want the output. e.g.
What I would suggest is to "outsource" the settings for the overdue months.
This is a good habit, as everyone looking at the table can see those settings and propably adjust them - without going into the formula.
And it is possible to use these settings in another formula :-)
If you use Excel 365 you can make the formula more readable/understandable with the LET-Formula.
=LET( OverdueMonthsForRating, IFNA(INDEX(configOverdue[Overdue months],MATCH([#Rating],configOverdue[Rating],0)),0), OverdueDate,IF(OverdueMonthsForRating>0, EDATE([#Date],OverdueMonthsForRating),TODAY()), IF(OverdueDate<TODAY(),"overdue","current") )
OverdueMonthsForRating is using a classic INDEX/MATCH to retrieve the number of months according to the Rating. In case Rating is not found 0 is returned
OverdueDate calculates - using EDATE - the overdue date based on the ratings date and OverdueMonthsForRating. In case Rating is not found TODAY is returned
Finally this date is evaluated against TODAY and the status is returned.
Classic Excel formula w/o LET:
=IF(EDATE([#Date],IFNA(INDEX(configOverdue[Overdue months],MATCH([#Rating],configOverdue[Rating],0)),TODAY()))<TODAY(),"overdue","current")
I have found the solution thought it would be good to share:
so the formula should be :
=((MAX(CT$6-$N8,0)-MAX(EOMONTH(CT$6,-1)-$N8,0))-(MAX(CT$6-$O8,0)-MAX(EOMONTH(CT$6,-1)-$O8,0))+(EOMONTH(CT$6,0)=EOMONTH($N8,0)))*1
:)
I am trying to write an excel formula which can be dragged across a row of cells to give the number of days in each month between two specified dates. E.g:
I tried using a if with sum product formula from the same website but for some reason it is not recognising the days of last month. was wondering if someone could help explain what this formula does and how to correct it.
=IFERROR(IF(AND(CS$6>=$M9,CS$6<=$N9),SUMPRODUCT(--(MONTH(ROW(INDIRECT($M9&":"&IF($N9="",TODAY(),$N9))))=MONTH(CS$6))),),"")
SECOND REVISION (R2)
Updated workbook link to capture fixed column headings
Eqn / formula also includes static view
Depending upon years spanned, R2 represents a less favourable outcome re space (will require more columns). In this case, number of additional columns required to match stacked view ~100 (!!).
FIRST REVISION (R1) etc. based upon #Charlotte.Sarah.Smith feedback
Days in each month were 1 day less than they should have been - this has been corrected (in both screenshot, linked sheet, and relevant eqns)
Also showcase a couple of sample illustrations/visual representations which may/not be of interest (included within linked sheet too)
Suppose we wanted to expand upon #Will's solution by stacking the dates by year, so that column headings can vary according to different start dates (as opposed to the very first start date that happens to appear)...
For instance, if the next row included the date range 'start = 10/02/16' through 'end = 15/03/19' - you'll appreciate that the number of columns spanning Jan-Dec ('16), Jan-Dec ('17),... up to (and incl.) the range in the first row (Jan '21 - Mar '23) becomes unwieldy.
By using a data table (see here) you can produce a 'stacked' view of the number of days per month regardless of the year (!) - see screenshot below and link to this [updated/corrected] worksheet.
R2 screenshot:
REVISION
See validation/reconciliation column at end
*Visual representations - could be useful for assessing trends etc.
Fun in 3D too!
1] Red font: first row that defines the construct of the data table: enter date range in the format '10/02/2021-15/08/2023'
Cell E3 eqn (drag to right):
=IFERROR(IF($A3>EDATE(E$2,1),0,MAX(,IFERROR(IF(MIN($B3,MIN(EDATE(E$2,1),$B3))=E$2-1,0,MIN($B3,MIN(EDATE(E$2,1),$B3))),"")-MAX($A3,E$2))),0)
(Similar to what we've seen previously)
2] Table below red font: enter any start date (as a regular date, e.g. dd/mm/yyyy) < end date (likewise, regular date) in columns A, B as desired/req.
Data Table
3] Data Table (column data): enter the following in cell c4 (drag down as req.)
=TEXT(A4,"dd/mm/yyyy")&"-"&TEXT(B4,"dd/mm/yyyy")
4] Data Table (highlight cells c3:d42, insert data table, select blank/empty cell for 'row input' and c2 for 'column input')
The data table should 'come to life' (calculate sheet, shift + F9) otherwise.
FilterXML
5] Split result by delimiter '|' using FilterXML as follows (cell E4, only drag down, not to right):
=IFERROR(TRANSPOSE(FILTERXML("<AllText><Num>"&SUBSTITUTE(LEFT(MID(D4,2,LEN(D4)-1),LEN(MID(D4,2,LEN(D4)-1))-1),"|","</Num><Num>")&"</Num></AllText>","//Num")),"")
VALIDATION
Note the check column: date difference = sum of days in table (default cell colour is otherwise RED):
REVISION 2
Here is the formula for a static version of above (i.e. no stacking by year, instead, each date in column headers are distinct re calcs) - it was already available in row with red font(!!)
=IFERROR(IF($A3>EDATE(C$2,1),0,MAX(,IFERROR(IF(MIN($B3,MIN(EDATE(C$2,1),$B3))=C$2-1,0,MIN($B3,MIN(EDATE(C$2,1),$B3))),"")-MAX($A3,C$2))),0)
Viola!
One potential solution would be to compare dates using the MIN and MAX functions like this:
=IF(MIN($B2+1,D$1)-MAX($A2,C$1)<0,"",MIN($B2+1,D$1)-MAX($A2,C$1))
screenshot
This solution uses month starting dates (e.g., Feb 1, 2021), but it could be adapted to work with month end dates instead (e.g., Feb 28, 2021).
The idea is that you take the later of the finish date or month end date minus the lesser of the start date or the month start date. For the first month and last months, this returns the number of days within that range inclusive of the first and last day. For all the months between the first and last months, it returns the number of days in the month.
The +1 on the minimum of the end dates seems a little counterintuitive, but it's necessary to include the first and last day in the count. Without it, you'd get 14 days in March 2022 instead of 15.
The "if less than zero" function makes it return a blank (or zero if you'd prefer) for any month outside the dat range.
This solution does not summarize by month (e.g., 19 days for Feb 2021 plus 28 days for 2022 to get 47 total days in February), but that's consistent with the sample you provided. The comments also indicate that you want to consider the years as well.
I am using this formula to determine if the date is column F occurs after the date in column G, but before the date in column G + 3 work days.
I was using the following formula which worked quite well:
=SUMPRODUCT(($F$5:$F$1000>$G$5:$G$1000)*($F$5:$F$1000<($G$5:$G$1000+3)))
But I realized I was not accounting for weekends in the final "+3."
So I tried this:
=SUMPRODUCT(($F$5:$F$1000>$G$5:$G$1000)*($F$5:$F$1000<(WORKDAY($G$5:$G$1000,3))))
And it returns #VALUE! This happens whether I push Ctrl+Shift+Enter or not.
How do I make this work please?
As appointed by #Tim, the workday function cannot accept a range. But you can simulate what you need with the weekday function by using this:
If(weekday(G2:G5; x)>=y;5;3)
Where x is your code of sunday, y is Wednesday. If your working day is greater than wednesday, then you will sum 5, not 3.
=SUM(IF($F$2:$F$3 > $G$2:$G$3; 1; 0)*
IF($F$2:$F$3 < $G$5:$G$1000 + IF(WEEKDAY($G$2:$G$3)>= Y ;5;3); 1; 0))
Weekday does accept a range, and it returns a range of weekdays. When you compute if (that also accepts a range) you create an 0/1 matrix (which is like your
indicator matrix of rows where the condition is active). If you * both matrix of conditions, you will have the remaining rows that matches both conditions. And lastly, if you apply the sum, you will get the count.
This is a way to simulate sumproduct when you have conditions that requires a formula
You will have to use ctrl +shift + enter.
03/09/2015 02/09/2015
07/09/2015 03/09/2015
Using this dates i obtained the result of 2.
Which is correct, both dates are greater than their partners but lower than their partners + 3 working days
P.S: I use spanish Excel, so it can be mistakes of formula translating
Lori_m, in the comments to my question provided a working answer:
Try inserting a + sign into the formula to convert the range to an array: WORKDAY(+$G$5:$G$1000,3) – lori_m 17 hours ago
Thanks.
The Start_Date argument of the WORKDAY function cannot accept a range (eg $G$5:$G$1000 in your code). I'm not exactly sure what your trying to do without more details and some sample data, so that's the best help I can give you.
I have the following excel table
------A ----------------- B ----------------------C ------------------------D
1 --First--------------Last-----------------Start Date--------------End Date
2 --John--------------Smith-------------10/09/2014------------24/11/2014
3---John--------------Smith--------------20/11/2014------------31/01/2015
(Occasionally I have duplicate names on a spread sheet). I am creating a formula which determines the number of days between the earliest start date and latest end date from two date ranges. Ultimately the answer from this table should be C2 - D3 (143 days).
I have nearly finished the formula, but I am stuck with the nested IF ELSE logic in between. It is as follows.
=IF(AND($C3>$C2,$D3<$D2), DATEDIF($C3,$D3,"d"),
IF($C3>$C2, DATEDIF($C3,$D2,"d"),
IF($D3<$D2, DATEDIF($C2,$D3,"d"), DATEDIF($C2,$D2,"d"
))
))
With this I get 4 days, which is using C3 - D2. This means the statement is terminating after the True condition in the second IF statement. What would I need to add to ensure it checks the logic in the third IF statement (Is D3 a later date than D2).
Using your provided example, this should work for you:
=MAX($D2:$D3)-MIN($C2:$C3)
I have an Excel 2010 workbook with this formula:
=EOMONTH("01"&TEXT(B7,"MMM")&IF(MONTH(CMVAR)<4,TEXT(YEAR(CMVAR)-1,"YYYY"),TEXT(YEAR(CMVAR),"YYYY")),0)
It resolves when you are in the cell and press Enter, however when the workbook first opens or is refreshed, the result is #VALUE!. Here are the components:
B7 =IF(OR(MONTH(CMVAR)>6,MONTH(CMVAR)<4),"Apr",IF(MONTH(CMVAR)=4,TEXT(EDATE(CMVAR,-3),"MMM"),IF(MONTH(CMVAR)=5,TEXT(EDATE(CMVAR,-3),"MMM"),TEXT(EDATE(CMVAR,-3),"MMM"))))
which equates to Apr.
CMVAR 31/03/2015
The formula is being used because in April, May, June (first three fiscal periods) we require comparison data to show in the 12-period grid from the previous financial year. From July onwards we will have comparable data from the current year and so the grid can start from April. Once the month has been determined I'm trying to work out what the date of the end of that period is, taking into account that Jan, Feb and Mar are actually periods 10, 11 and 12 of the fiscal year and so the year element of the formula will be the prior year if CMVAR shows the date to be in any of those months.
Is there a better way that avoids the error or a way to fix it?
It is not completely clear what result do you expect for different values of CMVAR, but looking at your formula, I suppose you want it to be:
You can calculate Result with the following formula:
=EOMONTH(CMVAR,-MAX(MOD(MONTH(CMVAR)-4,12),3))
If the picture above does not show your expected output, can you please prepare similar table?
EDIT:
To explain how the problem is solved, I have created additional columns with intermediate calculations:
column C is the month difference between CMVAR and expected result - the goal is to find a formula returning this number
column D calculates month of CMVAR
column E - function MOD returns the remainder after number is divided by divisor (12).
column E matches all values of C, except 0,1,2, so in column F function MAX replaces those values with 3
Your EOMONTH formula is going wrong because the TEXT part should be in the form TEXT(date,"YYYY"). YEAR(CMVAR) gives a number rather than a date.
You could use instead
=EOMONTH("01"&TEXT(B7,"MMM")&IF(MONTH(CMVAR)<4,TEXT(EDATE(CMVAR,-12),"YYYY"),TEXT(CMVAR"YYYY")),0)
or this may be easier than using the TEXT functions
=EOMONTH("01"&B7&IF(MONTH(CMVAR)<4,YEAR(CMVAR)-1,YEAR(CMVAR)),0)
Your B7 formula is OK but could be simplified to
=IF(OR(MONTH(CMVAR)>6,MONTH(CMVAR)<4),"Apr",TEXT(EDATE(CMVAR,-3),"MMM"))