Swift Number Of Weeks in Month - swift

I'm using Swift 5. I'm trying to get the number of weeks in a month. Jan 2021 prints 6 weeks, but May prints 5 weeks. I'm thinking the problem is firstWeekday is set to 1. Is there a way to get the number of weeks in a given month without setting firstWeekday?
var localCalendar = Calendar.current
localCalendar.firstWeekday = 1
let weekRange = localCalendar.range(of: .weekOfMonth, in: .month, for: dateFormatter.date(from: monthName)!)
if let weekRange = weekRange {
print("\(monthName) has \(weekRange.count) weeks.")
}

It seems you do not want the number of weeks in a month at all. You want the number of rows in a calendar printout.
The formula for the number of rows needed to represent a month on a standard Gregorian-style calendar is as follows.
Start with the number of days in the month. Add to that the number of blank days before the start of the month, beginning with Sunday. For example, January 2021 is 31 + 5 = 36, because it's 31 days long but 5 days (Sunday thru Thursday) are not part of it before the start. To put it another way: the first day of January 2021 is a Friday; that is day 5 of the week if we call Sunday day 0, so we get 31 + 5.
Now integer-divide by 7. We need at least that number of rows. So (31+5)/7 using integer division is 5. The question is: is that all the rows we need?
To find out, get the remainder of that division. If it is not zero, add another row. So (31+5)%7 is 1, which tells us that one more day needs to be accommodated so we need another row. That makes 6.
Thus:
// `startingOn` pretends that Sunday is day 0
func rowsNeededForMonthWith(numberOfDays n: Int, startingOn i: Int) -> Int {
let (quot,rem) = (n+i).quotientAndRemainder(dividingBy: 7)
return quot + (rem == 0 ? 0 : 1)
}
Here are some quick sanity tests:
// January 2021
rowsNeededForMonthWith(numberOfDays: 31, startingOn: 5) // 6
// But suppose it had 30 days?
rowsNeededForMonthWith(numberOfDays: 30, startingOn: 5) // 5
// Or suppose it had started on Thursday? (cf July 2021)
rowsNeededForMonthWith(numberOfDays: 31, startingOn: 4) // 5

Related

How to calculate days between two dates only if they belong to a certain month

For example the date range is
28-01-2022 and 20-03-2022
I want to count separately the days that belong to january (3) february (27) and march (19)
I would really appreciate the help!!
In python, you can do the following:
import calendar
import datetime
from dateutil import relativedelta
start_str = "1-03-2022"
end_str = "20-12-2024"
start = datetime.datetime.strptime(start_str, "%d-%m-%Y")
end = datetime.datetime.strptime(end_str, "%d-%m-%Y")
days = []
while start.year <= end.year and start.month <= end.month:
if start.year == end.year and start.month == end.month:
days.append(end.day - start.day)
else:
days.append(calendar.monthrange(
start.year, start.month)[1]-start.day+1)
start = start.replace(day=1)
start += relativedelta.relativedelta(months=1)
print(*days)
This includes the start day and excludes the end date, meaning that in your example, 4 days belong to January and 19 to March. Does February have 27 days only?

print first monday of every week of current month Flutter/Dart

I have a calendar in my Flutter App and I need to print a list of the weeks in the current month. But rather than starting on the 1st day of each month, it needs to start with the first Monday of the month (e.g. 05 April 2021 as the first Monday of April 2021). Then I need to print out the following weeks in that month, again starting from Monday. This includes the days of the next month that the final week of the current month follows on from (e.g. 26 April 2021 - 02 May 2021). It should print like this:
05 Apr - 11 Apr
12 Apr - 18 Apr
19 Apr - 25 Apr
26 Apr - 02 May
Start by figuring out how to find how many days there are from a given weekday to the Monday on or after that day. Examples help; if the given weekday is:
Monday, add 0 days.
Tuesday, add 6 days.
Wednesday, add 5 days.
... etc. ...
Sunday, add 1 day.
We could make a lookup table, but we also can devise that the offset (in days) from a given weekday to the Monday on or after that day has the form (7 - x) % 7
where x corresponds to the given weekday. We'd want that value to be 0 for Monday, 1 for Tuesday, and so on, until 6 for Sunday. Dart's DateTime.weekday uses values 1 (DateTime.monday) through 7 (DateTime.sunday), so we can easily map that to the value we want via DateTime.weekday - DateTime.monday.
Once we compute that offset, we can find the first day of the current month, add that offset to find the first Monday of the month, and then you can iteratively add 7 days until you reach the next month, and we can use DateFormat from package:intl to format the dates the way you want:
import 'package:intl/intl.dart';
String formatDate(DateTime dateTime) => DateFormat('dd MMM').format(dateTime);
void main() {
var now = DateTime.now();
var firstOfMonth = DateTime(now.year, now.month, 1);
var firstMonday =
firstOfMonth.addCalendarDays((7 - (firstOfMonth.weekday - DateTime.monday)) % 7);
var currentMonday = firstMonday;
while (currentMonday.month == now.month) {
var nextMonday = currentMonday.addCalendarDays(7);
var nextSunday = nextMonday.addCalendarDays(-1);
print('${formatDate(currentMonday)} - ${formatDate(nextSunday)}');
currentMonday = nextMonday;
}
}
See https://stackoverflow.com/a/68216029/ for the implementation of the addCalendarDays extension method.

How to convert an official DateTime to the astronomically exact DateTime?

I've been trying to figure out how to convert a birthday (DateTime) to the astronomically "exact" DateTime value. Timezone: UTC+1.
Example:
My friend was born 1984-01-27 11:35
1984 is a leap year. But 1700, 1800 and 1900 were not leap years. So until the 29. February of the year 2000 we are running behind in astronomoically exact time. In 1984 we are "almost" one day behind. So the astronomoically exact time would be after the official DateTime of my friend's birth, right?
These are the Gregorian calendar tweaks I know of:
Every year has 365 days
Every 4th year is a leap year (= has 366 days instead of 365)
Every 100th year is not a leap year
Every 400th year is a leap year (dispite the previous rule)
The additional day is added at the end of February (February has 29 days in a leap year)
Astronomoically a year has 365,2422 days.
Which means that a day is 24,0159254794 hours long.
A time value where the official and astronomoical times are "exactly" the same would be 2000-03-01T00:00:00, right?
So one would need to figure out how big the discrepancy between the official time and the astronomically exact time is at a given official time.
I've been thinking about it for hours, until my head started hurting. I figured I'll share my headache with you. Maybe you guys know any time library that can calculate this?
I came up with a "solution" that seems to be fairly accurate enough. Here's what it does:
The method starts at 1600-03-01T00:00. 18 years after Pope Gregor XIII. (after whom our Gregorian Calendar system is named) fixed the Julian Calendar (named after Julius Caesar) in 1582 by declaring that after the 4th October (Thursday) the next day would be the 15th October (Friday) - so there is actually no 5th to 14th October 1582 in history books - and also adding the 100th and 400th year rules to the calendar system.
The method sums up the discrepany between the official date and the exact date until the given date is reached.
At leap years it applies the correction added by Pope Gregor XIII. It does so at the end of February.
Code:
public static DateTime OfficialDateTimeToExactDateTime(DateTime dtOfficial)
{
const double dExactDayLengthInHours = 24.0159254794;
DateTime dtParse = new DateTime(1600, 3, 1, 0, 0, 0);
double dErrorInHours = 0.0;
while (dtParse <= dtOfficial)
{
dErrorInHours += dExactDayLengthInHours - 24;
dtParse = dtParse.AddDays(1);
if (dtParse.Month == 3 && dtParse.Day == 1 &&
((dtParse.Year % 4 == 0 && dtParse.Year % 100 != 0) ||
(dtParse.Year % 400 == 0)) )
{
dErrorInHours -= 24;
}
}
dErrorInHours += ((double)dtOfficial.Hour + (double)dtOfficial.Minute / 60 + (double)dtOfficial.Second / 3600) * (dExactDayLengthInHours - 24);
return dtOfficial.AddHours(dErrorInHours * -1);
}
I did some sanity testing:
If you pass a date before 2000-03-01T00:00 you get a negative correction. Because we measure days shorter as they in fact are.
If you pass a date after 2000-03-01T00:00 you get a positive correction. This is because 2000 is a leap year (while 1700, 1800 and 1900 are not), but the correction applied is too big. In 24 x 400 = 4800 years the correction would be about one day too big. So in the year 1600 + 4800 = 6400 (if man is still alive), you would need to delcare 6400 a non-leap year, despite the rules of the Gregorian calendar.

Find how many sundays in a month asp classic

I am trying to use asp classic to find how many working days (mon - sat) are in the month and how many are left.
any help or pointers greatly appreciated!
Here's how you can find the number of Sundays in a month without iteration. Somebody posted a JavaScript solution a few months back and I ported it to VBScript:
Function GetSundaysInMonth(intMonth, intYear)
dtmStart = DateSerial(intYear, intMonth, 1)
intDays = Day(DateAdd("m", 1, dtmStart) - 1)
GetSundaysInMonth = Int((intDays + (Weekday(dtmStart) + 5) Mod 7) / 7)
End Function
So, your total work days would just be the number of days in the month minus the number of Sundays.
Edit:
As #Lankymart pointed out in the comments, the above function gives you the number of Sundays in the month but it doesn't tell you how many are left.
Here's another version that does just that. Pass in any date and it will tell you how many Sundays are left in the month starting with that date. If you want to know how many Sundays are in a full month, just pass in the first day of the month (e.g., DateSerial(2014, 8, 1)).
Function GetSundaysRemainingInMonth(dtmStart)
intDays = Day(DateSerial(Year(dtmStart), Month(dtmStart) + 1, 1) - 1)
intDays = intDays - Day(dtmStart) + 1
GetSundaysRemainingInMonth = Int((intDays + (Weekday(dtmStart) + 5) Mod 7) / 7)
End Function
Edit 2:
#Cheran Shunmugavel was interested in some specifics about how this works. First, I just want to restate that I didn't develop this method originally. I just ported it to VBScript and tailored it to the OP's requirement (Sundays).
Imagine a February during a leap year. We have 29 days during the month. We know from the start that we have four full weeks, so each weekday will be represented at least four times. But that still leaves one addition day that's unaccounted for (29 Mod 7 = 1). How do we know if we get an extra Sunday from that one day? Well, in this case, it's pretty simple. Only if our start date is a Sunday can we count an extra Sunday for the month.
What if the month has 30 days? Then we have two extra days to account for. In that case, the start date can be a Saturday or a Sunday and we can count an extra Sunday for the month. And so it goes. So we can see that if we're X additional days within an upcoming Sunday, we can count an extra Sunday.
Let's put this in tabular form:
Addl Days Needed
Day To Count Sunday
---------- ----------------
Sunday 1
Saturday 2
Friday 3
Thursday 4
Wednesday 5
Tuesday 6
Monday 7
So what we need is a formula that we can apply to these situations so that they all result in the same value. We'll need to assign some value to each day and combine that value with the number of addition days needed for Sunday to count. Seems reasonable that if we assign an inverse value to the weekdays and add that to the number of additional days, we can get the same result.
Addl Days Needed Value Assigned
Day To Count Sunday To Weekday Sum
---------- ---------------- -------------- ---
Sunday 1 6 7
Saturday 2 5 7
Friday 3 4 7
Thursday 4 3 7
Wednesday 5 2 7
Tuesday 6 1 7
Monday 7 0 7
So, if weekday_value + addl_days = 7 then we count an extra Sunday. (We'll divide this by 7 later to give us 1 additional Sunday). But how do we assign the values we want to the weekdays? Well, VBScript's Weekday() function already does this but, unfortunately, it doesn't use the values we need by default (it uses 1 for Sunday through 7 for Saturday). We could change the way Weekday() works by using the second param, or we could just use a Mod(). This is where the + 5 Mod 7 comes in. If we take the Weekday() value and add 5, then mod that by 7, we get the values we need.
Day Weekday() +5 Mod 7
---------- --------- -- -----
Sunday 1 6 6
Saturday 7 12 5
Friday 6 11 4
Thursday 5 10 3
Wednesday 4 9 2
Tuesday 3 8 1
Monday 2 7 0
That's how the + 5 Mod 7 was determined. And, with that solved, the rest is easy(er)!
#Zam is on the right track you need to use WeekDay() function, here is a basic idea of how to script it;
<%
Dim month_start, month_end, currentdate, dayofmonth
Dim num_weekdays, num_past, num_future
Dim msg
'This can be configured how you like even use Date().
month_start = CDate("01/08/2014")
month_end = DateAdd("d", -1, DateAdd("m", 1, month_start))
msgbox(Day(month_end))
For dayofmonth = 1 To Day(month_end)
currentdate = CDate(DateAdd("d", dayofmonth, month_start))
'Only ignore Sundays
If WeekDay(currentdate) <> vbSunday Then
num_weekdays = num_weekdays + 1
If currentdate <= Date() Then
num_past = num_past + 1
Else
num_future = num_future + 1
End If
End If
Next
msg = ""
msg = msg & "Start: " & month_start & "<br />"
msg = msg & "End: " & month_end & "<br />"
msg = msg & "Number of Weekdays: " & num_weekdays & "<br />"
msg = msg & "Weekdays Past: " & num_past & "<br />"
msg = msg & "Weekdays Future: " & num_future & "<br />"
Response.Write msg
%>
How about using "The Weekday function returns a number between 1 and 7, that represents the day of the week." ?

How to convert year month and min information to day num

I want to convert the given year, month and min information to day of year info.
For eg lets say
year 2004, month 2, day 2 = 33rd day of year
how can I do it in matlab?
Get the datenum for Jan 1 of that year, and subtract it from the given yy/mm/dd. For example, today's day of the year:
jan1 = datenum(datestr(now,'yy'),'yy')
now - jan1 + 1
Check the above against here.
For a specific date,
>> yy = 2004; mm = 2; dd = 2;
>> doty = datenum(yy,mm,dd) - datenum(yy,1,0)
doty =
33