Excel VBA - 从格式错误的日期中提取正确的日期?

时间:2014-05-10 22:37:29

标签: excel vba date excel-vba formatting

我目前正在学习VBA编程,并遇到了以下情况,我将非常感谢您的帮助。理想情况下,不仅要找到解决方案,还要了解解决方案的工作原理和原因。

假设有一个数据库,可以从中导出数据电子表格。其中一列具有日期值,但它们的格式与导出格式不同。系统将日期发送为mm / dd / yyyy hh:mm AM / PM,例如04/11/2014 09:24 AM,但电子表格将其标识为dd / mm / ...,这意味着它将输入04作为日期和11月作为月份。

在此列中,如果该日期在该月的12个月之前或之前,则该单元格将被格式化为日期。如果日期超过12日,则格式化为一般格式。

我的问题是,我可以编写一个可以反转日期和月份值的VBA宏并在新列中创建正确的日期吗?我认为它首先必须确定一个单元格是否被格式化为日期,然后以某种方式提取日期和月份在正确的位置,或者如果它被格式化为一般格式,然后使用不同的代码来提取正确的日期。

如果对于这个社区来说这个问题太基础了,而另一个社区更适合我,那么我很乐意在那里重新提出我的问题。

修改

在我的评论下面,我玩了一些功能,并寻找其他类似的功能,可能有助于做我需要的,用月值切换日期值,到目前为止,我有:

'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

现在我只需要找出一个条件函数来根据值或格式或列A确定何时应用每组公式。

但是有没有相同的功能通过VBA来实现这一目标?我需要这些日期和时间列只保存值,而不是公式,以便我可以直接从中导出数据。并且在某种程度上把它放在VBA代码中对我来说似乎更“干净”,使用公式对我来说就像一个易变的解决方案。我不确定如何正确解释这个问题,但我在数据操作背后的正确编码方面更加舒适。

EDIT2:

我已经解决了工作表函数解决方案,如下所示。我花了一段时间才弄清楚如何绕过日期格式化单元格的FIND错误,并且只有在写入= IF时Excel中建议的列表中偶然发现了IFERROR函数。

'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"))

现在至少我有一个可行的解决方案,但我仍然对这些公式的VBA翻译感兴趣,并将继续搜索这些公式。

2 个答案:

答案 0 :(得分:0)

检查一下。我们以您的公式为例:

=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等效功能:

Find = Instr
日期= DateSerial
文字=格式(不完全相同但最接近)

代码相同:

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

请注意[A1]是一种快捷Evaluate功能,也可以写为Evaluate("A1")
我用它来指代公式中的单元格A1。您可以使用传统的Range Object引用,如下所示:Range("A1")。我使用了快捷方式因为它看起来更干净。但是在大数据集中这是不可取的。

您的时间公式:

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

代码等效:

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

您还可以检查单元格的格式,如下所示:

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

不确定以上是否能真正解决您的问题 从数据库中提取日期时间戳时,有很多种可能性 如果您提到的方案只是您遇到的问题,那么上述解决方案可能会有效。

答案 1 :(得分:0)

这现在是一个旧线程,但是如果有人遇到类似问题(如我所做)时偶然发现它,我会提供它。

我为此建议的VBA功能如下所示。它的风格并不严格遵循纯粹的编程实践(变量的声明等);而是相对容易理解的。

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