I'm pulling in datetime information from SQL, converting it to a string then using substring to chop it in date, hour, and minute variables. In the database, the date is shown as MM/dd/YYYY, and the month/day always use two digits (such as 09/04/2016) but after the query it shows up in my application as (9/4/2016) which throws off my substring count. How do I get my query to keep the 0 in front when necessary?
Private Sub pullDate()
pulledDate = dgvTimeManager.SelectedItem.TimeIn.ToString
pulledDate = pulledDate.Substring(0, 10)
datepickerEdit.SelectedDate = Date.Parse(pulledDate)
End Sub
Private Sub pullStartTime()
pulledStartTime = dgvTimeManager.SelectedItem.TimeIn.ToString
If pulledStartTime.Length = 22 Then
pulledStartHour = pulledStartTime.Substring(11, 2)
pulledStartMinute = pulledStartTime.SubString(14, 2)
pulledStartAMPM = pulledStartTime.SubString(20, 2)
ElseIf pulledStartTime.Length = 21 Then
pulledStartHour = pulledStartTime.SubString(11, 1)
pulledStartMinute = pulledStartTime.SubString(13, 2)
pulledStartAMPM = pulledStartTime.SubString(19, 2)
End If
If pulledStartAMPM = "AM" Then
comboStartEditAMPM.SelectedIndex = 0
ElseIf pulledStartAMPM = "PM" Then
comboStartEditAMPM.SelectedIndex = 1
End If
txtStartHrEdit.Text = pulledStartHour
txtStartMinEdit.Text = pulledStartMinute
End Sub
dgvtimemanger is the datagrid that's displaying my time table, the starttime.length conditions are to take into account the hour being displayed as one digit or two (the same thing i'm dealing with now for the date)
Instead of converting to a string use the properties on the DateTime object. For example:
pulledStartHour = pulledStartTime.Hour.ToString("00")
will return the hour (in 24 hour format) which you then convert to a string.
You could also use .ToString(format) instead.
pulledStartHour = pulledStartTime.ToString("hh")
pulledStartAMPM = pulledStartTime.ToString("tt")
Which gives you the hours (in 12 hour format) and the AM/PM.
You can also combine the string foramts:
pulledStartTime.ToString("hh:mm tt")
https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx
You're spending way too much effort working with strings. Use the DateTime value you're given, and this code gets much simpler:
Private Sub pullDate()
datepickerEdit.SelectedDate = dgvTimeManager.SelectedItem.TimeIn.Date
End Sub
Private Sub pullStartTime()
Dim pulledStartTime As DateTime = dgvTimeManager.SelectedItem.TimeIn
txtStartHrEdit.Text = pulledStartTime.ToString("hh")
txtStartMinEdit.Text = pulledStartTime.ToString("mm")
comboStartEditAMPM.SelectedIndex = If(pulledStartTime.Hour >= 12, 1, 0)
End Sub
Additionally, I'd tend to write these kind of methods to accept an argument:
Private Sub SetStartTime(DateTime timeToSet)
datepickerEdit.SelectedDate = timeToSet.Date
txtStartHrEdit.Text = timeToSet.ToString("hh")
txtStartMinEdit.Text = timeToSet.ToString("mm")
comboStartEditAMPM.SelectedIndex = If(timeToSet.Hour >= 12, 1, 0)
End Sub
And then call them like this:
SetStartTime(dgvTimeManager.SelectedItem.TimeIn)
Note how the first one is so simple it's not even worth building the method anymore, and so I instead include the code as part of setting the time.
Related
I am trying to make a small helper app to assist in reading SCCM logs. Parsing the dates has been pretty straightforward until I get to the timezone offset. It is usually in the form of "+???". literal example: "11-01-2016 11:44:25.630+480"
DateTime.parse() handles this well most of the time. But occasionally I run into a time stamp that throws an exception. I cannot figure out why. This is where I need help. See example code below:
Dim dateA As DateTime = Nothing
Dim dateB As DateTime = Nothing
Dim dateStr_A As String = "11-07-2016 16:43:51.541+600"
Dim dateStr_B As String = "11-01-2016 11:44:25.630+480"
dateA = DateTime.Parse(dateStr_A)
dateB = DateTime.Parse(dateStr_B)
MsgBox(dateA.ToString & vbCrLf & dateB.ToString)
IF run it would seem dateStr_B is an invalid time stamp? Why is this? I've tried to figure out how to handle the +480 using the 'zzz' using .ParseExact() format as shown here Date Formatting MSDN
Am I missing something with the timezone offset? I've searched high and low but these SCCM logs seem to use a non standard way of representing the offset. Any insight would be greatly appreciated
The problem is that +480 is indeed an invalid offset. The format of the offset from UTC (as produced when using the "zzz" Custom Format Specifier) is hours and minutes. +600 is 6 hours and 0 minutes ahead of UTC, which is valid. +480 would be 4 hours and 80 minutes ahead of UTC, which is invalid as the number of minutes can't be more than 59.
If you have some external source of date and time strings that uses an offset that is simply a number of minutes (i.e. +600 means 10 hours and +480 means 8 hours), you will need to adjust the offset before using DateTime.Parse or DateTime.ParseExact.
[Edit]
The following function takes a timestamp with a positive or negative offset (of any number of digits) in minutes, and returns a DateTime. It throws an ArgumentException if the timestamp is not in a valid format.
Public Function DateTimeFromSCCM(ByVal ts As String) As DateTime
Dim pos As Integer = ts.LastIndexOfAny({"+"c, "-"c})
If pos < 0 Then Throw New ArgumentException("Timestamp must contain a timezone offset", "ts")
Dim offset As Integer
If Not Integer.TryParse(ts.Substring(pos + 1), offset) Then
Throw New ArgumentException("Timezone offset is not numeric", "ts")
End If
Dim hours As Integer = offset \ 60
Dim minutes As Integer = offset Mod 60
Dim timestamp As String = ts.Substring(0, pos + 1) & hours.ToString & minutes.ToString("00")
Dim result As DateTime
If Not DateTime.TryParse(timestamp, result) Then
Throw New ArgumentException("Invalid timestamp", "ts")
End If
Return result
End Function
Thank you for the insight. I had a feeling I would need to handle this manually. I just wanted to make sure I wasn't missing something simple in the process. My knowledge of the date and time formatting is a bit lacking.
As such, I have altered my code so that it handles the offset. Granted I will have to add some more input validation in the final product.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim dateA As DateTime = Nothing
Dim dateB As DateTime = Nothing
Dim dateStr_A As String = correctOffset("11-07-2016 16:43:51.541+600")
Dim dateStr_B As String = correctOffset("11-07-2016 16:43:51.541+480")
dateA = DateTime.Parse(dateStr_A)
dateB = DateTime.Parse(dateStr_B)
MsgBox(dateA.ToString & vbCrLf & dateB.ToString)
End Sub
Public Function correctOffset(ByVal ts As String)
Dim offset As Integer = CInt(ts.Substring(ts.Length - 3))
Dim offHour As Integer = offset / 60
Dim offMin As Integer = offset - (offHour * 60)
Dim strhour As String = Nothing
Dim strmin As String = Nothing
If offHour <= 9 Then
strhour = "0" & CStr(offHour)
Else
strhour = CStr(offHour)
End If
If offMin <= 9 Then
strmin = "0" & CStr(offMin)
Else
strmin = CStr(offMin)
End If
Return ts.Substring(0, ts.Length - 3) & strhour & ":" & strmin
End Function
I have a Data Sheet form which has a calculated field column. However the field will not display even though it has the correct value. The field in question is "numRisk":
Sub Calculate_Risk (Form As Object)
Dim OrderPrice, IfDonePrice, TotBrSymComm, BrComm, Risk As Double
Dim Symbol As String
Dim IntRateMult, noContracts As Integer
If MinTick = 0 OR Rate = 0 Then
Exit Sub
End If
Symbol = RTrim(Form.getByName("txtSymbol").CurrentValue)
If Symbol = "" Then
Exit Sub
End If
OrderPrice = Form.getByName("fmtOrder_Price").CurrentValue
IfDonePrice = Form.getByName("fmtIf_Done_Price").CurrentValue
noContracts = Form.getByName("fmtNo_Contracts").CurrentValue
If NOT USIntRates Then
Risk = ABS(OrderPrice - IfDonePrice) / MinTick
Else
Risk = ABS(OrderPrice\1 - IfDonePrice\1) * MinTick
IntRateMult = IIf(Symbol = "FV" OR Symbol = "TU",400, 200)
Risk = ABS(Risk - IntRateMult * ABS(OrderPrice - OrderPrice\1
IfDonePrice + IfDonePrice\1))
End If
Risk = Risk * MinTickVal / Rate
TotBrSymComm = BrSymComm + BrSymCommAud
BrComm = IIf(TotBrSymComm = 0, BrCommission, BrSymCommAud + BrSymComm/Rate)
Risk = noContracts*(Risk + BrComm * 2)
Form.getByName("numRisk").Value = Risk
End Sub
The subroutine is called from the following routine which is triggered when the form is loaded:
Sub FromListForm(Event as Object)
Dim Form As Object
Dim TodaysDate As New com.sun.star.util.Date
Dim CurrDate As Date
Form=Event.Source.getByName("MainForm_Grid")
Form.RowSet.first()
Do Until Form.RowSet.isAfterLast()
Get_Contract(Form)
Get_Broker_Comm(Form)
Calculate_Risk(Form)
If isEmpty(Form.getByName("OrderDate").Date) Then
CurrDate = Date()
TodaysDate.Day = Day(CurrDate)
TodaysDate.Month = Month(CurrDate)
TodaysDate.Year = Year(CurrDate)
Form.getByName("OrderDate").CurrentValue = TodaysDate
End If
Form.RowSet.next()
Loop
Form.RowSet.last()
End Sub
Also is there a more efficient method to cycle through the rows? As this seems so slow I can see the row pointer moving down the table as each row is processed.
If I understand correctly, you're trying to enter individual values into each cell in a column of a tablegrid control? I don't believe that's possible.
Inside a tablegrid control, all values have to come from the underlying query. I recommend writing a query to do these calculations, and using that query as the basis for the form - that would solve both the problem of displaying the calculated result as well as improving the load speed of the form (since database logic in determining query results is almost always more efficient than a macro going row-by-row).
Alternately, you could have the calculated field be standalone, showing only the calculated result for the currently selected row of the tablegrid control. In this scenario, the "form loaded" event would only do the calculation for the first row, and the calculating macro would be triggered each time the row selection changed.
I am having problems with my syntax below. I keep on getting Run Time error.
Please advise.
Dim a As Date Dim b As Date Dim c As Double
If Val(txtStart.Text) = Null Or Val(txtEnd.Text) = Null Then
Cells(eRow, 6).Value = txtEffort.Value
Else
a = Format(txtStart.Text, "hh:mm AMPM")
b = Format(txtEnd.Text, "hh:mm AMPM")
c = Abs(b - a) * 24
Cells(eRow, 6).Value = c
End If`
Your prompt response is much appreciated! Thanks!
You cannot throw text that looks like a date, time or datetime into the Format function without converting them to real dates.
a = Format(CDate(txtStart.Text), "hh:mm AMPM")
b = Format(CDate(txtEnd.Text), "hh:mm AMPM")
You could also use TimeValue for the time. The DateValue function will only see the date portion of the text string. CDate sees both the date and time.
Addendum:
You will have the same problem with c = Abs(b - a) * 24. The output of the Format function is text so both a & b will now be text representations of time. You need to convert them back to doubles representing time with c = Abs(TimeValue(b) - TimeValue(a)) * 24.
You haven't provided much in the way of detail. There is probably a better way to do this.
Judging by the code posted, time is being compared.
The problem arises because Abs expects a number, but in fact, a string is subtracted from a string, which causes an exception to throw.
If a and b are string variables holding values like "16:00" that are later formatted into "4:00", I suggest implementing a function to calculate the time in minutes, and then their difference.
Here is the function:
Function ConvertToMinutes(ByVal time_str As String) As Integer
ConvertToMinutes = 0
On Error GoTo Ende
splts = Split(time_str, ":")
ConvertToMinutes = CInt(splts(0)) * 60 + CInt(splts(1))
Ende:
On Error GoTo 0
End Function
Then, the code will look like
a = Trim(Format(txtStart.Text, "hh:mm AMPM"))
b = Trim(Format(txtEnd.Text, "hh:mm AMPM"))
C = Abs(ConvertToMinutes(b) - ConvertToMinutes(a)) * 24
Please adjust the calculations so that you get the required result.
So if I have lets say 10 textboxes I need to fill I have to repeat a loop 10 times and each time add to a different text box. Right now I have something like this:
If i = 0 Then
Shift0 = endTime - startTime
textStart0.text = startTime
textEnd0.text = endTime
chkBox0.checked = True
End If
I have I repeating like that 8 more times to make 9. I want to make it so that the loop would increase the number from 0-9 every time it goes through
If i = (x) Then
Shift(x) = endTime - startTime
textStart(x).text = startTime
textEnd(x).text = endTime
chkBox(x).checked = True
End If
x = x + 1
How can I put it in the loop so that the number in the name of the object increased with every loop?
Control arrays are a thing of the past, from the VB6 days, unfortunately, as you've discovered, they can still have their uses!
Try this for your loop;
For i = 0 to 9
Shift0 = endTime - startTime ' Is Shift0 a control!?
FindControl("textStart" & i).Text = startTime
FindControl("textEnd" & i).Text = endTime
FindControl("chkBox" & i).Checked = True
Next
With this function to help...
Private Function FindControl(pName As String) As Control
Dim vMatches = Me.Controls.Find(pName, True)
If vMatches IsNot Nothing AndAlso vMatches.Length > 0 Then Return vMatches(0)
Throw New Exception("Could not find the specified control!")
End Function
Having said all that, I would strongly recommend re-thinking how your form and application work to avoid this!
Something like this would work
For x = 0 to 9
Shift(x) = endTime - startTime
textStart(x).text = startTime
textEnd(x).text = endTime
chkBox(x).checked = True
next x
You can use the Controls property of a Control with an index. If your form contains exactly and only 10 textboxes, this will work fine:
For i as Integer = 1 to 10
Form1.Controls(i).Text = "Box " + i.ToString()
Next
If you have other controls in the form you have no guarantee over the index (you can't reply on 1 to 10 being the textboxes as your design progresses). Therefore, I'd recommend you put them inside a panel and refer to this panel's Controls:
For i as Integer = 1 to 10
Panel1.Controls(i).Text = "Box " + i.ToString()
Next
To learn more about loops in VB.NET, start here: http://www.tutorialspoint.com/vb.net/vb.net_loops.htm
I'm writing a script that will count a numbers of days between few separate dates. I have a data in cell like:
1-In Progress#02-ASSIGNED TO TEAM#22/01/2013 14:54:23,4-On
Hold#02-ASSIGNED TO TEAM#18/01/2013 16:02:03,1-In Progress#02-ASSIGNED
TO TEAM#18/01/2013 16:02:03
That's the info about my transaction status. I want to count the numbers of days that this transaction was in "4-On Hold". So in this example it will be between 18/01/2013 and 22/01/2013.
I wrote something like this(sorry for ma native language words in text)
Sub Aktywnywiersz()
Dim wiersz, i, licz As Integer
Dim tekstwsadowy As String
Dim koniectekstu As String
Dim pozostalytekst As String
Dim dataztekstu As Date
Dim status4jest As Boolean
Dim status4byl As Boolean
Dim datarozpoczecia4 As Date
Dim datazakonczenia4 As Date
Dim dniw4 As Long
wiersz = 2 'I start my scrypt from second row of excel
Do Until IsEmpty(Cells(wiersz, "A")) 'this should work until there is any text in a row
status4jest = False 'is status 4-On Hold is now in a Loop
status4byl = False 'is status 4-On Hold was in las loop
dniw4 = 0 ' numbers od days in 4-On Hold status
tekstwsadowy = Cells(wiersz, "H").Value2 'grabing text
tekstwsadowy = dodanieprzecinka(tekstwsadowy) 'in some examples I had to add a coma at the end of text
For i = 1 To Len(tekstwsadowy)
If Right(Left(tekstwsadowy, i), 1) = "," Then licz = licz + 1 'count the number of comas in text that separates the changes in status
Next
For j = 1 To licz
koniectekstu = funkcjaliczeniadni(tekstwsadowy) 'take last record after coma
Cells(wiersz, "k") = koniectekstu
dataztekstu = funkcjadataztekstu(koniectekstu) 'take the date from this record
Cells(wiersz, "m") = dataztekstu
status4jest = funkcjaokreslenia4(koniectekstu) 'check if there is 4-On Hold in record
Cells(wiersz, "n") = status4jest
If (status4byl = False And staus4jest = True) Then
datarozpoczecia4 = dataztekstu
status4byl = True
ElseIf (status4byl = True And staus4jest = False) Then
datazakonczenia4 = dataztekstu
status4byl = False 'if elseif funkcion to check information about 4-On Hold
dniw4 = funkcjaobliczeniadniw4(dniw4, datazakonczenia4, datarozpoczecia4) 'count days in 4-On Hold
Else
'Else not needed...
End If
tekstwsadowy = resztatekstu(tekstwsadowy, koniectekstu) 'remove last record from main text
Next
Cells(wiersz, "L") = dniw4 ' show number of days in 4-On Hold status
wiersz = wiersz + 1
Loop
End Sub
Function funkcjaliczeniadni(tekstwsadowy As String)
Dim a, dl As Integer
dl = Len(tekstwsadowy)
a = 0
On Error GoTo errhandler:
Do Until a > dl
a = Application.WorksheetFunction.Find(",", tekstwsadowy, a + 1)
Loop
funkcjaliczeniadni = tekstwsadowy
Exit Function
errhandler:
funkcjaliczeniadni = Right(tekstwsadowy, dl - a)
End Function
Function dodanieprzecinka(tekstwsadowy As String)
If Right(tekstwsadowy, 1) = "," Then
dodanieprzecinka = Left(tekstwsadowy, Len(tekstwsadowy) - 1)
Else
dodanieprzecinka = tekstwsadowy
End If
End Function
Function resztatekstu(tekstwsadowy, koniectekstu As String)
resztatekstu = Left(tekstwsadowy, Len(tekstwsadowy) - Len(koniectekstu))
End Function
Function funkcjadataztekstu(koniectekstu As String)
funkcjadataztekstu = Right(koniectekstu, 19)
funkcjadataztekstu = Left(funkcjadataztekstu, 10)
End Function
Function funkcjaobliczeniadniw4(dniw4 As Long, datazakonczenia4 As Date, datarozpoczecia4 As Date)
Dim liczbadni As Integer
liczbadni = DateDiff(d, datarozpoczecia4, datazakonczenia4)
funkcjaobliczaniadniw4 = dniw4 + liczbadni
End Function
Function funkcjaokreslenia4(koniectekstu As String)
Dim pierwszyznak As String
pierwszyznak = "4"
If pierszyznak Like Left(koniectekstu, 1) Then
funkcjaokreslenia4 = True
Else
funkcjaokreslenia4 = False
End If
End Function
And for now I get
Run-time error "13"
in
dataztekstu = funkcjadataztekstu(koniectekstu) 'take the date from this record
I would be very grateful for any help.
You are getting that error because of Type Mismatch. dataztekstu is declared as a date and most probably the expression which is being returned by the function funkcjadataztekstu is not a date. You will have to step through it to find what value you are getting in return.
Here is a simple example to replicate that problem
This will give you that error
Option Explicit
Sub Sample()
Dim dt As String
Dim D As Date
dt = "Blah Blah"
D = getdate(dt)
Debug.Print D
End Sub
Function getdate(dd As String)
getdate = dd
End Function
This won't
Option Explicit
Sub Sample()
Dim dt As String
Dim D As Date
dt = "12/12/2014"
D = getdate(dt)
Debug.Print D
End Sub
Function getdate(dd As String)
getdate = dd
End Function
If you change your function to this
Function funkcjadataztekstu(koniectekstu As String)
Dim temp As String
temp = Right(koniectekstu, 19)
temp = Left(temp, 10)
MsgBox temp '<~~ This will tell you if you are getting a valid date in return
funkcjadataztekstu = temp
End Function
Then you can see what that function is returning.
I tried running your code, but it is a little difficult to understand just what it is that you want to do. Part of it is the code in your language, but the code is also hard to read beacuse of the lack of indentation etc. :)
Also, I do not understand how the data in the worksheet looks. I did get it running by guessing, though, and when I did I got the same error you are describing on the second run of the For loop - that was because the koniectekstu string was empty. Not sure if this is your problem, so my solution is a very general.
In order to solve this type of problem:
Use Option Explicit at the top of your code module. This will make you have to declare all variables used in the module, and you will remove many of the problems you have before you run the code. Eg you are declaring a variable status4jest but using a different variable called staus4jest and Excel will not complain unless you use Option Explicit.
Declare return types for your functions.
Format your code so it will be easier to read. Use space before and after statements. Comment everything! You have done some, but make sure a beginner can understand. I will edit you code as an example of indentation.
Debug! Step through your code using F8 and make sure all variables contain what you think they do. You will most likely solve your problem by debugging the code this way.
Ask for help here on specific problems you run into or how to solve specific problems, do not send all the code and ask why it is not working. If you break down your problems into parts and ask separately, you will learn VBA yourself a lot faster.
A specific tip regarding your code: look up the Split function. It can take a string and make an array based on a delimiter - Example: Split(tekstwsadowy, ",") will give you an array of strings, with the text between the commas.
Did I mention Option Explicit? ;)
Anyway, I hope this helps, even if I did not solve the exact error you are getting.