Generate only weekend days in Excel - excel

I am trying in Excel to show only weekend days, but the auto function is only able to show the weekdays.
I would like to obtain in the end a list of only Sat and Sun for a certain period.
Trying to use the WORKDAYS.INTL and WORKDAYS using the start date and a filtering on Sun/Sat, but in Excel 365 that is not working like expected.
Can somebody suggest something?
Thanks in advance.

With Office 365 Dynamic Array Formula:
=FILTER(SEQUENCE(B2-B1+1,,B1),WEEKDAY(SEQUENCE(B2-B1+1,,B1),2)>5)
If one does not have the Dynamic Array formula then one can put this in the first cell:
=IFERROR(AGGREGATE(15,7,ROW(INDEX($ZZ:$ZZ,$B$1):INDEX($ZZ:$ZZ,$B$2))/(WEEKDAY(ROW(INDEX($ZZ:$ZZ,$B$1):INDEX($ZZ:$ZZ,$B$2)),2)>5),ROW($ZZ1)),"")
And copy down.

Try the following User Defined Function:
Public Function weekends(d1 As Date, d2 As Date)
Dim c As Collection, i As Date, s As String
Set c = New Collection
For i = d1 To d2
s = Format(i, "dddd")
If s = "Saturday" Or s = "Sunday" Then
c.Add i, CStr(i)
End If
Next i
ReDim temp(1 To c.Count, 1 To 1)
For j = 1 To c.Count
temp(j, 1) = c.Item(j)
Next j
weekends = temp
End Function
In Excel 365 it should spill-down by itself.
EDIT#1:
If you have an older version of Excel (in my case Excel 2007/Win7) there is not automatic spill-down. Just select a block of cells in the column and array-enter the formula.

Related

Check for valid date - VBA

Guys my primary objective is to avoid invalid days.
In sheet 1 i have:
A1 data validation with years (from 1900-2019)
B1 data validation with all months
C1 i use change event (if both fields A1 & A2 are not empty) calculate how many days the selected month has based on the selected year and create a data validation includes all available days.
For days calculation i use:
Option Explicit
Sub test()
Dim ndays As Long
With ThisWorkbook.Worksheets("Sheet1")
ndays = Day(DateSerial(.Range("A1").Value, .Range("B1").Value + 1, 1) - 1)
End With
End Sub
Sheet Structure:
Is there a batter way to calculate days?
you could use:
DateValue() function to build a date out of a string you compose with your year and month values and adding any valid day number (I chose "1" to be sure...)
EOMONTH() worksheet function to get the last day of the resulting date month:
like follows:
With someSheet
...
nb_days = Day(WorksheetFunction.EoMonth(DateValue(.Range("A1").Value & " " & .Range("B1").Value & " 1"), 0))
...
End With
I suggest to use the UDF (User Defined Function) below.
Function MonthDays(Rng As Range) As Integer
Const Y As Integer = 1
Const M As Integer = 2
Dim Arr As Variant
Application.Volatile ' recalculates on every change
If Application.WorksheetFunction.Count(Rng) = 2 Then
Arr = Rng.Value
MonthDays = DateDiff("d", DateSerial(Arr(Y, 1), Arr(M, 1), 1), _
DateSerial(Arr(Y, 1), Arr(M, 1) + 1, 1))
End If
End Function
You can call it directly from the worksheet with a function call like =MonthDays(A1:A2) where A1 holds the year and A2 holds the month. If either is missing the function returns 0. The function accepts impossible numbers for both year and month and will return a logical result, such as the 14th month of a year being the following year's February. However, you can limit the entries by data validation.
All UDFs can be called as normal functions from your code. Cells(3, 1).Value = MonthDays(Range("A1:A2")) would have the same effect as entering the function call as described in the preceding paragraph in A3. However, if the function is called from VBA the line Application.Volatile would be not required (ineffective).

Which date system does VBA recognise/default to?

I'm writing a vba subroutine in excel to store all date values between 2 dates in a single dimension array and then check for each value in a range on another sheet.
My problem is when the code is grabbing the date values (as an integer(or double - whichever one it is actually using)) it is storing a value 1462 greater than the date value on the sheet.
I am using the 1904 date system so I can use negative time values and i'm in Australia so dates are formatted dd/mm/yyyy.
As an example;
On the sheet:
01/01/2019 = 42004
In the code: 01/01/2019 = 43466
I understand the difference is the same as the difference between date values when using 1900 vs. 1904 date systems which leads me to believe perhaps VBA is defaulting the sheet value to be in 1900 and converting the value to 1904 again when the code is run?
This workbook is set up as follows:
Sheet1 contains dates from 01/01/2019 to 25/01/2019 in Range("A1:A25").
Sheet2 contains 01/01/2019 in cell A1 and 10/01/2019 in cell A2and a activeX commandbutton.
And the code:
Private Sub CommandButton1_Click()
Dim myStart As Long
Dim myEnd As Long
myStart = Me.Range("A1").Value
myEnd = Me.Range("A2").Value
Dim v As Variant
Dim x As Long
Dim myArr As Variant
ReDim myArr(0 To 100)
x = 0
For v = myStart To myEnd
If v = Empty Then Exit For
myArr(x) = v
Debug.Print ("v = " & v)
Debug.Print ("myArr = " & myArr(x))
x = x + 1
Next
Dim lastrow As Long
lastrow = Me.Cells(Rows.Count, 4).End(xlUp).Row
x = 0
For Each cell In Sheet1.Range("A1:A100")
Debug.Print (CDbl(cell))
If cell = myArr(x) Then
Me.Cells(lastrow, 3).Value = cell
Me.Cells(lastrow, 4).Value = Format(cell, "dd/mm/yyyy")
lastrow = lastrow + 1
End If
x = x + 1
Next
End Sub
The output for the cell values in columns 3 and 4 are as follows (first 5 rows for demonstration):
C D Sheet1.Range("A1:A5")[when displayed as number format]
1 43466 01/01/2019 42004
2 43467 02/01/2019 42005
3 43468 03/01/2019 42006
4 43469 04/01/2019 42007
5 43470 05/01/2019 42008
The dates are written into the cell as a short date format not numeric value.
Using the intimidate window I've noticed ?cdbl(DateValue(now)) = 43496 (31/01/2019) which is making me wonder which values are correct for the dates using the 1904 date system? Per the values in Sheet1 it should be 42034.
Is the issue caused by a data type or function I've used, is it as questioned earlier - the sheet is converting them to 1904 when the values are assigned to Sheet2 with VBA, is it a bug with excel or something else?
I found a question with the same problem here on mrexcel however the resolution there was change Excel to use the 1900 date system or subtract 1462 from the values in the code which I'm hoping to avoid both of.
In general, the date system in VBA is different than the date system in Excel. You can have these 3 date system options:
Excel date
Excel date with 1904 property
VBA date
This is the difference:
The dates are converted to numbers. E.g., every date is converted to a number, but the starting number is a bit different.
In Excel, the 1 is converted to 01.January.1900;
In VBA, the 1 is converted to 31.December.1899;
In Excel with 1904, the 1 is converted to 02.January.1904;
The 1904 system (or its lack) is set per Workbook. Thus, if you have 2 workbooks in Excel, one with 1904 and one without it, it would recognize them correspondingly. The system is set in:
File > Options > Advanced > Use 1904 Date System
In general, if there is a chance, that someone somehow changes your workbook with the wrong system, consider adding this check at the openning of the workbook:
Private Sub Workbook_Open()
If ThisWorkbook.Date1904 Then
MsgBox "System is 1904, consider some action!"
End If
End Sub
If you are wondering which system to choose - I can recommend never using the 1904.
Story for the adding VBA to Excel and the 1904 problem
To provide a solution to my example a colleague mentioned if you dim the date values as the date datatype the issues with the values going sheet -> vba -> sheet disappears.

Excel return all data to another worksheet based on header name

In Microsoft Excel I want to create a table to be something like in picture below.
I already try using vlookup and index but I can't make it work like I want.
Please help me
Try to use VBA:
Sub TransformTbl()
Dim i As Long, j As Long, cnt As Long
With ActiveSheet
.Range("G1:I1") = Array("Date", "Event", "Place")
cnt = 1
For j = 2 To 4 'column
For i = 2 To 5 'row
If Len(.Cells(i, j)) <> 0 Then
cnt = cnt + 1
.Cells(cnt, 7) = .Cells(1, j) 'Date
.Cells(cnt, 8) = .Cells(i, j) 'Event
.Cells(cnt, 9) = .Cells(i, 1) 'Place
End If
Next i
Next j
End With
End Sub
I wrote a solution and it works fine with me. The formula is really complex and probably hard to understand. Though I'll try my best to explain it, updating the formula may still be a difficult work. All these three formula are written in Array Formula, press ctrl+shift+enter to complete.
Formula in G6:
=IFERROR(OFFSET($A$5,0,SMALL(
IF($B$6:$D$9<>"",1,99999999)*(COLUMN($B$6:$D$9)-1),ROW(A1))),"")
The outer IFERROR keeps your sheet from any #Err. The OFFSET for calling the right date. The formula inside SMALL generate an array with the rule: If there is an event, the value will be the number of the date for offset, otherwise, it will be 99999999 which giving the OFFSET an error and be blocked by IFERROR. With the data you gave, the array will be
{ 1,99999999, 3;
1, 2,99999999;
1,99999999,99999999;
99999999,99999999, 3 }
Formula in H6:
=IFERROR(OFFSET($A$5,
SMALL(IF($B$6:$D$9<>"",ROW($B$6:$D$9)-5)*
IF(COLUMN($B$6:$D$9)=MATCH(G6,$B$5:$D$5,0)+1,1,99999999),99999999),COUNTIF($G$6:G6,G6)),
MATCH(G6,$B$5:$D$5,0)),"")
The IFERROR and OFFSET works the same as G6. The formula in OFFSET.ROW generate nearly the same array as G6. This time the value is the row of event with the date determined by column G. Other gives 999999999 or more.
Formula in I6:
=IFERROR(OFFSET($A$5,MAX((ROW($B$6:$D$9)-5)*($B$6:$D$9=H6)*
(COLUMN($B$6:$D$9)=MATCH(G6,$B$5:$D$5,0)+1)),0),"")
IFERROR and OFFSET are still the same. And this time only the event which matches the date and the name of itself has a value, other remains 0.
Finally, I apologize for the poor readability. Wish someone can help me out with this :]

Excel VBA Sum from Multiple Sheets

I am trying to create a function or functions that can sum daily hours from time cards for each client to come up with the total hours worked per day. Each client has it's own sheet inside of a single workbook.
Currently, I have a function that determines the sheet that goes with the first client (the third sheet in the workbook):
Function FirstSheet()
Application.Volatile
FirstSheet = Sheets(3).Name
End Function
And one to find the last sheet:
Function LastSheet()
Application.Volatile
LastSheet = Sheets(Sheets.Count).Name
End Function
The part that I am having trouble with it getting these to work within the sum function.
=sum(FirstSheet():LastSheet()!A1
That is basically what I want to accomplish. I think the problem is that I don't know how to concatenate it without turning it into a string and it doesn't realize that it is sheet and cell references.
Any help would be greatly appreciated.
So, an example formula would look like this:
=SUM(Sheet2!A1:A5,Sheet3!A1:A5,Sheet4!A1:A5)
That would sum Sheet2-Sheet4, A1:A5 on all sheets.
Is there a reason you need to write the VBA code to do this?
Can't you just enter it as a formula once?
Also, if you're going to the trouble of writing VBA to generate a formula, it may make more sense to just do the sum entirely in VBA code.
If not, try this:
Sub GenerateTheFormula()
Dim x, Formula
Formula = "=SUM(" 'Formula begins with =SUM(
For x = 3 To Sheets.Count
Formula = Formula & Sheets(x).Name & "!A1," 'Add SheetName and Cell and Comma
Next x
Formula = Left(Formula, Len(Formula) - 1) & ")" 'Remove trailing comma and add parenthesis
Range("B1").Formula = Formula 'Where do you want to put this formula?
End Sub
Results:
The functions return strings and not actual worksheets. The Worksheet does not parse strings well. So add a third function that uses the Evaluate function:
Function MySum(rng As Range)
MySum = Application.Caller.Parent.Evaluate("SUM(" & FirstSheet & ":" & LastSheet & "!" & rng.Address & ")")
End Function
Then you would simply call it: MySum(A1)
It uses the other two function you already have created to create a string that can be evaluated as a formula.
I didn't understand ur question completely but As I understood u have different sheets of different clients which contains supoose column 1 date and column 2
contains hours on that particular date wise hours and a final sheet which column1 contains name of client and column 2 contains total hoursPlease try it
Sub countHours()
Dim last_Row As Integer
Dim sum As Double
sum = 0
'Because I know number of client
For i = 1 To 2 'i shows client particular sheet
last_Row = Range("A" & Rows.Count).End(xlUp).Row
Sheets(i).Activate
For j = 2 To last_Row
'In my Excel sheet column 1 contains dates and column 2 contains number of hours
sum = sum + Cells(j, 2)
'MsgBox sum
Next j
'Sheet 3 is my final sheet
ThisWorkbook.Sheets(3).Cells(i + 1, 2).Value = sum
sum = 0
Next i
End Sub
Happy Coding :

Macro to move data with changing date

Sorry if this isn't explained overly well, im a macro newbie so im not sure if this one is even possible..
I'm looking to create a weekday table for some simple statistic reporting that automatically creates a new row each day, and removes the oldest, showing the data for the current day and 6 days previous. Ideally i'd like the current day at the top of the table, and each day the entered data in the corresponding row moves down 1 row creating space for the new day's stats.
As some background info on what im trying to do.. im basically creating a friendly UI display (offline HTML) of the data recorded in a very simple 5 column (stats) by 7 row (weekdays) table. This database will need to be updated by multiple people with limited technical ability, so im basically trying to make it as easy as possible for them to enter stats each day without having to worry about also updating to correct dates and making sure they are replacing the right days data etc. In theory, it would be great to automate the process of updating the table each day to create space for them to enter the current days data, pushing yesterdays data down one row (and if the cell ranges for the whole table always the same, it should allow me to automate the updates to the offline HTML display as well).
Any ideas?
This should get you started:
Sub WeekdayTable()
Dim tbl As Range
Dim r As Integer
Set tbl = Range("A1:E7") 'Define your table, 5 columns x 7 rows. Modify as needed.
For r = tbl.Rows.Count To 2 Step -1
tbl.Rows(r).Value = tbl.Rows(r - 1).Value
Next
'empty out row 1
tbl.Rows(1).Clear
'Assuming the column 1 contains valid DATE values, _
' we can use the DateAdd function to print the next date:
tbl.Cells(1, 1) = DateAdd("d", 1, tbl.Cells(2, 1))
End Sub
First give a name to the date header cell. (Click the cell. Look at the top left of the screen where the cell coordinates appear. "A1", "B2", etc...
In that textbox, type the header name: "MyDateHeader"
then, use this macro (you can add it to the workbook open event, or activate)
Sub YourMacro()
Dim DateHeader As Range
Set DateHeader = Range("MyDateHeader")
Dim FirstDateCell As Range
Set FirstDateCell = DateHeader.Offset(1, 0)
Dim MyDay As Integer, MyMonth As Integer, MyYear As Integer
Dim CurrDay As Integer, CurrMonth As Integer, CurrYear As Integer
MyDay = Day(FirstDateCell.Value)
MyMonth = Month(FirstDateCell.Value)
MyYear = Year(FirstDateCell.Value)
CurrDay = Day(Date)
CurrMonth = Month(Date)
CurrYear = Year(Date)
If (MyDay <> CurrDay) Or (MyMonth <> CurrMonth) Or (MyYear <> CurrYear) Then
FirstDateCell.EntireRow.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromRightOrBelow
DateHeader.Offset(1, 0).Value = Date 'Careful, FirstDateCell has moved down.
DateHeader.Offset(8, 0).EntireRow.Clear
End If
End Sub

Resources