Excel VBA - Extract the correct dates from badly f

2019-08-03 06:38发布

I am currently learning VBA programming by doing, and have encountered the below situation with which I would appreciate your help. Ideally not just in finding a solution, but also to understand how and why the solution works.

Say that there is a database from which one can export a spreadsheet of data. One of the columns has date values, but they are badly formatted from the export. The system sends the dates as mm/dd/yyyy hh:mm AM/PM, for example, 04/11/2014 09:24 AM, but the spreadsheet has this identified as dd/mm/..., meaning it enters 04 as the day and 11 as the month.

Within this column, if the day is before or including 12 of the month, the cell is formatted as date. If the day is past the 12th, the cell is formatted with a general format.

My question is, could I write a VBA macro that could reverse the values for day and month and create the correct dates in a new column? I would think that it would first have to identify if a cell is formatted as date, and then somehow extract the date and month in the correct positions, or if it's formatted as a general format, and then use a different code to extract the correct date.

If this is too basic an issue for this community and there's another community more suited, I will gladly repost my question there.

EDIT:

After my comment below I played around with functions and looked for other similar functions that may help do what I need, switch the day value with the month value, and so far I have:

'for dates with general format: 04/14/2014 11:20 AM
=DATE(MID(A1,7,4),LEFT(A1,2),MID(A1,4,2)) 'in a column for the date
=TIME(MID(A1,12,2),MID(A1,15,2),"00") 'in a column for time, since I may need this

'for dates with a date format: 4/11/2014  7:35:00 PM
=DATE(TEXT(A1,"yyyy"),TEXT(A1,"dd"),TEXT(A1,"mm")) 'in a column for the date
=TEXT(A1,"hh:mm AM/PM") 'in a column for time

Now I just need to figure out a conditional function to identify when to apply each set of formulas according to the values or formatting or column A.

But are there equivalent functions to achieve this through VBA? I need these date and time columns to only hold values, not formulas, so that I may export the data out of them directly. And somehow putting this in VBA code seems more "clean" to me, using formulas feels to me like a volatile solution. I'm not sure how to explain this properly, but I'm somehow more confortable with proper coding behind my data manipulation.

EDIT2:

I've resolved the worksheet functions solution as below. It took me a while to figure out how to go around the FIND error with date formatted cells, and only found the IFERROR function by chance in the list Excel suggests when writing =IF.

'to get the correct date
=IF(IFERROR(FIND("/",A1),0)>0,DATE(MID(A1,7,4),LEFT(A1,2),MID(A1,4,2)),DATE(TEXT(A1,"yyyy"),TEXT(A1,"dd"),TEXT(A1,"mm")))
'to get the correct time
=IF(IFERROR(FIND("/",A1),0)>0,TIME(MID(A1,12,2),MID(A1,15,2),"00"),TEXT(A1,"h:mm AM/PM"))

Now at least I have a working solution, but I'm still interested in a VBA translation for these formulas and will continue searching for these.

2条回答
够拽才男人
2楼-- · 2019-08-03 06:53

This is now an old thread but in case anyone else stumbles upon it (as I did) with a similar problem, I'm just offering this up.

My suggested VBA function for this is shown below. Its style doesn't strictly follow purist programming practice (declaration of variables, etc); it's written, rather, to be relatively easily comprehensible.

Function Date_Text_Convert( _
            date_text As String, _
            return_with_month_letters As Boolean, _
            return_as_date_time_value As Boolean)

' Patrick S., June 2018
'   Intention: to enable mm/dd/yyyy[etc] imported text-string dates
'   to be switched to dd/mm/yyyy[etc]. Can be adapted for other cases.
'       Usage examples: if cell A2 contains the text-string:
'           06/26/2018 09:24 AM
'       then in, for example, cell B2, type:
'           =Date_Text_Convert(A2,TRUE,FALSE)   or  =Date_Text_Convert(A2,FALSE,FALSE)
'       which returns:
'           26-Jun-2018 09:24 am                or  26/06/2018 09:24 am
'   To return a date-and-time value instead of a string, use, for example:
'           =Date_Text_Convert(A2,TRUE,TRUE)

' establish the positions where the day and month digits start
    daypos = 4
    mthpos = 1
    rempos = 7 ' starting position of remaining part of the string

' establish the length of the relevant text sections: 2 characters each, in this case
    daylen = 2
    mthlen = 2

' so that,
    daytext = Mid(date_text, daypos, daylen)
    mthtext = Mid(date_text, mthpos, mthlen)
    remtext = Mid(date_text, rempos, 999) ' the remainder of the text string

' format the output according to 'return_with_month_letters'
' there are 2 options available, each using a different separator
    sep_stroke = "/"
    sep_hyphen = "-"

    If return_with_month_letters = True Then
            mthnum = mthtext * 1
            mthindex = ((mthnum - 1) * 3) + 1
            mthname = Mid("JanFebMarAprMayJunJulAugSepOctNovDec", mthindex, 3)
        newtext = daytext & sep_hyphen & mthname & sep_hyphen & LCase(remtext) ' LCase optional
    Else
        newtext = daytext & sep_stroke & mthtext & sep_stroke & UCase(remtext) ' UCase optional
    End If

' finally, return the output through the function name: either as a date, or as the text equivalent
    If return_as_date_time_value = True Then
        newdate = DateValue(newtext) + TimeValue(newtext)
        Date_Text_Convert = newdate
    Else
        Date_Text_Convert = newtext
    End If

End Function
查看更多
干净又极端
3楼-- · 2019-08-03 06:55

Check this out. Let's take for example your formula:

=IF(IFERROR(FIND("/",A1),0)>0,DATE(MID(A1,7,4),LEFT(A1,2),MID(A1,4,2)),DATE(TEXT(A1,"yyyy"),TEXT(A1,"dd"),TEXT(A1,"mm")))

VBA equivalent functions:

Find = Instr
Date = DateSerial
Text = Format (not exactly the same but the nearest)

Code equivalent:

Dim mydate As Date
Dim myformat As String

myformat = "mm/dd/yyyy hh:mm AM/PM"
If InStr(1, [A1], "/") > 0 Then
    mydate = DateSerial(Mid(Format([A1], myformat), 7, 4), _
        Left(Format([A1], myformat), 2), Mid(Format([A1], myformat), 4, 2))
Else
    mydate = DateSerial(Year([A1]), Month([A1]), Day([A1]))
End If
[B1] = mydate

Take note that [A1] is a shortcut Evaluate function which can also be written as Evaluate("A1").
I used that to refer to Cell A1 as in your formula. You can use the conventional Range Object reference like this: Range("A1"). I used the shortcut because it looks cleaner. But it is not advisable in huge data sets.

For your time formula:

=IF(IFERROR(FIND("/",A1),0)>0,TIME(MID(A1,12,2),MID(A1,15,2),"00"),TEXT(A1,"h:mm AM/PM"))

Code Equivalent:

Dim mytime As Date
If InStr(1, [A1], "/") > 0 Then
    mytime = TimeValue([A1])
Else
    '~~> myformat is declared above
    mytime = TimeValue(Format([A1], myformat))
End If
[C1] = mytime

You can also check the format of the cell like below:

Select Case True
Case [A1].NumberFormat = "General"
    mydate = DateSerial(Year([A1]), Month([A1]), Day([A1]))
    mytime = TimeValue(Format([A1], myformat))
Case [A1].NumberFormat = myformat '~~> again this is declared above
    mydate = DateSerial(Mid(Format([A1], myformat), 7, 4), _
        Left(Format([A1], myformat), 2), Mid(Format([A1], myformat), 4, 2))
    mytime = TimeValue([A1])
Case Else
    MsgBox "Invalid Format. Cannot be evaluated"
End Select
[B1] = mydate: [C1] = mytime

Not sure if above will really solve your problem.
There are just many possibilities when you extract datetime stamp from a database.
If the scenarios you mentioned are only the problems you encounter, then above solutions might work.

查看更多
登录 后发表回答