Excel, VB - Serialize an 8 digit date to mm/dd/yy - date

ISSUE
I am trying to convert a 8 digit number into a date while in an array. Examples of the entries are 12282009 or 12202007. There are other malformed entries in the field including dates entered as strings. I want the 8 digit number to be formatted as 12/28/09 or 12/20/07 respectively. I keep getting a type mismatch error on the third to last line below. How do I do this??
CODE
Dim del()
ReDim del(1 To importwsRowCount, 1 To 1)
del = Range("AH1:AH" & importwsRowCount).Value
Dim delChars As Long
Dim delType As String
For i = LBound(del, 1) To UBound(del, 1)
delChars = Len(del(i, 1)) 'Determine length of entry
If IsNumeric(del(i, 1)) = True Then 'Determine datatype of entry
delType = "Numeric"
del(i, 1) = Abs(del(i, 1))
Else
delType = "String"
del(i, 1) = UCase(del(i, 1))
End If
If delType = "Numeric" Then
If delChars = 8 Then
del(i, 1) = DateSerial((Right(del(i, 1), 4)), (Left(del(i, 1), 2)), (Mid(del(i, 1), 3, 2))) '<-- TYPE MISMATCH ERROR
End If
End If
ENTRY TEMPLATES
SEPT. 25, 20 (No year, no year! Delete.)
SEPT (No year, useless, delete.)
N/A (Rubbish! Deleted.)
LONG TIME AG (What moron thought this was a good idea, delete.)
JUNE 30, 200 (Apparently the field will only hold 12 characters, delete.)
CHARGED OFF (Useless, delete.)
94 DAYS (Take all characters preceding space and subtract from other field containing order date to obtain delinquent date.)
94 DPD (DPD in someones bright mind stands for Days Past Due I believe. Same as above.)
2008-7-15 12 (Not sure what additional number is, take all characters before space and transform.)
INVALID (Delete.)
BLANK (Do nothing.)
4/2/4/09 (Malformed date, delete.)
1/1/009 (Same as above.)
12282009 (Use nested LEFT and RIGHT and CONCATENATE with / in between.)
9202011 (Add leading zero, then same as above.)
92410 (Add leading zero, this will transform to 09/24/10)
41261 (Days since 31/12/1899, this will transform to 12/08/12)
1023 (Days since delinquent, subtract from ORDER DATE to get delinquent date.)
452 (Same as above.)
12 (Same as above.)
1432.84 (Monetary value, mistakenly entered by low IQ lackey. Delete.)

Right(Left(del(i, 1), 2), 6) is nonsensical.
The Left(del(i, 1), 2) part happens first and returns a 2-character string. If you then apply Right(..., 6) to that 2-character string you get an error.
The Mid function is needed here: Mid(del(i, 1), 3, 2)
Running the Abs function earlier changed the array entry from being a Variant with subtype String to being a Variant with subtype Double. This shouldn't necessarily affect the Left/Mid/Right functions but try:
del(i, 1) = CStr(del(i, 1))
del(i, 1) = DateSerial((Right(del(i, 1), 4)), (Left(del(i, 1), 2)), (Mid(del(i, 1), 3, 2)))
We need to identify what the actual value causing the error is so:
If delType = "Numeric" Then
If delChars = 8 Then
On Error Goto DateMismatchError
del(i, 1) = DateSerial((Right(del(i, 1), 4)), (Left(del(i, 1), 2)), (Mid(del(i, 1), 3, 2))) '<-- TYPE MISMATCH ERROR
On Error Goto 0
End If
End If
' at the end of your Sub or Function - I'm assuming Sub here
Exit Sub
DateMismatchError:
MsgBox "Date mismatch: error number " & Err.Number & ", " & Err.Description & _
" caused by data value: |" & del(i, 1) & "| at row " & i & ". Original data " & _
"value is |" & Range("AH" & i).Value2 & "|, displayed value is |" & _
Range("AH" & i).Text & "|, number format is |" & Range("AH" & i).NumberFormat & "|"
End Sub

You can use this shorter code to replace your array elements with formatted dates
It cuts down the amount of testing inside the loop to two IFs. If numeric test is run first - there is no point running a longer lenint test for strings that are not 8 characters
The string functions Left$, Mid$ etc are much quicker than their variant cousins Left, Mid etc
I have made a substituion for your importwsRowCount variable in the code below
Updated code to handle and dump results, now handles string tests and non-compliantnumbers as per barrowc comments
The code below puts the new dates into a second array, skipping the invalid dates
The second array is then dumped at `AI``
Sub ReCut2()
Dim del()
Dim X()
Dim lngCnt As Long
del = Range("AH1:Ah10").Value2
ReDim X(1 To UBound(del, 1), 1 To UBound(del, 2))
Dim delChars As Long
Dim delType As String
For lngCnt = LBound(del, 1) To UBound(del, 1)
If IsNumeric(del(lngCnt, 1)) Then
If Len(Int((del(lngCnt, 1)))) = 8 Then X(lngCnt, 1) = DateSerial(Right$(del(lngCnt, 1), 4), Left$(del(lngCnt, 1), 2), Mid$(del(lngCnt, 1), 3, 2))
End If
Next
[ai1].Resize(UBound(X, 1), UBound(X, 2)).Value2 = X
End Sub

Related

Separator for five or more digits number in Tableau

I was working on a Tableau Project. We want to have a separator for five or more digits numbers.
For ex:-
1 as 1
12 as 12
123 as 123
1234 as 1234
12345 as 12,345
123456 as 1,23,456
Can you please assist me, how to achieve this?
I am nearly sure that this cannot be done as long as numbers are formatted as numbers. However, as a workaround, I have developed a method which however will convert numbers to string. Let's say you have a column col of desired numbers
copy your column say col2 (save original for future use) and convery type to string
Create a new calculated field say desired by using this calculation
If LEN([Col2]) <= 4 THEN
[Col2]
ELSEIF LEN([Col2]) < 6 THEN
REPLACE([Col2], RIGHT([Col2], 3), "") + "," +RIGHT([Col2], 3)
ELSEIF LEN([Col2]) <8 THEN
REPLACE([Col2], RIGHT([Col2], 5), "") + "," +
REPLACE(RIGHT([Col2],5), RIGHT([Col2], 3), "") + "," +RIGHT([Col2], 3)
ELSE
REPLACE([Col2], RIGHT([Col2], 7), "") + "," +
REPLACE(RIGHT([Col2],7), RIGHT([Col2], 5), "") + "," +
REPLACE(RIGHT([Col2],5), RIGHT([Col2], 3), "") + "," +RIGHT([Col2], 3)
END
this CF will work exactly as desired for upto 9 digits.
Alignment is not a big problem, if considered

Python: add zeroes in single digit numbers without using .zfill

Im currently using micropython and it does not have the .zfill method.
What Im trying to get is to get the YYMMDDhhmmss of the UTC.
The time that it gives me for example is
t = (2019, 10, 11, 3, 40, 8, 686538, None)
I'm able to access the ones that I need by using t[:6]. Now the problem is with the single digit numbers, the 3 and 8. I was able to get it to show 1910113408, but I need to get 19101034008 I would need to get the zeroes before those 2. I used
t = "".join(map(str,t))
t = t[2:]
So my idea was to iterate over t and then check if the number is less than 10. If it is. I will add zeroes in front of it, replacing the number . And this is what I came up with.
t = (2019, 1, 1, 2, 40, 0)
t = list(t)
for i in t:
if t[i] < 10:
t[i] = 0+t[i]
t[i] = t[i]
print(t)
However, this gives me IndexError: list index out of range
Please help, I'm pretty new to coding/python.
When you use
for i in t:
i is not index, each item.
>>> for i in t:
... print(i)
...
2019
10
11
3
40
8
686538
None
If you want to use index, do like following:
>>> for i, v in enumerate(t):
... print("{} is {}".format(i,v))
...
0 is 2019
1 is 10
2 is 11
3 is 3
4 is 40
5 is 8
6 is 686538
7 is None
another way to create '191011034008'
>>> t = (2019, 10, 11, 3, 40, 8, 686538, None)
>>> "".join(map(lambda x: "%02d" % x, t[:6]))
'20191011034008'
>>> "".join(map(lambda x: "%02d" % x, t[:6]))[2:]
'191011034008'
note that:
%02d add leading zero when argument is lower than 10 otherwise (greater or equal 10) use itself. So year is still 4digit string.
This lambda does not expect that argument is None.
I tested this code at https://micropython.org/unicorn/
edited :
str.format method version:
"".join(map(lambda x: "{:02d}".format(x), t[:6]))[2:]
or
"".join(map(lambda x: "{0:02d}".format(x), t[:6]))[2:]
second example's 0 is parameter index.
You can use parameter index if you want to specify it (ex: position mismatch between format-string and params, want to write same parameter multiple times...and so on) .
>>> print("arg 0: {0}, arg 2: {2}, arg 1: {1}, arg 0 again: {0}".format(1, 11, 111))
arg 0: 1, arg 2: 111, arg 1: 11, arg 0 again: 1
I'd recommend you to use Python's string formatting syntax.
>> t = (2019, 10, 11, 3, 40, 8, 686538, None)
>> r = ("%d%02d%02d%02d%02d%02d" % t[:-2])[2:]
>> print(r)
191011034008
Let's see what's going on here:
%d means "display a number"
%2d means "display a number, at least 2 digits"
%02d means "display a number, at least 2 digits, pad with zeroes"
so we're feeding all the relevant numbers, padding them as needed, and cut the "20" out of "2019".

VBScript function to convert hex to unicode

I have hexadecimal string which consists of different languages letters.
Please help me with a vb-script function which converts this hexadecimal string to Unicode text.
For hex string "506F7274756775C3AA73" , I need to get "Português" as output.
I tried following function, it gives "Português" as output.
MsgBox ConvertHexToUnicode("506F7274756775C3AA73")
Function ConvertHexToUnicode(hexString)
Dim Strlen
Dim Charaset_array(20)
Dim i
Dim j
Strlen = Len(hexString)
i = 0
j = 1
Do
Charaset_array(i) = Mid(hexString,j, 2)
i = i + 1
j = j + 2
Loop While j < Strlen
ConvertHexToUnicode = ""
For Each chara In Charaset_array
If Not(IsEmpty(chara)) Then
ConvertHexToUnicode = ConvertHexToUnicode + ChrW("&H" & chara )
End If
Next
End Function
Use Mid() to cut your input string into hex numbers (strings), prepend &H to get hex literals, and ChrW() to build characters:
>> s = "00001F00"
>> WScript.Echo Mid(s, 5, 4)
>> WScript.Echo "&H" & Mid(s, 5, 4), CLng("&H" & Mid(s, 5, 4))
>> WScript.Echo ChrW("&H" & Mid(s, 5, 4)), AscW(ChrW("&H" & Mid(s, 5, 4)))
>>
1F00
&H1F00 7936
ἀ 7936

macro to extract dates from a weeks date range string and add 7 days to print next date range

I am writing a macro that processes an excel with lots of data. One of the rows contains a date range like wkstartdate - wkenddate and I would like to use dateadd function to print next date range every week (like '27-01-14 - 02-02-14' in below case) but unable to do so.
'06-01-14 - 12-01-14'
'13-01-14 - 19-01-14'
'20-01-14 - 26-01-14'
I used below excerpt which fails:
Range("E" & Lastrow).Select
prwk = Split(ActiveCell.Value, "-")
'curr_wkstart = DateAdd("d", 7, prwk(1)) 'error as maybe prwk(1) isnt correct format
'curr_wkend = DateAdd("d", 7, prwk(2)) 'error
Range("E" & Lastrow + 1).Value = curr_wkstart & curr_wkend 'no result
For testing purpose I print, prwk(1) which is 20/01/14 in the above case, in a diff cell and add 7 days, which gives me 1/21/2020 instead of '27/01/14'. I also tried using Cdate function, but still error
Can you please advise??
I think what you want to use here are the Format and DateSerial functions. Here's how I came at it:
Function GetNextWeek(TheStartWeek)
a = Split(TheStartWeek, " - ")
b = Split(a(1), "-")
c = DateSerial(b(2), b(1), b(0)) + 1
d = c + 6
GetNextWeek = Format(c, "dd-mm-yy") & " - " & Format(d, "dd-mm-yy")
End Function
Sub Test()
Debug.Print GetNextWeek("13-01-14 - 19-01-14") 'Givs you "20-01-14 - 26-01-14"
End Sub
Hope this helps.

ThinkinSphinx query not working with sphinx_select with four conditions

I'm trying to use ThinkingSphinx to return records that have a start date within a range OR an end date within the same range, basically any record that starts or ends within this range.
To do this, I am using a computed attribute and sphinx_select as per the documentation in combination with what this post suggests for date ranges, as follows (assume there are two records, record_a starts outside the range, but ends within the range and record_b starts and ends within the range):
with_display = "*, IF(start_at >= #{range_start.to_i}, 1, 0) + " +
"IF(start_at <= #{range_end.to_i}, 1, 0) + " +
"IF(end_at >= #{range_start.to_i}, 10, 0) + " +
"IF(end_at <= #{range_end.to_i}, 10, 0) AS display"
{
sphinx_select: with_display,
with: {'display' => [2, 20, 22]},
}
=> [record_b]
However, if I only use the start_at conditions, I get one record, and if I use only the end_at conditions, it returns both records.
with_display = "*, IF(start_at >= #{range_start.to_i}, 1, 0) + " +
"IF(start_at <= #{range_end.to_i}, 1, 0) AS display"
=> [record_b]
with_display = "*, IF(end_at >= #{range_start.to_i}, 10, 0) + " +
"IF(end_at <= #{range_end.to_i}, 10, 0) AS display"
=> [record_a, record_b]
If I'm understanding this correctly, having all four conditions, should result in both record_a and record_b being returned, since record_a should have a display value of 20, while record_b should have a display value of 22.
Am I missing something?
I just realized my math was wrong, given the cases I want to handle:
record_a will have a display of 21
record_b will have a display of 22
What I needed to do was change my array to:
{
sphinx_select: with_display,
with: {'display' => [12, 21, 22]},
}
in order the handle the cases of records that end within the range (21), records that start within range (12), and records than start and end within the range (22)