Prevent Excel converting number range to Date format - excel

I'm using Excel to translate data from one system which outputs .csv files to another which can read in .xls files. One column is sizes, which frequently includes terms like 4-6, 6-8, etc. When the csv is opened in Excel, these are automatically(?) converted to dates (6-Apr, 8-Jun, etc.) I can use
.NumberFormat = "m-d"
to get the data to *look like it should, but of course it's still a date value, and any operations I try to perform with it convert back to date. Is there any way to convert it to text in exactly that format? If I declare the .NumberFormat as "#", it just changes the value to a serial. I use the size, along with several other columns to create one long product ID code for inventory purposes.... when the size changes to a date value, I end up missing some of the data.

I found a solution to this problem that runs entirely from VBA - i.e., doesn't depend on special import conditions. I would appreciate any pointers or recommendations, especially if this has the potential to create unforeseen errors:
Dim Size as String
For i = 2 To LastRow
If IsDate(Range("H" & i).Value) Then
Size = Month(Range("H" & i)) & "-" & Day(Range("H" & i))
Range("H" & i).NumberFormat = "#"
Range("H" & i) = Size
End If
Next i
The column I'm testing includes size values that are string (XL, SM, 12T, etc), along with values that get converted to dates (4-6, 6-8). This seems to convert it back to a string value, and then properly treats it as a string value on all future uses (concatenations and logical tests)

Related

When writing an array of values to a range, how do I prevent excel from ignoring local date time formats

I have found many similar questions but never a solution that doesn't include brute force re-engineering.
Using this simple subroutine:
Sub readwrite()
Dim arr As Variant
Dim i As Long
arr = Range("F2:F36").Value
For i = LBound(arr) To UBound(arr)
arr(i, 1) = CStr(arr(i, 1))
Next i
Range("G2:G36").Value = arr
End Sub
I get conversion away from my UK locality into US date formats
instead of this:
I get this:
commenting out the Cstr line gets the first result. Adding the line:
arr(i, 1) = FormatDateTime(arr(i, 1), 0)
after the Cstr line doesn't fix things, nor does using Value2.
I realise that I can fix this with brute force looping, but my real world problem requires an unpredictable mix of dates and blank strings in two out of 32 columns and hundreds of rows, so I need something uncostly.
The brute force method is to ensure all conversions from dates to string and back again are done before trying to write out to the sheet:
arr(i, 1) = Format(arr(i, 1), "dd/mm/yyyy hh:mm")
arr(i, 1) = DateValue(arr(i, 1)) + TimeValue(arr(i, 1))
As I said, this is do-able and works, it's just a bit problematic.
I'd appreciate any advice. I'm a software tester by trade but also a fairly advanced coder.
addendum - I put =G2-F2 in column H and got:
Which means this date handling bug has real consequences.
I resolved the issue by ensuring the array held date objects (where necessary) when it was written out to the sheet. This meant ensuring that every final assignment of a date object to the array used
myarray(x,y) = datevalue(mydate)+timevalue(mydate)
because even though mydate was a date object, assigning it to the array in any other way converted it back to a string!
I had an ancillary issue in that I was using
myrange.value = Application.Transpose(myarray)
and the Transpose function converted the date objects to string objects again, which were then misinterpreted by the value assignment. I wrote and used my own transpose function instead.
It would have been simpler for me if Microsoft could fix the bug in value assignment and assume local formats when dealing with ambiguous date strings.
Change the NumberFormat of the Range prior to writing the values of the Array to the Range.
Range.NumberFormat
NumberFormat Codes
Range("G2:G36").NumberFormat = "d/m/yyyy hh:mm"
I never use copy and paste in my macros, but an "alternative method" to "Brute Force" would be:
Range("F2:F36").Copy
Range("G2:G36").PasteSpecial Paste:=xlPasteValues

Possible faster VBA lookup with wildcards?

I need to perform a quite a lot of lookups with wildcards on the worksheet using a macro (mainly lookup for value & returning the value from another column though with proper adjustment it can be also just looking for a value with wildcard, and some lookups only as checks if the value exists in the dataset). My data can't be sorted and all the lookups are within a loop A or loops within loop A; wildcards are included mostly for condition "string begins with...". I often have to find a value in one row and find corresponding value in row N rows below or above.
I have a working code, but I wonder if it can be done faster. #response to comment about posting it on Code Review (sorry, I cannot comment yet :)) - preparation the whole code to posting would take a bit too much time for me, confidentiality etc, so I prefer to treat it as a general question to be worked on this example.
Example data (I can add more columns, if I need any helper column):
Example Data picture at Imgur
Assume 100 000 rows (max xPagesCount = 1000, typically around 400; all values for certain xPage is in one block). Due to a lot of possible rows with additional data I can't simply find one value and add numbers to the found row to find the other values by their position.
Example lookups to perform while looping through consecutive xPages (so, for each given xPage):
value in row just below row with "RESTRICTIONS:" text
find name (which is always given with height (column C) = 35)
find RSW number (which can be in several rows depending on page content, but always below name)
find all rows starting with the same four digits as RSW, in two formats: DDDD.LLL.DD and DDDD.DDDDD.DD (L letter, D digit) (I use internal loop here)
check if there is a text "MASTER" (or "MASTER " etc.)
find all values between values "DOCUMENTS:" and "OPTIONS:", which quantity can be from 1 to 50 (I use internal loop here)
I was wondering, what is the fastest way to do such lookups?
What I tried:
using a dictionary on all dataset (keys in column A or C with, values
col.D) but as dictionary can't work on wildcards, I had to add ifs
for not finding a key to perform additional Application.Match
lookup... and then realized it mostly worked on these Match lookups
and not sure I even need a dictionary. I also have duplicate values
within a page and dictionary was getting only first value, regardless
their position (for example, several attachments could have value 1).
The main use remained dict.exists("MASTER") but when I removed
dictionary and changed it to IsError(Application.Match(...)) the code
worked slightly faster.
Application.Match in whole range, typical example: Application.Match(xPage & "4???.*", sh.Range("A1:A" & LastRow), 0)
in few places I use If xValue Like "????.???.??" Then construction
I have dictionary lookups with ifs redirecting to Application.Match:
xValue = dict(xPage & "ATH.416")
If dict(xPage & "ATH.416") = "" Then xValue = Application.Match("ATH.*", Sheets(1).Range("D:D"), 0)
What I consider, but not sure it's worth the effort:
altering the code that at the beginning of the iteration I find the first and the last row for xPage, and then each later check is performed in this range
xStartPage = sh.Range("D" & Application.Match(xPage, sh.Range("A1:A" & LastRow), 0))
'or, I guess better:
xStartPage = xEndPage + 1
If xPage = xPagesCount Then
xEndPage = LastRow
Else
xEndPage = sh.Range("D" & Application.Match(xPage + 1, sh.Range("A1:A" & LastRow), 0) - 1)
End If
xValue = sh.Range("D" & Application.Match("4???.*", sh.Range("D" & xStartPage & ":D" & xEndPage), 0)).Value

Global VBA date format and decimal separator

Is there a way to change VBA settings globally on PC to accept dates and number on a specified format? (on my case dd/mm/yyyy and comma)
Changing Excel settings doesn't solve it for me.
As an small time VBA developer, I'm mostly creating userforms for data input and validation. Alongside with some basic access privileges, It keeps users (mostly an client's hired arms) from nosing on the database and corrupting it.
But, on form's submitting, the textbox values are saved temporally on spreadsheet's cells. Somehow on this step dates get scrambled and in some cases an 3 decimal places numeric gets multiplied by a thousand (e.g. 1/2/2000 turn to 2/1/2000 and 1,234 turn 1234). It defeats the whole purpose of those applications - data gets corrupted.
I've been able to workaround these using the Format(expression, format) function, but that must be applied every time an date or some precision number is added, or even used on some auxiliary task.
This is an recurrent problem for me because, as an Brazilian, dates are formatted as dd/mm/yyyy and decimal separator is ","(comma) on practically 100% of my local users.
Anybody had similar problems?
TIA
Excel doesn't have a default date format. Excel uses the Window System Date format settings. You can change you system setting by go to Control Panel -> Change date, time and number formats.
Change Date Format in Windows 7, 8.1 and Windows 10 to dd-mm-yyyy
After adjusting the Windows System Settings to dd-mm-yyyy, CDate will expect strings to be in the dd-mm-yyyy.
Range("A1").Value = CDate( "11/01/2016" )
Result: Monday, January 11, 2016
Summary of comments for those to lazy to read.
Alright, as Thomas Inzina pointed, the strait answer to my question is NO, you can't because there isn't such thing in VBA as this Global setting.
As Rory pointed out, the CDate function should solve (indeed it does) this issue, at least as to the date. Again, Thomas answer didn't include it but it points to the windows conf that would be used by the CDate function.
Datetimepicker, suggested by cyboashu, would solve this issue too, but it requires some tweaking on the user's PC to be available. Too much work for me. Although, this approach has the "pretty" advantage, adds value to your project.
Still looking for the comma/dot bad conversion problem. I'll keep editing this answer while none better exists.
Here I write a function called gdate that accepts a string with a date in a specific format. Then I parse the string, then call cdate based on the users date settings.
All I have to do is find/replace cdate with gdate throughout my code. Now I have a way to handle all date formats by always expecting an exact one gdate("mm/dd/yyyy"). Adjust the parsing if you want to expect different format. Building off built-in objects and functions is how we make things work.
Function gdate(ByVal dstring As String)
' 0 = month-day-year; 1 = day-month-year; 2 = year-month-day
d = Mid(dstring, InStr(1, dstring, "/") + 1, Len(dstring) - 5 - InStr(1, dstring, "/"))
m = Left(dstring, InStr(1, dstring, "/") - 1)
y = Right(dstring, 4)
dtype = Application.International(xlDateOrder)
Select Case dtype
Case 0: gdate = CDate(m & "/" & d & "/" & y)
Case 1: gdate = CDate(d & "/" & m & "/" & y)
Case 2: gdate = CDate(y & "/" & m & "/" & d)
End Select
End Function

How to add leading zeros in Excel (timestamp with ms has no leading zeroes)

I’m still having a grave problem with some files. It’s a rather stupid problem, but I’ve been working at it for quite some time and can’t find a solution.
I need leading zeroes in the time stamps, at least on the ms level.
The timestamps that my software makes always look like this: (example)
9:55:1:19 (that is 9h, 55min, 1sec, 19 ms)
while what I need would look like
09:55:01:019
It’s no problem to make a conversion in Excel. I use
=VALUE(SUBSTITUTE(G2;":";",";3))
but I always get
09:55:01:190 (190ms!!)
Thus the milliseconds are always read like comma values, which is understandable from the software’s point of view.
I'd like a solution that either appends the correct values to the end of each row in a new column or directly changes the values in the original column (D) to the correct values. (Appending is OK because my other scripts work that way already!)
Can you help out really quickly?
https://www.dropbox.com/sh/3ch6ikddplnyjgg/vUfnVgbbzH here's an example file
With a value in A1 like:
0.413206238
the formula:
=SUBSTITUTE(TEXT(A1,"hh:mm:ss.000"),".",":")
will display:
09:55:01:019
EDIT#1:
Or if you want to convert the values in column D, in place. Select the cells and run this small macro:
Sub FFormat()
Dim r As Range, L As Long, U As Long
For Each r In Selection
ary = Split(r.Text, ":")
U = UBound(ary)
L = LBound(ary)
For i = L To U
If Len(ary(i)) = 1 Then
ary(i) = "0" & ary(i)
End If
Next i
If Len(ary(U)) = 2 Then
ary(U) = "0" & ary(U)
End If
r.Value = Join(ary, ":")
Next r
End Sub
If the original in R12 (my example) is text, you can enter this big formula: :)
=TEXT(LEFT(SUBSTITUTE(R12;":";REPT(" ";20));4);"00") & ":"&TEXT(MID(SUBSTITUTE(R12;":";REPT(" ";20));14;20);"00") & ":" & TEXT(MID(SUBSTITUTE(R12;":";REPT(" ";20));34;20);"00") & ":" & TEXT(MID(SUBSTITUTE(R12;":";REPT(" ";20));54;20);"000")
Depending on your regional settings you may need to replace field separator "; " by ","
Your data in column G has a leading space - this formula in a new column should convert to a valid time value whether you have leading spaces or not
=LEFT(TRIM(G2);FIND("^";SUBSTITUTE(TRIM(G2);":";"^";3))-1)+LOOKUP(1000;RIGHT(G2;{1;2;3})+0)/86400000
format as [h]:mm:ss.000
This will cope with single or double digit milliseconds, assumes that if there are no milliseconds you will still have third : followed by a zero. Also copes with single digit hours, minutes or seconds

Excel abnormally auto format my record from "xxxx/yy/zz" to "zz/yy/xxxx"

I debug in code, and I see that the value is in correct format, it just get and set the value only, and it seem like excel take the data as datetime instead of normal string,
I try to use NumberFormat and format it to either 1 of this (#, text, number, general) neither 1 is working, any help will be great
ActiveSheet.Range("D" & i).Value = arr(i, 4)
Update 1
Sorry to not mention arr, arr is multi dimension array, it store the column that I read from , I just get the value from excel, and the store in range by looping
Try this
ActiveSheet.Range("D1").Value = "'" & arr(1, 4)
I found the solution, although it look stupid
I change the format to
.NumberFormat = "yyyy/mm/dd"
and I get the value I want

Resources