1

I have a column containing dates in the right format:

dd.MM.yyyy hh:mm:ss

I am trying to first change the "." to "/" which works fine which the following code, but it automatically modifies the date format and recognizes the day as month if it's under 12.

dd/MM/yyyy hh:mm:ss

'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

So annoying...any ideas how to fix this ?

Mathieu Guindon
  • 69,817
  • 8
  • 107
  • 235
Seb
  • 508
  • 8
  • 25
  • Are you open to loops? – Jordan May 03 '17 at 10:47
  • Of course I could do it with a loop, but is it really the only way ? I feel like this is a minor issue that I just don't know how to fix. – Seb May 03 '17 at 10:49
  • isnt this similar ? http://stackoverflow.com/questions/7106238/changing-the-date-format-to-yyyy-mm-dd – krib May 03 '17 at 11:00
  • yes kind of, but there they go through each cell and in my case this is what I would (possibly) avoid. Also in my case, before the transformation, all cells have the same exact format so it seems like there is a way to avoid the loop – Seb May 03 '17 at 11:03
  • 2
    If you format your "dates in the correct format" as numbers, to you get actual numeric values or the values remain the same? If the latter (as hinted by the left-alignment of the values) then your dates are not dates, they're text and Excel isn't recognizing them as dates because what you deem "the correct format" isn't what the computer is configured with (from control panel settings). If you want to format dates, you need to work with dates, not *strings that look like dates*. And then the formatting is done with `NumberFormat`, not with string replacements. – Mathieu Guindon May 03 '17 at 15:24
  • @Mat'sMug : Yes it makes sense, I am in the "latter" case. But how come I can do this process manually and it works but when I run the "Recorded Macro" it doesn't produce the same result ? – Seb May 03 '17 at 16:01
  • Because the macro recorder isn't flawlessly replicating every single action! – Mathieu Guindon May 04 '17 at 11:48

6 Answers6

2

If you don't mind looping through your data you can do the following:

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

Or, if you want you can use a helper column and use the following formula:

=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")
Jordan
  • 4,424
  • 2
  • 18
  • 32
  • I have about 15000 rows, but if I don't find a more elegant solution it is definitely a good option yes :) – Seb May 03 '17 at 11:01
  • @Seb a pain I know! If it's more useful I've also added a formula you can use as a helper column. – Jordan May 03 '17 at 11:15
  • I tried it and it works great, actually the runtime isn't long as I thought it would (main reason why I wanted to avoid a loop) – Seb May 04 '17 at 08:38
  • What you want to avoid is a loop that iterates Cells in a Range - a For Each loop is good for iterating that collection class, but a For loop against an in-memory array will be faster. In general Excel VBA programming you want to work with an in-memory array, not cells. When performance matters anyway. – Mathieu Guindon May 04 '17 at 11:44
2

This code takes C2:C25 in Sheet1, converts it to an in-memory array, traverses that array and converts all values to actual dates, and then dumps the converted values to D2:D25 on Sheet1, applies the desired NumberFormat in column D, and then resizes the column to fit.

output result

The result is dates properly treated as dates, custom-formatted:

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

The ToArray helper function is this one (adapted from 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

You should be able to easily adapt this code to work with your worksheet and data.

Mathieu Guindon
  • 69,817
  • 8
  • 107
  • 235
  • I tried this but it doesn't work ( I mean the format issue is still there). I guess it's due to the last lines. What I did is created a new column, formatted it to `dd/mm/yyyy hh:mm:ss` and pasted the result – Seb May 04 '17 at 12:36
  • 1
    Yeah, doesn't work, I made that screenshot in PhotoShop. – Mathieu Guindon May 04 '17 at 12:40
1

This should suffice for what you need. I don't care for the .Range("C:C") here, but this should be fine as long as column C only ever has this data in it. The problem with using .Range("C:C") is that it will always modify and load the entire column which can reduce performance. If I have a chance, ill edit the code to use a more elegant solution, I just wanted to get a working answer up first to get you started.

In any event, here's the code:

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

Replace ThisWorkbook.Sheets(1) with an appropriate reference to the sheet you are modifying. You don't have to if the target sheet is the first sheet within the workbook running the code.

This should be much better than a loop. The only downside to this approach is that in order to retrieve the values from these cells and perform operations on them (using the dd-mm-yyyy format) is that you have to retrieve and manipulate these values in another array. If you try using excel formulas on these values you will not get the expected results. This is unavoidable (at least to my knowledge) when you are using a non-standard date format.

Let me know if you have any questions.

Take care,

Brandon

EDIT:

Here's a slightly more elegant solution that should have a slight performance increase. I made it (hopefully) easier to set the correct target worksheet. I also have resized the range to only include the necessary number of rows. See below:

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

EDIT (Again):

This last solution is far more elegant and preserves the "DATE" format. You can then edit the cell as needed. This uses a UDF (user defined function). You can just type the function with the date to be fixed as the target. It will output a date that you can then modify to the formatting you need:

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
Brandon Barney
  • 2,382
  • 1
  • 9
  • 18
  • To clarify the third approach, the problem is that your initial value is a string that cannot be implicitly converted to a date given the odd formatting. The approach above splits apart the string, returns a string that can be implicitly converted, and then converts this string to a date. The output is then a date value that Excel understands. – Brandon Barney May 03 '17 at 16:02
  • Upvoted for the UDF part.. which looks very very close to my own solution :) – Mathieu Guindon May 03 '17 at 16:13
0

Without having to manually modify regional settings, you must use a loop.

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
Docmarti
  • 376
  • 2
  • 6
-1

There are a variety of ways of dealing with this, and the most obvious would be to adjust your NumberFormat property. Try recording some macros with various formats to see how it could work for you. In your case, the code could be something like:

Sheet1.Columns("C:C").NumberFormat = "mm/dd/yyyy"
Ambie
  • 4,872
  • 2
  • 12
  • 26
  • this is what I am trying right now, see my last edit. I don't want `mm/dd/yyyy` though. This is the issue. I want `dd/mm/yyyy` rather – Seb May 03 '17 at 10:44
  • Are you running the code on data you've already played with though? Once you ran that `Replace` routine the cell data itself would be corrupt. Can you reload the base data and run `NumberFormat = "dd/mm/yyy"` on that? – Ambie May 03 '17 at 10:55
  • No the second picture is the result of the code applied to the first one. The problem is that on the second picture the data are totally different (before the 13th the format is different than after the 13th). – Seb May 03 '17 at 10:59
  • I still think you need to check your data are dates. Can you confirm it for me by putting `=ISNUMBER(C1)` in one of your columns and drag it down. If any result says `FALSE` then it might be Text. It's just that left alignment in your first picture that's making me suspicious. – Ambie May 03 '17 at 11:14
  • It returns `False` yes. Which is good right ? If I could keep it formatted as text all the way through the "." to "/" transformation it would be perfect. But for some reason Excel automatically changes the format when I turn those "." into "/". – Seb May 03 '17 at 12:35
  • No, FALSE isn't so good. It means your base data is `Text`. So when you're replacing the `.` with `/`, Excel is trying to change the type to a date where it can. It also explains with the `NumberFormat` won't work. I think your best bet would be to pose another question, explaining that your original data is `Text` and that you want to change it to `Date` and be displayed in the way you want. It's a simple one to solve - so you'll get answers quickly. – Ambie May 03 '17 at 12:40
  • I just checked. If I manually do the following : 1) Format the colummn As "Text" 2) Select the whole column 3)CTRL+F > Find "." replace with "/" it works. And if I record that I get the exact same code I show above...but for some reasons when I run the code it doesn't produce the sam result. Regarding what you said above, I don't want to change it as a `Date` format. `Text` would be just fine – Seb May 03 '17 at 13:06
  • Adjusting the number format without tweaking the data won't do anything, because Excel is seeing the values as strings, not dates. – Mathieu Guindon May 03 '17 at 16:15
-1

What about something like this

Sub Makro3()

Columns("C:C").Select

Selection.NumberFormat = "dd\/mm\/yyyy hh:mm:ss"
End Sub

Added hh:mm:ss

krib
  • 569
  • 4
  • 14
  • If I write this before the above code I get the 2nd picture too, so no differences – Seb May 03 '17 at 12:40
  • Tested this and got different result. I would guess that it has something to do with local time/date settings – krib May 03 '17 at 12:43
  • Adjusting the number format without tweaking the data won't do anything, because Excel is seeing the values as strings, not dates. – Mathieu Guindon May 03 '17 at 16:15