1

I have a column of unknown length (column Q) and I want to find the earliest and latest dates in this column. The first entry is in cell "Q2". The Dates are not arrange chronologically, so I can't simply look at the first and last row. Furthermore, I would like to paste all the months between the two dates in a new Worksheet. I have tried to find the earliest and latest dates, but I am already strugling with the code. Here is an extract of my code:

Dim SDate As Date, LDate As Date
Dim last_row As Long
Dim LastCell As String

last_row = Worksheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row

LastCell = "Q" & last_row

Worksheets("Sheet1").Select
SDate = WorksheetFunction.Min(Range("Q2:LastCell"))
EDate = WorksheetFunction.Max(Range("Q2:LastCell"))

It tells me that it does not recognize the value LastCell as an input for Range. [The date format in Sheet1 is dd.mm.yyyy]

As Output in Sheet2, I would like for it to look something like this (Starting in Cell D2): Wanted Output

Can anyone help me out? I am pretty new to VBA

Thank you in advance Sam

Pᴇʜ
  • 56,719
  • 10
  • 49
  • 73
Samboff
  • 33
  • 5
  • Are there expected to be duplicate instances of any dates? – Samuel Everson Jun 23 '21 at 10:03
  • Yes it is a possibility – Samboff Jun 23 '21 at 10:05
  • 3
    `Range("Q2:LastCell")` will not work using the variable `LastCell`. Therefore you need to change it to `Worksheets("Sheet1").Range("Q2:" & LastCell)`. You might benefit from reading [How to avoid using Select in Excel VBA](https://stackoverflow.com/questions/10714251/how-to-avoid-using-select-in-excel-vba). – Pᴇʜ Jun 23 '21 at 10:06
  • Is it expected that the dates will change years or will it always be dates within the same year? – Samuel Everson Jun 23 '21 at 10:11
  • 1
    They also change years, so it can go up to 5 / 6 years – Samboff Jun 23 '21 at 10:21

2 Answers2

1

Please, try the next code. It does not use any iteration:

Sub testDateRange()
   Dim sh As Worksheet, sh2 As Worksheet, lastR As Long, rng As Range, minDate As Date, maxDate As Date, arrD
   
   Set sh = ActiveSheet         'use here your necessary sheet
   Set sh2 = ActiveSheet.Next   'use here your necessary sheet (Sheet2)
   lastR = sh.Range("Q" & sh.rows.count).End(xlUp).row
   Set rng = sh.Range("Q1:Q" & lastR)
   minDate = WorksheetFunction.min(rng)
   maxDate = WorksheetFunction.Max(rng)

   If minDate = DateValue("00:00:00") Or maxDate = DateValue("00:00:00") Then
        MsgBox "One of the two necessary date could not be found...": Exit Sub
   End If

   arrD = GetMonthsInt(minDate, maxDate)    'get an array of the necessary months interval
   'Drop the array contents in the sheet:
   With sh2.Range("D1").Resize(1, UBound(arrD))
        .value = Application.Transpose(arrD)
        .NumberFormat = "MMM YY"
        .EntireColumn.AutoFit
    End With
End Sub

Private Function GetMonthsInt(startDate As Date, endDate As Date) As Variant
   Dim monthsNo As Long, rows As String, monthsInt
    monthsNo = DateDiff("m", startDate, endDate, vbMonday)
    rows = Month(startDate) & ":" & monthsNo + Month(startDate)

    If Day(startDate) > 28 Then dd = 28 Else: dd = Day(startDate)
    monthsInt = Evaluate("Text(Date(" & Year(startDate) & ",row(" & rows & "),1),""mmmm YYYY"")")
    GetMonthsInt = monthsInt
End Function
FaneDuru
  • 38,298
  • 4
  • 19
  • 27
  • Using your code I get: January, March, March, Mai, Mai... It seems to jump every month that does not have 31 days and replace it with the following month (so skipping february and writing march instead) and the writing march again – Samboff Jun 23 '21 at 12:34
  • @Samboff It shouldn't... I test it and did not skip anything. If not confidential, can you share the workbook with your date range. Even a dummy one... – FaneDuru Jun 23 '21 at 12:39
  • I can't seem to find a way to attach an excel-file – Samboff Jun 24 '21 at 08:57
  • @Samboff You cannot attach anything, but you can use a transfer site. [Tis one]() is free and easy to be used. Or send it to my personal mail (on my profile), but in such a case you should announce me. Otherwise, it will go in Spam... – FaneDuru Jun 24 '21 at 09:03
  • 1
    i sent it per mail :) – Samboff Jun 24 '21 at 09:36
  • 1
    @Samboff Yes, the code had a problem. **Corrected now**... It happens that your first date was 31 of January. The above code Evaluates a range equal to the months difference between the start date and end date, incrementing a month, but **keeping the found day of the start date**, which is not relevant for the necessary format, but if it is bigger than 28 it cannot correctly generate the necessary month for February and the same for the ones having less the 31 days. I am happy i could understand why it does not return exactly like in my tests. Voted up for that...:) Changed the function only. – FaneDuru Jun 24 '21 at 10:13
  • @Samboff I thought I delivered the most compact and elegant code, but I missed the possibility that the first day to have more then 28 days... – FaneDuru Jun 24 '21 at 10:19
  • @Samboff Please, try now the last version. I simplified it, using 1 for the day, which anyhow does not affect the result in any circumstance. In the previous update, I only created a `dd` variable but I did not use it. I did not copy the working code, I only copied the declaration and the line giving it a value... – FaneDuru Jun 24 '21 at 10:36
1

I found this a bit difficult due to Excel auto formatting my outputs as "yyyy/mm/d" however overcame this issue by using the Range.Value2 property rather than Value.

Relevant documentation:


Sub ShowMonthsBetweenTwoDates()
    Dim DatesArray As Variant
    Dim StartDate As Date: StartDate = Range("C1")
    Dim EndDate As Date: EndDate = Range("C2")
    Dim Iteration As Long
    Dim Years As Long
    Dim Months As Long
    Dim TotalMonths As Long
    
    Months = (Month(EndDate) - Month(StartDate))
    Years = (Year(EndDate) - Year(StartDate)) * 12
    TotalMonths = (Months + Years)
    
    ReDim DatesArray(TotalMonths)
    For Iteration = LBound(DatesArray) To UBound(DatesArray)
        If WorksheetFunction.EDate(StartDate, Iteration) <= EndDate Then
            DatesArray(Iteration) = WorksheetFunction.EDate(StartDate, Iteration)
        End If
    Next Iteration
    Iteration = 0
    
    Dim DestinationRange As Range
    Set DestinationRange = Range("E1").Resize(1, TotalMonths + 1)
    DestinationRange.NumberFormat = "mmm-yy"
    
    DestinationRange.Value2 = DatesArray
End Sub

This sub:

  • Uses dates as start and end from Cells C1 and C2, which use the MIN and MAX functions to find the lowest and highest date in a range (See sample screenshot below)
  • Counts the months between the two dates. This is done in 2 steps, months and years to account for multiple years in your date span.
  • Loops through using EDate to check if the date is <= the end date and if so it adds the date to the array.
  • Defines a destination range (I used Cell E1 as the starting point) and then writes the Array values to this range.

Sample Worksheet data tested on:

Sample worksheet date range and data

And the output:

Sample output


Note:

You may need to do some testing and modify the calculations/if statement evaluation as I haven't tested for all possible date scenarios - with the calculations I've used there may be some discrepancies (such as if the end date is 1 day less then the day of the date being checked) but again - this has not been tested for.

This solution allows you to get a start and end date and output your desired format to a range.


Note Note:

Using Value2 may provide unexpected results if using the output data in any calculations/evaluations - if you must use the Value property instead and look up one of the many existing solutions for getting around the obnoxious auto formatting of dates by excel.

Samuel Everson
  • 2,097
  • 2
  • 9
  • 24
  • 1
    Thank you very much! Yeah I was having the same problem with the formatting before as well. It was switching between the format I wanted, and just writing Oktober 2020 (as a string and without a date behind it). – Samboff Jun 23 '21 at 12:20