Change time format from number like 600 to 06:00:00 - excel

I have two workbooks, A and B, and two sheets, 1 and 2, in both the workbooks.I need to compare and find the match based on time.In workbook B in sheet 1 I have hh:mm:ss 24 hrs time format.But in workbook A I have the time from and to in number format like 600 and 800 etc. I have changed the time format in workbook 1 to hh:mm:ss but the problem I am getting after running for the first time is that the time has changed to 06:00:00 and again in the second run it is changed to 00:00:00.
Dim rCell As Range
Dim iHours As Integer
Dim iMins As Integer
Dim lrow As Long
Dim rn As Range
lrow = Sot.Range("d" & Rows.Count).End(xlUp).Row
Sot.Activate
Set rCell = Sot.Range(Cells(5, 4), Cells(lrow, 5))
For Each rn In rCell
If IsNumeric(rn.Value) And Len(rn.Value) > 0 Then
iHours = rn.Value \ 100
iMins = rn.Value Mod 100
rn.Value = (iHours + iMins / 60) / 24
rn.NumberFormat = "h:mm:ss"
End If
Next
For i = 5 To eRowplan
Time_from = Sotplan.Range("D" & i).Value
Time_To = Sotplan.Range("E" & i).Value
Time_from = TimeSerial(Hour(Time_from), Minute(Time_from),
Second(Time_from))
Time_To = TimeSerial(Hour(Time_To), Minute(Time_To), Second(Time_To))
If B_Time > "24:00" Then B_Time = "23:59"
B_Time = TimeSerial(Hour(B_Time), Minute(B_Time), Second(B_Time))
I tried with above codes

You can do it with the following if you want a VBA solution for e.g. 600 = 600 minutes
Dim c
For Each c In Selection
c.Value2 = c / (24 * 60)
c.NumberFormat = "hh:mm"
Next c
Or just use = A1 / (24 * 60) and format as Time
Update after comments
For 600 = 06:00:00 you can use the following
Dim c, tmpTime As Variant
For Each c In Selection
' Test if number
If IsNumeric(c.Value2) Then
' Split into character array, the Len(c.Value2) limits the size of the array otherwise
' an additional empty element is created
tmpTime = Split(StrConv(c.Value2, vbUnicode), Chr$(0), Len(c.Value2))
' Write results back and format
With c.Offset(0, 1)
.Value2 = Join(tmpTime, ":")
.NumberFormat = "hh:mm:ss"
End With
End If
Next c

Related

Excel-VBA: fist and last row calculation wrong

this is my first VBA project and I'm stuck on one problem:
I need to calculate the monthly skewness using daily returns for multiple assets. First, I detected the months and the cells where each month ends and put them in a new worksheet. Based on that, I calculate the monthly skewness for each asset to get a table with the month in the rows and the assets in the columns.
The problem is that the skewness for the first and last row (so the first and last month) are incorrect, although the month is correctly identified by the first part of the code. In the second part of the code where I calculate the skewness I get this error: "Unable to get the Skew property of the WorksheetFunction class" for the line where I want to store the skew in an array but the skewness is calculated nonetheless. How can I solve the error message and get the correct skewness for the first and last month?
Any help would be very appreciated!
This is the code im working with:
Sub step1()
Dim v()
Dim idm()
Dim MonthCount As Single
'find the last row
lastrow = Worksheets("Data").Range("A2").End(xlDown).Row
'save the dates to array
v = Worksheets("Data").Range("A2:A" & lastrow).Value
'count the number of months
M = 1 'initiate month counter
For Row = 1 To lastrow - 2
'extract months from dates
month1 = month(v(Row, 1))
month2 = month(v(Row + 1, 1))
'increase the counter when the month changes
If month1 <> month2 Then
M = M + 1
End If
Next
MonthCount = M
'resize the month arrays
ReDim idm(1 To MonthCount, 1 To 2)
'detect change in month and save cumulative days as row indexes
M = 1 'initiate month counter
For Row = 1 To lastrow - 2
month1 = month(v(Row, 1))
month2 = month(v(Row + 1, 1))
If month1 <> month2 Then
'save the month
idm(M, 1) = month1 & "/" & Year(v(Row, 1))
'save the cumulative days
idm(M, 2) = Row + 1
M = M + 1
End If
On Error Resume Next
Next
'save the last month, cannot detect month change with empty row below
idm(M, 1) = month1 & "-" & Year(v(Row, 1))
idm(M, 2) = Row
'End With
'write the month array to excel
Worksheets("Months").Range("A2:B" & MonthCount + 1).Value = idm
End Sub
Sub step2()
Dim idMonth()
'save last month index to array
lastmonth = Worksheets("Months").Range("B2").End(xlDown).Row
idMonth() = Worksheets("Months").Range("B2:B" & lastmonth).Value
Dim z As Variant
Dim r1, r2 As Double
nc = Worksheets("Data").Range("A1").CurrentRegion.Columns.Count
'initiate counters
r1 = 2
r2 = 2
For c = 1 To nc
For M = 1 To UBound(idMonth) 'm is month index
'end of the month
r2 = idMonth(M, 1)
'set range for skewness
Set month_range = Worksheets("Data").Range(Cells(r1, c + 1).Address, Cells(r2, c + 1).Address)
'store skew in array
'error in the following line
z = Application.WorksheetFunction.skew(month_range)
'write skew to cells
Worksheets("Months").Cells(M + 1, c + 2).Value = z
'start of next month
r1 = r2 + 1
On Error Resume Next
Next
Next
End Sub

How do I split a custom DateTime format in excel using VB Macros?

I have a document with the following:
FullDateTime FullDate FullTime Day Month Year Hour Minute Second
dd/mm/yyyy hh:mm:ss AM/PM
and I would like to fill in the other columns using macros to split the first column and place the whole date, whole time, day, month, year, hour, minute and second in the other columns. FullDateTime is every five minutes and I want to the DateTime to run for a whole year. I imagine the code to look something like:
Sub Func()
Dim 5mindays as Integer = 12*24*365
Dim x As Integer
Dim date
Dim time
For x = 1 To 5mindays
Split(," ")
Split(,"/")
Split(,":")
.Offset(0,1) = date(0)
...
.Offset(0,8) = time(2)
Add the next FullDateTime field below the existing one (adding 5 minutes)
Next
But have no idea how to actually do it. Please give me some ideas on how to solve this. Thanks!
Try after setting the correct worksheet name and year to process,
Option Explicit
Sub funk()
Dim dt As Long, yr As Long, tm As Long, dttm As Double
yr = 2018
dt = DateSerial(yr, 1, 1)
With Worksheets("sheet6")
Do While Year(dt) = yr
Do While TimeSerial(0, tm * 5, 0) < 1
dttm = dt + TimeSerial(0, tm * 5, 0)
.Cells(tm + 1 + (dt - DateSerial(yr, 1, 1)) * 288, "A").Resize(1, 9) = _
Array(dttm, dt, dttm - dt, _
Day(dt), Month(dt), yr, _
Hour(dttm), Minute(dttm), 0)
tm = tm + 1
Loop
tm = 0
dt = dt + 1
Loop
With .Range(.Cells(1, "A"), .Cells(.Rows.Count, "I").End(xlUp))
.Columns("A").NumberFormat = "dd/mm/yyyy hh:mm:ss AM/PM"
.Columns("B").NumberFormat = "dd/mm/yyyy"
.Columns("C").NumberFormat = "hh:mm:ss"
.Columns("D:I").NumberFormat = "0"
End With
End With
End Sub

Excel if value appears in any column, average the reference column

I need to find the average of a number found in column F if a particular value is found in any of the other columns in the sheet.
For instance: I have the following in a range...
A B C D E F
Red    Bill   Jack   Ruby   Bill   250
Blue  Ruby   Ivan   Raul   Ted   350
Green  Ted   James Rick   Ted   125
Red   Ted   Phil   Ruby   Bill   300
And in this worksheet, I want to find any instance of the name Bill and get the average of the number found in column F. In this case, the answer of 275 because Bill's name shows up in two rows. In the same respect, If I choose to look at Ted's numbers, the answer should be 258 because Ted's name shows up in three rows.
I would also appreciate if the formula would ignore any blank cells in the process of calculating the answer.
Thanks in advance!
I would use the function below, assuming that the data is placed in Sheet1.
Function my_average(strName As String) As Variant
Dim varArrayNames As Variant
Dim varValues As Variant
Dim dblInSum(1 To 4) As Double '~~> change to "1 To 40"
Dim lngCnt As Long
Dim strRow As String
Dim dblSum As Double
varArrayNames = Sheet1.Range("B1:E4").Value '~~> change to "B1:G40"
varValues = Sheet1.Range("F1:F4").Value '~~> change to "H1:H40"
For lngCnt = LBound(varArrayNames, 1) To UBound(varArrayNames, 1)
strRow = Join(WorksheetFunction.Index(varArrayNames, lngCnt, 0))
If InStr(strRow, strName) > 0 Then
dblInSum(lngCnt) = 1
End If
Next lngCnt
dblSum = WorksheetFunction.Sum(dblInSum)
If dblSum > 0 Then
my_average = WorksheetFunction.SumProduct(dblInSum, Application.Transpose(varValues)) / dblSum
Else
my_average = 0
End If
End Function
Testing:
Place =my_average("Bill") in any workbook (or a cell reference instead of "Bill").
Formulas:
Results:
Assuming the lookup value (Bill etc) is in cell C7, add the following formula in G1 then copy down for other rows.
=IF(ISERROR(MATCH($C$7,A1:E1,0)),"",F1)
Then do
=AVERAGE(G1:G4)
So if Bill is in any col a-e that number is taking into the ave... If so depending on your data size why not do this simply...:
Sub simplesearch()
cnt = 0
tot = 0
srchval = InputBox("What are we looking for?")
lr = Range("A1000000").End(xlUp).Row
For i = 1 To lr
For j = 1 To 5
If Cells(i, j).Value = srchval Then
tot = tot + Cells(i, 6).Value
cnt = cnt + 1
End If
Next j
Next i
If Not (cnt = 0) Then
MsgBox (tot / cnt)
Else
MsgBox ("0")
End If
End Sub

Unique Count Formula for large dataset

I am having trouble determining a way to enter a 1 or 0 into an adjacent cell to indicate whether or not a value is unique when working with a large dataset. I have read of multiple methods for accomplishing this, however none of them seem efficient for my purposes: I am using an instance of Excel 2010 (so I do not have the Distinct Count feature in PivotTables, and when I try to use PowerPivot it crashes my file due to processing limitations.
In this StackOverflow question: Simple Pivot Table to Count Unique Values there are suggestions to use SUMPRODUCT or COUNTIF, but when working with 50,000+ rows as I am, this causes terrible performance and a file size of ~35 MB instead of ~3 MB. I wanted to know if there is a better solution for a large, dynamic dataset whether it is a formula or VBA.
An example of what I would like to accomplish is (with the Unique column being the adjacent cell):
Name Week Unique
John 1 1
Sally 1 1
John 1 0
Sally 2 1
I attempted to script the same functionality of COUNTIF but with no success:
For Each Cell In ThisWorkbook.Worksheets("Overtime & Type Data").Range("Z2:Z" & DataLastRow)
If Worksheets("Overtime & Type Data").Cells(Cell.Row, 26) <> Worksheets("Overtime & Type Data").Cells(Cell.Row - 1, 26) Then
FirstCell = Cell.Row
End If
If (Worksheets("Overtime & Type Data").Range(Cells(FirstCell, 26), Cells(Cell.Row, 26)) = Worksheets("Overtime & Type Data").Range(Cells(Cell.Row, 26))) = True Then
Cell.Value = 1
Else
Cell.Value = 0
End If
Next Cell
This code ran on over 130,000 rows successfully in less than 3 seconds. Adjust the column letters to fit your dataset.
Sub tgr()
Const colName As String = "A"
Const colWeek As String = "B"
Const colOutput As String = "C"
Dim ws As Worksheet
Dim rngData As Range
Dim DataCell As Range
Dim rngFound As Range
Dim collUniques As Collection
Dim arrResults() As Long
Dim ResultIndex As Long
Dim UnqCount As Long
Set ws = ThisWorkbook.Sheets("Overtime & Type Data")
Set rngData = ws.Range(colName & 2, ws.Cells(Rows.Count, colName).End(xlUp))
Set collUniques = New Collection
ReDim arrResults(1 To rngData.Cells.Count, 1 To 1)
On Error Resume Next
For Each DataCell In rngData.Cells
ResultIndex = ResultIndex + 1
collUniques.Add ws.Cells(DataCell.Row, colName).Value & ws.Cells(DataCell.Row, colWeek).Value, ws.Cells(DataCell.Row, colName).Value & ws.Cells(DataCell.Row, colWeek).Value
If collUniques.Count > UnqCount Then
UnqCount = collUniques.Count
arrResults(ResultIndex, 1) = 1
Else
arrResults(ResultIndex, 1) = 0
End If
Next DataCell
On Error GoTo 0
ws.Cells(rngData.Row, colOutput).Resize(rngData.Cells.Count).Value = arrResults
End Sub
One approach is to sort by Name and Week. Then you can determine Unique for any row by comparing with the previous row.
If you need to preserve the order, you could first write a column of Index numbers (1, 2, 3, ...) to keep track of order. After calculating Unique, sort by Index to restore the original order.
The whole process could be done manually with relatively few steps, or automated with VBA.
I'm not sure how well this will work with 50000 values, but it goes through ~1500 in about a second.
Sub unique()
Dim myColl As New Collection
Dim isDup As Boolean
Dim myValue As String
Dim r As Long
On Error GoTo DuplicateValue
For r = 1 To Sheet1.UsedRange.Rows.Count
isDup = False
'Combine the value of the 2 cells together
' and add that string to our collection
'If it is already in the collection it errors
myValue = Sheet1.Cells(r, 1).Value & Sheet1.Cells(r, 2).Value
myColl.Add r, myValue
If isDup Then
Sheet1.Cells(r, 3).Value = "0"
Else
Sheet1.Cells(r, 3).Value = "1"
End If
Next
On Error GoTo 0
Exit Sub
DuplicateValue:
'The value is already in the collection so put a 0
isDup = True
Resume Next
End Sub
Just about any bulk operation will beat a loop involving worksheet cells. You might be able to trim the time down a bit by performing all of the calculations in memory and only returning the values back to the worksheet en masse when it is complete.
Sub is_a_dupe()
Dim v As Long, vTMP As Variant, vUNQs As Variant, dUNQs As Object
Debug.Print Timer
On Error GoTo bm_Uh_Oh
Set dUNQs = CreateObject("Scripting.Dictionary")
With Worksheets("Sheet1")
vTMP = .Range(.Cells(2, 1), .Cells(Rows.Count, 2).End(xlUp)).Value2
ReDim vUNQs(1 To UBound(vTMP, 1), 1 To 1)
For v = LBound(vTMP, 1) To UBound(vTMP, 1)
If dUNQs.Exists(Join(Array(vTMP(v, 1), vTMP(v, 2)))) Then
vUNQs(v, 1) = 0
Else
dUNQs.Add Key:=Join(Array(vTMP(v, 1), vTMP(v, 2))), _
Item:=vTMP(v, 2)
vUNQs(v, 1) = 1
End If
Next v
.Cells(2, 3).Resize(UBound(vUNQs, 1), 1) = vUNQs
End With
Debug.Print Timer
bm_Uh_Oh:
dUNQs.RemoveAll
Set dUNQs = Nothing
End Sub
Previous experience tells me that the variety of data (as well as hardware, etc) will impact timing the process but in my random sample data I received these elapsed times.
 50K records ..... 0.53 seconds
130K records .... 1.32 seconds
500K records .... 4.92 seconds

How to implement an advanced look up macro in excel?

How do I have to change the following macro code and formula which works for the data and question below(earlier data/question,column F) to make it suitable for Problemstatement (2) and (3) ?
(1)
Earlier data
Colums C,D,E,F
Row 1 4,10,40,F
Row 2 4,12,48,F
Row 3 4,14,56,F
Row 4 3,16,48,F
Row 5 1,18,18,F
Row 6 1,20,10,F
Row 7 0,22,0,0
Intention of the column F
If Cx <> 0, Fx = Cx
If Cx = 0, Fx = the address of the cell in Column C that produces minimum of (C1 * D7 - E1, C2 * D7 - E2, ..., CN * D7 - EN) and is >0.
** Macro code and formula for column F**
Public Function MinimumC()
Dim rngCurrent As Range
Set rngCurrent = Application.ThisCell
Dim rngMin As Range
Dim minimum As Long
minimum = 100000000
Dim tmp As Long
Dim rngC As Range
Set rngC = ActiveSheet.Range("C1:C" & rngCurrent.Row - 1)
For Each c In rngC.Cells
If c.Value2 <> 0 Then
tmp = c.Value2 * rngCurrent.Offset(0, -2).Value2 - c.Offset(0, 2)
If tmp < minimum Then
minimum = tmp
Set rngMin = c
End If
End If
Next c
MinimumC = rngMin.Value2
End Function
Formula in F1 and copy down column F: =IF(C1<>0,C1,MinimumC())
(2)
How do I have to change the macro and formula to archieve the same in the following data format:
New data 1
Colums AZ,BA,BB,BC,BD,BE,BF,BG
Row 1 4,4,4,10,10,10,120,444
Row 2 4,4,4,12,12,12,144,444
Row 3 4,4,4,14,14,14,168,444
Row 4 3,3,3,16,16,16,144,333
Row 5 1,1,1,18,18,18,54,111
Row 6 1,1,1,20,20,20,60,111
Row 7 0,0,0,22,22,22,0,???
Intention of the column BG
If(And(AZ>0;BA>0;BB>0);Concatenate(AZ;BA;BB))
otherwise, the adress cell of Concatenate (AZ;BA;BB), unequal to 000, to minimize the following difference (AZn*BCx+BAn*BDx+BBn*BEx)-BFn
(3)
How do I have to change the macro and formula from (1) to archieve the same in the following data format:
New data 2
Colums AZ,BA,BB,BC,BD,BE,BF,BG,BH,BI,BJ,BK,BL,BM,BN
Row 1 4,4,4,10,10,10,120,444,3,3,3,10,10,10,90,333
Row 2 4,4,4,12,12,12,144,444,3,3,3,12,12,12,108,333
Row 3 4,4,4,14,14,14,168,444,3,3,3,14,14,14,126,333
Row 4 3,3,3,16,16,16,144,333,3,3,3,16,16,16,144,333
Row 5 1,1,1,18,18,18,54,111,2,2,2,18,18,18,108,222
Row 6 1,1,1,20,20,20,60,111,1,1,1,20,20,20,60,111
Row 7 0,0,0,22,22,22,0,?,0,0,0,22,22,22,0,?
Intention of the column BN
If(And(AZ>0;BA>0;BB>0;BH>0;BI>0;BJ>0);0
otherwise the adress of the cell either concatenate (AZ;BA;BB) or concatenate (BH;BI;BJ) to minimize (AZn*BCx+BAn*BDx+BBn*BEx)-BFn or (BHn*BKx+BIn*BLx+BJn*BMx)-BNn .In this case i need to find the adress of either concatenate (AZ;BA;BB) or concatenate (BH;BI;BJ) with the min difference and that cell of BN (concatenate) unequal to 000.
I appriciate the help. Thank you very much!!!!
To solve problem 2, the macro is basically identical. You just need to replace the C range with the AZ range, and change the calculation of tmp:
Public Function CalcBG()
Dim rngCurrent As Range
Set rngCurrent = Application.ThisCell
Debug.Print rngCurrent.Address
Dim rngMin As Range
Dim minimum As Long
minimum = 100000000
Dim tmp As Long
Dim rngAZ As Range
Set rngAZ = ActiveSheet.Range("AZ1:AZ" & rngCurrent.Row - 1)
Debug.Print rngAZ.Address
For Each c In rngAZ.Cells
If c.Value2 <> 0 Then
tmp = ((c.Value * rngCurrent.Offset(0, -4).Value2) + (c.Offset(0, 1).Value2 * rngCurrent.Offset(0, -3).Value2) + (c.Offset(0, 2).Value2 * rngCurrent.Offset(0, -2)) - c.Offset(0, 6).Value2) 'This is your calculation (AZn * BCx) + ... - BFn
If tmp < minimum Then
minimum = tmp
Debug.Print minimum
Set rngMin = c.Offset(0, 7)
Debug.Print rngMin
End If
End If
Next c
Debug.Print minimum
Debug.Print rngMin.Address
CalcBG = rngMin.Address 'Return the address rather than the value
End Function
And the formula:
=IF(AND(AZ1>0,BA1>0,BB1>0),CONCATENATE(AZ1,BA1,BB1),CalcBG())
Place that in BG1 and copy down Column BG.
Problem 3
This added a couple more variables, but the basic structure is the same.
Based on the data, I also assumed that "If(And(AZ>0;BA>0;BB>0;BH>0;BI>0;BJ>0);0" should have been If(And(AZ>0;BA>0;BB>0;BH>0;BI>0;BJ>0),concatenate(bh,bi,bj). Otherwise your values in BO would all be 0. Also, your last column is BO, not BN. :)
Here's the macro:
Public Function CalcBO()
Dim rngCurrent As Range
Set rngCurrent = Application.ThisCell
Debug.Print rngCurrent.Address
Dim rngMin As Range
Dim minimum As Long
minimum = 100000000
Dim tmp1 As Long
Dim tmp2 As Long
Dim lowest As Long
Dim rngAZ As Range
Set rngAZ = ActiveSheet.Range("AZ1:AZ" & rngCurrent.Row - 1)
Debug.Print rngAZ.Address
For Each c In rngAZ.Cells
If c.Value2 <> 0 Then
'(AZn*BCx+BAn*BDx+BBn*BEx)-BFn
tmp1 = ((c.Value2 * rngCurrent.Offset(0, -12).Value2) + (c.Offset(0, 1).Value2 * rngCurrent.Offset(0, -13).Value2) + (c.Offset(0, 2).Value2 * rngCurrent.Offset(0, -10).Value2)) - c.Offset(0, 6).Value2
'(BHn*BKx+BIn*BLx+BJn*BMx)-BNn
tmp2 = ((c.Offset(0, 8).Value2 * rngCurrent.Offset(0, -4).Value2) + (c.Offset(0, 9).Value2 * rngCurrent.Offset(0, -3).Value2) + (c.Offset(0, 10).Value2 * rngCurrent.Offset(0, -2).Value2)) - c.Offset(0, 14).Value2
lowest = WorksheetFunction.Min(tmp1, tmp2)
If lowest < minimum Then
minimum = lowest
Debug.Print minimum
Set rngMin = c.Offset(0, 7)
Debug.Print rngMin
End If
End If
Next c
Debug.Print minimum
Debug.Print rngMin.Address
CalcBO = rngMin.Address
End Function
And here is the formula for BO1. Copy down as usual:
=IF(AND(AZ1>0,BA1>0,BB1>0,BH1>0,BI1>0,BJ1>0),CONCATENATE(BH1,BI1,BJ1),CalcBO())
There's a lot of repetition among those macros, and they could probably be condensed. But they should do what you need.
One note: The macros won't differentiate between equal minimum values. For example, in problem set 3, the two calculations (AZn ... - BFn and BHn ... - BNn) return the same value, 6, which also happens to be the minimum value among all the calculations. In this case, it returns the first address (AZn ... - BFn). So in Problem 3, you will get the answer $BG$6 in BO7, even though $BG$6 and $BO$6 both satisfy the conditions. Your requirements didn't specify what to do in case there was more than one minimum value, so I left it as it is.
Let me know if you need any help with the above.

Resources