【问题标题】:Forcing Date Format in ExcelExcel中的强制日期格式
【发布时间】:2017-10-01 03:13:32
【问题描述】:

我有一列包含正确格式的日期:

我正在尝试首先更改“。”到“/”,下面的代码可以正常工作,但它会自动修改日期格式并将日期识别为月份,如果它低于 12。

'2) Replace "." by "/"
    'Range("C:C").NumberFormat = "@" ' I tried with AND without this line...no difference
    'Range("C:C").NumberFormat = "dd.mm.yyyy hh:mm:ss" ' if I add this then only "." starting from 13th of January are reaplced by "/"
    'Range("C:C").NumberFormat = "dd/mm/yyyy hh:mm:ss" ' no differences at all neither
    Columns("C:C").Select
    Selection.Replace What:=".", Replacement:="/", LookAt:=xlPart, _
        SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _
        ReplaceFormat:=False

太烦人了……有什么办法解决这个问题吗?

【问题讨论】:

  • 你对循环开放吗?
  • 当然我可以用循环来做,但这真的是唯一的方法吗?我觉得这是一个小问题,我只是不知道如何解决。
  • 是的,但是它们会遍历每个单元格,就我而言,这是我(可能)会避免的。同样在我的情况下,在转换之前,所有单元格都具有相同的确切格式,因此似乎有一种方法可以避免循环
  • 如果您将“正确格式的日期”格式化为数字,您会得到实际的数值还是数值保持不变?如果是后者(如值的左对齐所暗示的那样),那么您的日期不是日期,它们是文本并且 Excel 不会将它们识别为日期,因为您认为“正确的格式”不是计算机的配置为(从控制面板设置)。如果你想格式化日期,你需要使用日期,而不是看起来像日期的字符串。然后使用NumberFormat 完成格式化,而不是字符串替换。

标签: vba excel formatting


【解决方案1】:

这段代码在Sheet1 中获取C2:C25,将其转换为内存数组,遍历该数组并将所有值转换为实际日期,然后将转换后的值转储到@987654325 @ on Sheet1,在 D 列中应用所需的 NumberFormat,然后调整列的大小以适合。

结果是将日期正确地视为日期,自定义格式:

Public Sub ConvertToDate()

    Dim target As Variant
    target = ToArray(Sheet1.Range("C2:C25")) 'todo: adjust to desired range

    Dim i As Long
    For i = LBound(target) To UBound(target)
        target(i) = ToDate(target(i))
    Next

    'here you'd probably dump the result in C2:
    Sheet1.Range("D:D").NumberFormat = "dd.MM.yyyy hh:mm:ss"
    Sheet1.Range("D2").Resize(UBound(target), 1).value = Application.WorksheetFunction.Transpose(target)
    Sheet1.Range("D:D").EntireColumn.AutoFit

End Sub

Private Function ToDate(ByVal value As String) As Date

    ' make sure our assumptions are correct before going any further:
    Debug.Assert value Like "##.##.#### ##:##:##"

    Dim datetimeParts As Variant
    datetimeParts = Strings.Split(value, " ")

    Dim dateParts As Variant
    dateParts = Strings.Split(datetimeParts(0), ".")

    Dim datePart As Date
    datePart = DateTime.DateSerial(dateParts(2), dateParts(1), dateParts(0))

    Dim result As Date
    result = CDate((CStr(datePart) & " " & datetimeParts(1)))

    ToDate = result

End Function

ToArray 辅助函数就是这个(改编自this post):

Private Function ToArray(ByVal target As Range) As Variant
    Select Case True
        Case target.Rows.Count = 1
            'horizontal 1D range
            ToArray = Application.WorksheetFunction.Transpose(Application.WorksheetFunction.Transpose(target.value))
        Case target.Columns.Count = 1
            'vertical 1D range
            ToArray = Application.WorksheetFunction.Transpose(target.value)
        Case Else
            '2D array: let Excel to the conversion itself
            ToArray = target.value
    End Select
End Function

您应该能够轻松地调整此代码以处理您的工作表和数据。

【讨论】:

  • 我试过了,但它不起作用(我的意思是格式问题仍然存在)。我想这是由于最后几行。我所做的是创建一个新列,将其格式化为dd/mm/yyyy hh:mm:ss 并粘贴结果
  • 是的,不行,我在 PhotoShop 中截屏了。
【解决方案2】:

这应该足以满足您的需要。我不关心这里的.Range("C:C"),但这应该没问题,只要C 列中只有这些数据。使用 .Range("C:C") 的问题在于它总是会修改和加载整个列,这会降低性能。如果我有机会,我会编辑代码以使用更优雅的解决方案,我只是想先得到一个有效的答案来帮助你开始。

无论如何,代码如下:

Sub FixDateFormatting()
    Dim ArrayDates() As Variant

    ' Load all the dates into an array for modification
    ArrayDates = ThisWorkbook.Sheets(1).Range("C:C").Value

    ' Edit the format of the destination to be text based. This will prevent Excel from assuming format
    ' Note: This must be done after the values are put into the array, otherwise you could load values in the
    ' wrong format.
    ThisWorkbook.Sheets(1).Range("C:C").NumberFormat = "@"

    Dim i As Long
    ' Loop through the array and properly format all of the data
    For i = LBound(ArrayDates, 1) To UBound(ArrayDates, 1)
        ArrayDates(i, 1) = Format(CStr(Replace(ArrayDates(i, 1), ".", "/")), "dd/mm/yyyy hh:mm:ss")
    Next

    ' Output the modified data
    ThisWorkbook.Sheets(1).Range("C:C").Value = ArrayDates
End Sub

ThisWorkbook.Sheets(1) 替换为对您正在修改的工作表的适当引用。如果目标工作表是运行代码的工作簿中的第一个工作表,则不必这样做。

这应该比循环好得多。这种方法的唯一缺点是,为了从这些单元格中检索值并对它们执行操作(使用 dd-mm-yyyy 格式),您必须在另一个数组中检索和操作这些值。如果您尝试对这些值使用 excel 公式,您将不会得到预期的结果。当您使用非标准日期格式时,这是不可避免的(至少据我所知)。

如果您有任何问题,请告诉我。

保重,

布兰登

编辑:

这是一个稍微更优雅的解决方案,应该会稍微提高性能。我(希望)更容易设置正确的目标工作表。我还调整了范围以仅包含必要的行数。见下文:

Sub FixDateFormatting()
    Dim TargetSheet As Worksheet
    ' Set the correct target sheet here:
    Set TargetSheet = ThisWorkbook.Sheets(1)

    Dim LastColRow As Long
    ' Store the absolute last row within a long variable for later use
    LastColRow = TargetSheet.Range("C1048576").End(xlUp).Row

    Dim TargetRange As Range
    ' Assumes your data starts in cell 2 (has a header row). Change the 2 as needed.
    Set TargetRange = TargetSheet.Range("C2:C" & LastColRow)

    Dim ArrayDates() As Variant

    ' Load all the dates into an array for modification
    ArrayDates = TargetRange.Value

    ' Edit the format of the destination to be text based. This will prevent Excel from assuming format
    ' Note: This must be done after the values are put into the array, otherwise you could load values in the
    ' wrong format.
    TargetRange.NumberFormat = "@"

    Dim i As Long
    ' Loop through the array and properly format all of the data
    For i = LBound(ArrayDates, 1) To UBound(ArrayDates, 1)
        ArrayDates(i, 1) = Format(CStr(Replace(ArrayDates(i, 1), ".", "/")), "dd/mm/yyyy hh:mm:ss")
    Next

    ' Output the modified data
    TargetRange.Value = ArrayDates
End Sub

编辑(再次):

最后一个解决方案更加优雅,并保留了“DATE”格式。然后,您可以根据需要编辑单元格。这使用 UDF(用户定义函数)。您只需键入要固定的日期作为目标的函数。它将输出一个日期,然后您可以将其修改为您需要的格式:

Public Function FixDateFormat(InputDate As String) As Date
    ' This will ensure that the string being input is appropriate for this function
    ' Modify the pattern as needed.

    If InputDate Like "##.##.#### ##:##:##" Then
        Dim DateTime As Variant
        DateTime = Split(InputDate, " ")

        Dim DateInfo As Variant
        DateInfo = Split(DateTime(0), ".")

        Dim HolderString As String
        HolderString = Format(DateInfo(1), "00") & "/" & Format(DateInfo(0), "00") & "/" & Format(DateInfo(2), "0000") & " " & DateTime(1)

        Debug.Print HolderString

        Dim OutputDate As Date
        OutputDate = CDate(HolderString)

        FixDateFormat = OutputDate
    Else
        ' Comment out this line to return a "#VALUE" error instead
        FixDateFormat = vbNullDate
        Exit Function
    End If
End Function

【讨论】:

  • 为了澄清第三种方法,问题在于您的初始值是一个字符串,鉴于奇怪的格式,它不能隐式转换为日期。上面的方法将字符串拆分,返回一个可以隐式转换的字符串,然后将此字符串转换为日期。然后输出是 Excel 可以理解的日期值。
  • 赞成 UDF 部分.. 这看起来非常接近我自己的解决方案 :)
【解决方案3】:

无需手动修改区域设置,必须使用循环。

LastRow = Cells(Rows.Count, "C").End(xlUp).Row
Set r = Range(Cells(1, "C"), Cells(LastRow, "C"))
t = r.Value
For i = 1 To UBound(t)
 t(i, 1) = Replace(t(i, 1), ".", "/")
Next
r.NumberFormat = ""
r.FormulaLocal = t

【讨论】:

    【解决方案4】:

    这样的事情呢

    Sub Makro3()
    
    Columns("C:C").Select
    
    Selection.NumberFormat = "dd\/mm\/yyyy hh:mm:ss"
    End Sub
    

    已添加hh:mm:ss

    【讨论】:

    • 如果我在上面的代码之前写这个,我也会得到第二张图片,所以没有区别
    • 对此进行了测试并得到了不同的结果。我猜这与本地时间/日期设置有关
    • 调整数字格式而不调整数据不会有任何作用,因为 Excel 将值视为字符串,而不是日期。
    【解决方案5】:

    如果您不介意遍历数据,可以执行以下操作:

    Sub Test()
    
    Dim ws As Worksheet
    
    Set ws = ThisWorkbook.Sheets("SheetName")
    
    For Each Cell In ws.Range("C2:C" & ws.Cells(ws.Rows.Count, "C").End(xlUp).Row)
        Cell.Value = CDate(Replace(Cell.Value, ".", "/"))
    Next Cell
    
    End Sub
    

    或者,如果您愿意,可以使用辅助列并使用以下公式:

    =TEXT(DATE(MID(C2,SEARCH(".",C2,SEARCH(".",C2,1)+1)+1,4),MID(C2,SEARCH(".",C2,1)+1,2),MID(C2,1,2))+TIMEVALUE(RIGHT(C2,8)),"dd/mm/yyyy hh:mm:ss")
    

    【讨论】:

    • 我有大约 15000 行,但如果我没有找到更优雅的解决方案,那绝对是一个不错的选择:)
    • @Seb 很痛苦,我知道!如果它更有用,我还添加了一个公式,您可以将其用作帮助列。
    • 我试过了,效果很好,实际上运行时间并没有我想象的那么长(我想避免循环的主要原因)
    • 您要避免的是在一个范围内迭代单元格的循环 - For Each 循环有利于迭代该集合类,但针对内存数组的 For 循环会更快。通常,您希望使用内存数组而不是单元格进行 Excel VBA 编程。当性能很重要时。
    【解决方案6】:

    有多种方法可以解决这个问题,最明显的方法是调整您的 NumberFormat 属性。尝试录制一些具有各种格式的宏,看看它是如何为您工作的。在您的情况下,代码可能类似于:

    Sheet1.Columns("C:C").NumberFormat = "mm/dd/yyyy"
    

    【讨论】:

    • 这是我现在正在尝试的,请参阅我的最后一次编辑。我不想要mm/dd/yyyy。这就是问题所在。我想要dd/mm/yyyy 而不是
    • 你是否在你已经玩过的数据上运行代码?一旦你运行了Replace 例程,单元格数据本身就会损坏。你能重新加载基础数据并在上面运行NumberFormat = "dd/mm/yyy"吗?
    • 否,第二张图片是代码应用于第一张的结果。问题是第二张图片的数据完全不同(13号之前的格式和13号之后的格式不同)。
    • 我仍然认为你需要检查你的数据是日期。您能否通过将=ISNUMBER(C1) 放入您的某一列并将其向下拖动来为我确认。如果任何结果显示FALSE,那么它可能是文本。只是你第一张照片中的左对齐让我怀疑。
    • 它返回 False 是的。哪个好?如果我可以通过“。”一直将其格式化为文本。到“/”转换就完美了。但是由于某种原因,当我打开那些“。”时,Excel会自动更改格式。进入“/”。
    猜你喜欢
    • 2018-01-28
    • 2020-10-04
    • 2018-01-24
    • 2013-03-17
    • 2015-08-22
    • 2013-09-16
    • 1970-01-01
    • 2018-04-24
    相关资源
    最近更新 更多