I am creating an Excel file that helps people to automatically process data. Within this sheet, there are numbers that have to be parsed (and converted to other units, etc. Simple calculations).
The numbers come in US format which means they look like:
0,000,000.00
In Germany, numbers are displayed like this:
0.000.000,00
In VBA, I actually don't know, which localized Version of Excel is in use (German or English).
Question:
Is there an easy way to parse the US format numbers into data type Double regardless of the Excel localization, that is used by the user?
Some notes. I did not change locale to test.
'http://msdn.microsoft.com/en-us/library/office/bb177675(v=office.12).aspx
Debug.Print Application.International(xlCountryCode) 'Excel locale
Debug.Print Application.International(xlCountrySetting) 'Windows locale
sDecimal = Application.International(xlDecimalSeparator)
sThousand = Application.International(xlThousandsSeparator)
sNumber = "1,000,000.00"
If sThousand <> "," Then
If sDecimal <> "." Then
sNumber = Replace(sNumber, ",", "")
sNumber = Replace(sNumber, ".", sDecimal)
End If
End If
Debug.Print sNumber
A possible solution may be:
cells(y, x) = Val(Replace(cells(y, x).Text, ",", ""))
Val always uses the . as decimal mark (CDbl for example uses cultural settings, tested in a German Excel).
Related
I've written an app which brings in a CSV export from our HR system, loops through all the records and applies the values from the HR system to active directory.
It works a treat, and when running on my machine i get no errors whatsoever.
When running it on one of our servers, where it is ultimately going to live and will be executed by a service account, I get date conversion errors...
System.InvalidCastException: Conversion from string "21/08/2020" to type 'Date' is not valid.
Right at the start of my code I'm defining the region...
Dim ukCulture = New Globalization.CultureInfo("en-GB")
System.Threading.Thread.CurrentThread.CurrentCulture = ukCulture
And if I query current culture at runtime, it shows 'en-GB', so that seems right.
If i write out the date strings, they all look right, and the compare operation is working fine.
The error seems to occur in this section of code...
Dim converted_hr_accountexpiry_timestamp= hr_row(0).Item("Termination Date") & ""
Dim hr_termdate_var() As String = converted_hr_accountexpiry_timestamp.split("/")
updatescript = updatescript.Replace("$x", "'" & hr_termdate_var(0) & "'") _
.Replace("$y", "'" & hr_termdate_var(1) & "'") _
.Replace("$z", "'" & hr_termdate_var(2) & "'")
So for context, this code is building up a powershell script which is executed to make the necessary changes in AD.
The section of that powershell code that we're looking at here is this...
$server = "MyPrimaryDNSServer.FQDN"
$exp = get-date -Day $x -Month $y -Year $z -Hour 00 -Minute 00 -Second 00
$expirydate = $exp.ToUniversalTime().AddDays(1)
It seems clear that its trying to use a US date format, because if the date provided would match an acceptable US date, ie 3/5/2020, then it will accept it and the wrong date will be applied. The error is only thrown when the day (dd) portion of the date would not be accepted as MM on an american format date, ie 31/07/2020.
And to re-iterate; this issue doesnt happen on my machine, only on the server that will eventually execute the application. I've been through all the region settings on that device itself and everything is set to united kingdom, with the correct dd/MM/yyyy formats for dates.
I'm at a total loss on this one and pulling out what little hair i have left.
Any suggestions/help appriciated!
EDIT 1:
This is the full exception, minus the users name obvs...
Error with account : Joe Bloggs (1010245)
System.InvalidCastException: Conversion from string "24/07/2020" to type 'Date' is not valid.
at Microsoft.VisualBasic.CompilerServices.Conversions.ToDate(String Value)
at Microsoft.VisualBasic.CompilerServices.Operators.CompareObject2(Object Left, Object Right, Boolean TextCompare)
at Microsoft.VisualBasic.CompilerServices.Operators.CompareObjectEqual(Object Left, Object Right, Boolean TextCompare)
at Atlas.Main.GetAccountsWithUpdates()
EDIT 2:
So it looks like there are two errors occuring, which is why I couldnt find it by commenting each related line out in turn.
This is definately one of the erroring lines...
converted_ad_expiry_timestamp = converted_ad_expiry_timestamp.ToString("dd/MM/yyyy").Split(" ")(0)
The value returned is a datetime not a date, so i use tostring and split it on the space to grab just the date portion.
The second error seems to occur in here...
If Not (converted_hr_expiry_timestamp = converted_ad_expiry_timestamp) Then updateme = True : If (hr_row(0).Item("Termination Date")) = "" Then account_expiration_date = "$null" Else account_expiration_date = converted_hr_expiry_timestamp
It looks like the problem revolves around converted_ad_expiry_timestamp which seems to be a Date (the VB type is an alias for a .NET DateTime). You put it into a specific localized format via converted_ad_expiry_timestamp.ToString("dd/MM/yyyy") and then rely on automated conversion to turn it back into a Date. The way to avoid the problem you see here (where the automated conversion uses the system locale to decide the format) is to use one of the Parse or ParseExact family to control the conversion yourself. With Parse, you can specify the locale to use, or with ParseExact you can specify the format.
Similarly, when you attempt to compare them in If Not (converted_hr_expiry_timestamp = converted_ad_expiry_timestamp) Then ..., the first item in the comparison is a string; if you want to do this comparison, you need to either use ToString on the ad_expiry or parse the hr_expiry into a Date.
I would also recommend using Option Strict if you can to turn these implicit conversions into errors, or if that would introduce to many issues, at least turn on the warning for implicit conversions.
I know this is just working around the problem, and will stop it working locally for you (or rather move the problem to your machine), but if it's only ever going to be run on that server can you not just swap X and Y values?
Dim converted_hr_accountexpiry_timestamp= hr_row(0).Item("Termination Date") & ""
Dim hr_termdate_var() As String = converted_hr_accountexpiry_timestamp.split("/")
updatescript = updatescript.Replace("$x", "'" & hr_termdate_var(1) & "'") _
.Replace("$y", "'" & hr_termdate_var(0) & "'") _
.Replace("$z", "'" & hr_termdate_var(2) & "'")
Alternatively, if you want it to work on both systems then maybe a check before running the offending code is in order, something like this has worked for me in the past:
Try
Dim TempTimeString As String = "31/01/2020 01:00 AM"
Dim ConvertedTime As Date
ConvertedTime = DateTime.Parse(TempTimeString)
'''Don't swap X and Y as it was able to convert
Catch ex As Exception
'''swap X and Y as it was unable to convert
End Try
I've created a set of macro files in Microsoft Word's VBA as a sort of a CAT tool (CAT = https://en.wikipedia.org/wiki/Computer-assisted_translation). The problem is that there are cases where I display the text needed to be translated and the user needs to input text in his own language. That might include some special chars, like "ăîâșț/ĂÎÂȘȚ", or even quotes or brackets. Is there any way to use those in some InputBox function? Or, at least, some way to let the user input the text he needs in some TextBox or something?... Or how should I approach this?... Maybe UTF-8 support would be what I need? Or?... Any help would be appreciated!...
I've tried Microsoft Word's vba function InputBox. I'm also thinking if, maybe, I would be able to create my own InputBox, with my conditions on it, I might be able to have one that accepts those chars too, or all the chars into some string variable... Here is something someone on StackOverflow says:
Is it possible to create an 'input box' in VBA that can take a text selection with multiple lines as an input? (I'm referring to gizlmo's answer...)
Here are 3 lines of code that contain that (although it's more of a how to question, not a debugging question, so those are not really needed...)
MsgBox ("Ziua " & Str(ziua) & " - " & titlurien(ziua))
titluales = InputBox("Titlul original: " & titlurien(ziua), "Ziua: " & Str(ziua) & ", Rapsodia Realitatilor " & monthname(lunanecesara) & Str(annecesar))
titluriro(ziua) = titluales
I expect the output to be exactly what he typed, whether it's quotes, brackets or special characters (like "ăîâșț"/"ĂÎÂȘȚ")...
A VBA InputBox will take any character typed or pasted into it. The characters available to type depends on the Language version of Windows and Office that the end user has installed.
Below is a test I just made with your example character string "ăîâșț/ĂÎÂȘȚ"
SpecialCharInput()
Dim str As String
str = InputBox("Enter you text", "Special Test Input Box")
Debug.Print str
End Sub
On my English language system, the only trouble it had was with the upper and lower case "ȘȚ" Turkish characters. By trouble I mean it turned those characters into question marks "??" in the result string. I'm sure though, if my system supported the Turkish language that those characters would be recognized and outputted properly.
I've been searching for two days to solve my issue but so far nothing.
There are many (Very many) vba excel tools developed where I work and our regional settings in PC-s determine comma as decimal separator but reports and data downloaded from our systems have dot as decimal separator. In these tools, when needed we just have set UseSystemSeparators = False then DecimalSeparators = "." and at the end of the macro reverted back.
Now new people have been getting windows 10 pcs and some of the tools run into errors. I got a Win10 pcs from IT to test and found out that no matter how I set in Excel settings VBA Macro uses PC regional settings while on the sheet it still uses what is determined in Excel settings. Same file, same test in my win7 pc and if set then both on sheet and vba macro use local application settings.
Does anyone know what is the reason and how it could be fixed? I can figure many workarounds but all of these mean that the tools need to be re-coded and there are just so many of these that I still keep finding out new tools used and what were developed before I joined the company in March. Changing all ~300 PC-s regional setting is not an option because it needs to be comma as decimal separator normally.
Edit: just to make it bit more clear I'll add some code:
Sub test()
Application.UseSystemSeparators = False
Application.DecimalSeparator = "."
variable = "10.1"
MsgBox CDbl(variable)
End Sub
Under Windows 7 - no problem. The variable containing a string can be converted.
Under Windows 10 - Run-time error, Type mismatch. Both PCs have "," as system separator. The funny thing is that when I change "10.1" to "10,1" then that works on both PC-s. Settings for both PC-s are same. System uses "," as separator and Excel is set to use ".".
This answer is based on your system (default) DecimalSeparator being a comma (,) and ThousandsSeparator being a point (.) - using Windows 10
Explanation:
I've done a little testing and found that the following functions only change the DecimalSeparator within Excel, not within VBA:
Application.UseSystemSeparators = False
Application.DecimalSeparator = "."
Application.ThousandsSeparator = ","
This means that when you change the DecimalSeparator by using these lines of code, it will simply update the values in your workbooks to be displayed in the format you are specifying. For example if your system format is:
100.000.000,99
Then the lines of code above would convert the values to:
100,000,000.99
However, when you run the code above and try to use a string that uses the non-system format for the DecimalSeparator then VBA will not recognise it.
Example:
(Where Application.DecimalSeparator = "," when Application.UseSystemSeparators = True)
Sub TestSeparator()
Application.UseSystemSeparators = False
Application.DecimalSeparator = "."
Application.ThousandsSeparator = ","
Debug.Print CDbl("100.99")
End Sub
This code will return 10099, not the expected double of 100,99 (in your system format) because VBA does not see the DecimalSeparator of "." as a DecimalSeparator.
Solution:
When referencing doubles within VBA you will need to always use the computer's system separators for thousands and decimals. If the doubles are being imported as strings you will need to use Replace to convert the string into a format that uses system separators.
Solution Example:
(Where Application.DecimalSeparator = "," when Application.UseSystemSeparators = True)
If you have the string of "100,000,000.99" in cell A1 and use the following code:
CDbl(Range("A1").Value)
It will produce a "Type mismatch" error. However if you convert this into a string that uses your system separators then no error will be given. You will need to use two Replace functions though, one to remove the ThousandsSeparators and one to convert the DecimalSeparators to your system ones:
CDbl(Replace(Replace(Range("A1").Value, ",", ""), ".", ",")
Perhaps this:
Set ws = ThisWorkbook.Worksheets("Name of Worksheet")
ws.Cells.Replace What:="", Replacement:="", LookAt:=xlPart, _ SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=True, ReplaceFormat:=True
Set ws = Nothing
Decimal numbers (in our regional settings, a comma is the decimal symbol) are changed to semicolons when used in formula.
Let's say I have a number (I actually just parse it from a field but I need it in a variable):
c = 10,5
If I do:
sheet.Cells(1,1).Formula = "=SUM(" & c & ",10)"
The whole formula becomes:
=SUM(10;5;10)
The comma is always changed to a semi-colon which ruins the double number.
I can use .FormulaLocal and then the semi-colon separator works in the formula but the comma still works too so that does not solve it.
Changing the regional settings does not work since I need to distribute it and won't be able to get everyone to change the regional settings.
Anything that could save me from this?
if c i as string containing a double value in local format, you can use:
sheet.Cells(1,1).Formula = "=SUM(" & Str(CDbl(c)) & ",10)"
CDblwill convert the value from the locale string to the Double format, and Str will make a String of it using . as decimal separator.
You can also replace the DecimalSeparator directly:
sheet.Cells(1,1).Formula = "=SUM(" & Replace(c, Application.DecimalSeparator, ".") & ",10)"
I run different scripts using VBA on my German computer with an English Excel 2013 version installed on it. My local decimal settings are set to be international, e.g. 123123 is displayed as 123,123.00.
However, when I program with VBA, the decimal changes.
For example:
sub decimal_problem()
dim sDecSep as string
dim sThSep as string
sDecSep = Application.International(xlDecimalSeparator) ' sDecSep = "."
sThSep = Application.International(xlThousandsSeparator) ' sThSep = ","
Fmt= "#" & sThSep & "##0" & sDecSep & "00"
'Fmt looks like "#,##0.00" which is what I want
Msgbox(Format(123123,Fmt)) 'Fmt="#,##0.00"
end sub
The number in the Msgbox is 123.123,00 which is the German number format and definitely not the format I specified before.
When I change the format in a Spreadsheet I get the correct separators (e.g. "#,##0.00" returns 123,123.00).
Do you have a solution for that issue?
As I need to calculate with the formatted number, I try to avoid to change the number to a string and use the replace function.
Thanks for the help!
The code you mentioned above is showing the output you wanted that is 123,123.00 and not 123.123,00.
Also as soon as you will assign this value to a range in a sheet this Text value will get converted to numeric automatically. for ex - thisworkbook.sheets(1).range("a1").value = Format(123123, Fmt)--This will assign cell a1 walue as 123,123.00 which will be in numeric
please let me know if I have not understood your scenario.