问题
I have to convert some formatted text to excel time, but I could not find a solution by myself.
Here are some examples of the input text and how it should be converted:
- 1 Hour 14 minutes ==> 01:14:00
- 1 minute. ==> 00:01:00
- 1 Hour 1 minute ==> 01:01:00
- 2 minutes ==> 00:02:00
- 3 minutes 12 seconds ==> 00:03:12
- 29 seconds ==> 00:00:29
Observe that some times there are both minutes and seconds and some others only one of minutes/seconds, besides some times you find minutes (plural) and some others just minute (singular). Finally, some punctuation signs could be in the text some times.
The data is in a spreadsheet column and I want to extract the excel formatted time in a different column on the spreadsheet.
I've tried different versions of TimeValue()
and DateValue()
and some nested replace() all of them in a cell formula, but none of them worked for all cases.
Could you give me an idea or some advice on how to approach this problem?
Thanks,
Paul
回答1:
I played with your question for a day and see that others have done the same. I admire the solution offered by Ron Rosenfeld using FilterXML which I briefly considered and discarded as too outlandish. It isn't. But neitehr is it neat. For "neat" look no further than the UDF below. Call it from the worksheet with something like =TextToTime(A1)
and be sure to format the cell you put this in as Time, perhaps like hh:mm:ss
.
Function TextToTime(Cell As Range) As Double
Dim Fun(2) As String
Dim Txt() As String
Dim Tmp As Variant
Dim i As Integer
Txt = Split(Cell.Value)
For i = 1 To UBound(Txt)
If Not IsNumeric(Txt(i)) Then
Tmp = 0
Tmp = InStr("HMS", UCase(Left(Trim(Txt(i)), 1)))
If Tmp Then
Fun(Tmp - 1) = Val(Txt(i - 1))
End If
End If
Next i
For i = LBound(Fun) To UBound(Fun)
If Fun(i) = "" Then Fun(i) = 0
Next i
TextToTime = TimeValue(Join(Fun, ":"))
End Function
This function is very versatile. It can translate strings like "5 hours 10 minutes 15 seconds" or "5 hours, 10 minutes, 15 seconds" (with commas) or even "5 hours & 10 minutes and 15 seconds". It needs the spaces around the numbers but doesn't balk at extra spaces or typos. It fails on "75 minutes and 66 seconds" but that could be dealt with if it's an issue. The key advantage of VBA is that almost anything can be dealt with using just a few extra lines of code. Worksheeet functions don't have that capability.
回答2:
Since you did not include VBA as a tag, here are some formula solutions.
If you have Windows Excel 2013+ with the FILTERXML
function, you can use the following:
=IFERROR(FILTERXML("<t><s>" & SUBSTITUTE(LOWER(A1)," ","</s><s>") & "</s></t>","//s[contains(.,'hour')]/preceding-sibling::*[1]")/24,0)+
IFERROR(FILTERXML("<t><s>" & SUBSTITUTE(LOWER(A1)," ","</s><s>") & "</s></t>","//s[contains(.,'min')]/preceding-sibling::*[1]")/1440,0)+
IFERROR(FILTERXML("<t><s>" & SUBSTITUTE(LOWER(A1)," ","</s><s>") & "</s></t>","//s[contains(.,'sec')]/preceding-sibling::*[1]")/86400,0)
If you do not have the FILTERXML
function:
- Create a named formula:
Formulas
→Defined Names
→Define Name
→New Name
→ seq_99 →Refers to:
→=IF(ROW(INDEX($A:$A,1,1):INDEX($A:$A,255,1))=1,1,(ROW(INDEX($A:$A,1,1):INDEX($A:$A,255,1))-1)*99)
Then use this formula:
=IFERROR(INDEX(TRIM(MID(SUBSTITUTE(TRIM(A1)," ",REPT(" ",99)),seq_99,99)),-1+MATCH("hour*",TRIM(MID(SUBSTITUTE(TRIM(A1)," ",REPT(" ",99)),seq_99,99)),0))/24,0)+
IFERROR(INDEX(TRIM(MID(SUBSTITUTE(TRIM(A1)," ",REPT(" ",99)),seq_99,99)),-1+MATCH("min*",TRIM(MID(SUBSTITUTE(TRIM(A1)," ",REPT(" ",99)),seq_99,99)),0))/1440,0)+
IFERROR(INDEX(TRIM(MID(SUBSTITUTE(TRIM(A1)," ",REPT(" ",99)),seq_99,99)),-1+MATCH("sec*",TRIM(MID(SUBSTITUTE(TRIM(A1)," ",REPT(" ",99)),seq_99,99)),0))/86400,0)
Algorhythm:
- Find the string
hour
ormin
orsec
- Return the node prior to each of those strings
- divide by the appropriate factor to create an excel time value
Note: Custom format column B as hh:mm:yy
As pointed out by @ScottCraner in the comments, the formulas can be shortened by creating an array formula:
Using FILTERXML
:
=SUM(IFERROR(FILTERXML("<t><s>" & SUBSTITUTE(LOWER(A1)," ","</s><s>") & "</s></t>","//s[contains(.,"&{"'hour'","'min'","'sec'"}&")]/preceding-sibling::*[1]")/{24,1440,8640},0))
without FILTERXML
:
=SUM(IFERROR(INDEX(TRIM(MID(SUBSTITUTE(TRIM(A1)," ",REPT(" ",99)),seq_99,99)),-1+MATCH({"hour*","min*","sec*"},TRIM(MID(SUBSTITUTE(TRIM(A1)," ",REPT(" ",99)),seq_99,99)),0))/{24,1440,8640},0))
In some earlier versions of Excel, you may need to "confirm" this array-formula it by holding down ctrl + shift while hitting enter. If you do this correctly, Excel will place braces {...}
around the formula as observed in the formula bar
回答3:
Well, you can take this and edit it to suit your needs:
Here is the formula :
=IF(IFERROR(FIND("da",A2,1),0)>0,LEFT(A2,FIND(" ",A2,1)-1)*24,0)+IFERROR(1*MID(A2,FIND(" ",A2,FIND(" ",A2,1)+1),FIND(" ",A2,FIND(" ",A2,FIND(" ",A2,1)+1)+1)-FIND(" ",A2,FIND(" ",A2,1)+1)),0*1)
I just wanted the date converted to hours so you can finish to what you want.
回答4:
If you decide to use VBA and you keep the same format in Column A you could use the below code:
Code:
Sub Converter()
Dim i As Long, y As Long, LastRow As Long
Dim arrData As Variant, arrLine As Variant
Dim Hours As String, Minutes As String, Seconds As String
With ThisWorkbook.Worksheets("Sheet1")
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
arrData = .Range("A1:A" & LastRow)
For i = LBound(arrData) To UBound(arrData)
Hours = "00"
Minutes = "00"
Seconds = "00"
arrLine = Split(arrData(i, 1), " ")
For y = LBound(arrLine) To UBound(arrLine)
If InStr(1, UCase(arrLine(y)), "HOUR") > 0 Then
Hours = Right("0" & arrLine(y - 1), 2)
ElseIf InStr(1, UCase(arrLine(y)), "MINUTE") > 0 Then
Minutes = Right("0" & arrLine(y - 1), 2)
ElseIf InStr(1, UCase(arrLine(y)), "SECOND") > 0 Then
Seconds = Right("0" & arrLine(y - 1), 2)
End If
Next y
.Range("B" & i).Value = Hours & ":" & Minutes & ":" & Seconds
Next i
End With
End Sub
Output:
来源:https://stackoverflow.com/questions/61200039/convert-excel-text-to-time