Given a date how can I add a number of days to it, but exclude weekends. For example, given 11/12/2008 (Wednesday) and adding five will result in 11/19/2008 (Wednesday) rather than 11/17/2008 (Monday).
I can think of a simple solution like looping through each day to add and checking to see if it is a weekend, but I'd like to see if there is something more elegant. I'd also be interested in any F# solution.
using Fluent DateTime https://github.com/FluentDateTime/FluentDateTime
var dateTime = DateTime.Now.AddBusinessDays(4);
public DateTime AddBusinessDays(DateTime dt, int nDays)
{
int weeks = nDays / 5;
nDays %= 5;
while(dt.DayOfWeek == DayOfWeek.Saturday || dt.DayOfWeek == DayOfWeek.Sunday)
dt = dt.AddDays(1);
while (nDays-- > 0)
{
dt = dt.AddDays(1);
if (dt.DayOfWeek == DayOfWeek.Saturday)
dt = dt.AddDays(2);
}
return dt.AddDays(weeks*7);
}
Without over-complicating the algorithm, you could just create an extension method like this:
public static DateTime AddWorkingDays(this DateTime date, int daysToAdd)
{
while (daysToAdd > 0)
{
date = date.AddDays(1);
if (date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday)
{
daysToAdd -= 1;
}
}
return date;
}
I would use this extension, remember since it is an extension method to put it in a static class.
Usage:
var dateTime = DateTime.Now.AddBusinessDays(5);
Code:
namespace ExtensionMethods
{
public static class MyExtensionMethods
{
public static DateTime AddBusinessDays(this DateTime current, int days)
{
var sign = Math.Sign(days);
var unsignedDays = Math.Abs(days);
for (var i = 0; i < unsignedDays; i++)
{
do
{
current = current.AddDays(sign);
} while (current.DayOfWeek == DayOfWeek.Saturday ||
current.DayOfWeek == DayOfWeek.Sunday);
}
return current;
}
}
}
Source:
https://github.com/FluentDateTime/FluentDateTime/blob/master/src/FluentDateTime/DateTime/DateTimeExtensions.cs
int daysToAdd = weekDaysToAdd + ((weekDaysToAdd / 5) * 2) + (((origDate.DOW + (weekDaysToAdd % 5)) >= 5) ? 2 : 0);
To wit; the number of "real" days to add is the number of weekdays you're specifying, plus the number of complete weeks that are in that total (hence the weekDaysToAdd / 5) times two (two days in the weekend); plus a potential offset of two days if the original day of the week plus the number of weekdays to add "within" the week (hence the weekDaysToAdd mod 5) is greater than or equal to 5 (i.e. is a weekend day).
Note: this works assuming that 0 = Monday, 2 = Tuesday, ... 6 = Sunday. Also; this does not work on negative weekday intervals.
I created an extension that allows you to add or subtract business days.
Use a negative number of businessDays to subtract. It seems to work in all cases.
namespace Extensions.DateTime
{
public static class BusinessDays
{
public static System.DateTime AddBusinessDays(this System.DateTime source, int businessDays)
{
var dayOfWeek = businessDays < 0
? ((int)source.DayOfWeek - 12) % 7
: ((int)source.DayOfWeek + 6) % 7;
switch (dayOfWeek)
{
case 6:
businessDays--;
break;
case -6:
businessDays++;
break;
}
return source.AddDays(businessDays + ((businessDays + dayOfWeek) / 5) * 2);
}
}
}
Example:
using System;
using System.Windows.Forms;
using Extensions.DateTime;
namespace AddBusinessDaysTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
label1.Text = DateTime.Now.AddBusinessDays(5).ToString();
label2.Text = DateTime.Now.AddBusinessDays(-36).ToString();
}
}
}
F# flavor of http://stackoverflow.com/questions/1044688 's answer:
namespace FSharpBasics
module BusinessDays =
open System;
let private weekLength = 5
(*operation*)
let addBusinessDays (numberOfBusinessDays: int) (startDate: DateTime) =
let startWeekDay = startDate.DayOfWeek
let sign = Math.Sign(numberOfBusinessDays)
let weekendSlide, businessDaysSlide =
match startWeekDay with
| DayOfWeek.Saturday when sign > 0 -> (2, -1)
| DayOfWeek.Saturday when sign < 0 -> (-1, 1)
| DayOfWeek.Sunday when sign > 0 -> (1, -1)
| DayOfWeek.Sunday when sign < 0 -> (-2, 1)
| _ -> (0, 0)
let baseStartDate = startDate.AddDays (float weekendSlide)
let days = Math.Abs (numberOfBusinessDays + businessDaysSlide) % weekLength
let weeks = Math.Abs (numberOfBusinessDays + businessDaysSlide) / weekLength
let baseWeekDay = int baseStartDate.DayOfWeek
let oneMoreWeekend =
if sign = 1 && days + baseWeekDay > 5 || sign = -1 && days >= baseWeekDay then 2
else 0
let totalDays = (weeks * 7) + days + oneMoreWeekend
baseStartDate.AddDays (float totalDays)
[<EntryPoint>]
let main argv =
let now = DateTime.Now
printfn "Now is %A" now
printfn "13 business days from now would be %A" (addBusinessDays 13 now)
System.Console.ReadLine() |> ignore
0
This is better if anyone is looking for a TSQL solution. One line of code and works with negatives.
CREATE FUNCTION[dbo].[AddBusinessDays](#Date date,#n INT)RETURNS DATE AS BEGIN
DECLARE #d INT;SET #d=4-SIGN(#n)*(4-DATEPART(DW,#Date));
RETURN DATEADD(D,#n+((ABS(#n)+#d-2)/5)*2*SIGN(#n)-#d/7,#Date)END
Here is how I did it.
I had to calculate SLA (Service Level Agreement) due dates based on a start date and number of days, and account for weekends and public holidays:
public DateTime? CalculateSLADueDate(DateTime slaStartDateUTC, double slaDays)
{
if (slaDays < 0)
{
return null;
}
var dayCount = slaDays;
var dueDate = slaStartDateUTC;
var blPublicHoliday = new PublicHoliday();
IList<BusObj.PublicHoliday> publicHolidays = blPublicHoliday.SelectAll();
do
{
dueDate = dueDate.AddDays(1);
if ((dueDate.DayOfWeek != DayOfWeek.Saturday)
&& (dueDate.DayOfWeek != DayOfWeek.Sunday)
&& !publicHolidays.Any(x => x.HolidayDate == dueDate.Date))
{
dayCount--;
}
}
while (dayCount > 0);
return dueDate;
}
blPublicHoliday.SelectAll() is a cached in-memory list of public holidays.
(note: this is a cut down version for sharing publicly, there is a reason its not an extension method)
enter code public static DateTime AddWorkDays(DateTime dt,int daysToAdd)
{
int temp = daysToAdd;
DateTime endDateOri = dt.AddDays(daysToAdd);
while (temp !=0)
{
if ((dt.AddDays(temp).DayOfWeek == DayOfWeek.Saturday)|| (dt.AddDays(temp).DayOfWeek == DayOfWeek.Sunday))
{
daysToAdd++;
temp--;
}
else
{
temp--;
}
}
while (endDateOri.AddDays(temp) != dt.AddDays(daysToAdd))
{
if ((dt.AddDays(temp).DayOfWeek == DayOfWeek.Saturday) || (dt.AddDays(temp).DayOfWeek == DayOfWeek.Sunday))
{
daysToAdd++;
}
temp++;
}
// final enddate check
if (dt.AddDays(daysToAdd).DayOfWeek == DayOfWeek.Saturday)
{
daysToAdd = daysToAdd + 2;
}
else if (dt.AddDays(daysToAdd).DayOfWeek == DayOfWeek.Sunday)
{
daysToAdd++;
}
return dt.AddDays(daysToAdd);
}
DateTime oDate2 = DateTime.Now;
int days = 8;
for(int i = 1; i <= days; i++)
{
if (oDate.DayOfWeek == DayOfWeek.Saturday)
{
oDate = oDate.AddDays(2);
}
if (oDate.DayOfWeek == DayOfWeek.Sunday)
{
oDate = oDate.AddDays(1);
}
oDate = oDate.AddDays(1);
}
Given the number of the original day in the year D and original day in the week W and the number of workdays to add N, the next weekday number is
W + N % 5.
The next day in the year (with no wraparound check) is
D + ((N / 5) * 7) + N % 5).
This is assuming that you have integer division.
Formula will be: Workday(date,no.of days,(weekday(1)))
Try this. This will help.
Related
Hi I am using C# web app on visual studio.
I have written code to be able to distinguish if the time is between 6am-2pm - 2pm-10pm and 10pm - 6am...
The code runs like a dream for the 6-2 - 2-10 times but for the 10pm - 6am.. the code runs fine until midnight and then it just resets my counter to 0 and stays at 0 until 6am.. I can't get my head around why this is doing.. Does anybody have a solution..
public DateTime Shiftstart { get; set; }
public DateTime Shiftend { get; set; }
public string Itemseriesmaster { get; set; }
public string SeriesMasterId { get; set; }
public void CalcShiftPeriod() //constructor
{
DateTime now = DateTime.Now; //date time now
int currentHour = now.Hour; //hour now
int shiftHourStart;
if (currentHour >= 6 && currentHour <= 13)
{
shiftHourStart = 6;
}
else if (currentHour >= 14 && currentHour <= 21)
{
shiftHourStart = 14;
}
else
{
shiftHourStart = 22;
}
Shiftstart = now.Date.AddHours(shiftHourStart);
Shiftend = Shiftstart.AddHours(7);
Shiftend = Shiftend.AddMinutes(59);
Shiftend = Shiftend.AddSeconds(59);
}
The code is calculating total units packed and is working fine, and resets after the given time on both the 6-2 and 2 - 10 shifts..
Until it gets to the 10pm - 6am and then just completely stops at midnight.
BizManager biz = new BizManager();
DataTable dt = new DataTable();
if (DDLProduct.SelectedValue.Equals("G120C-2") || DDLProduct.SelectedValue.Equals("G120 PM240-2") )
{
RefreshMainGridTht(selectedProduct, shiftStart, shiftEnd
);
}
dt = biz.GetPacktstatisticsForShift(shiftStart, shiftEnd, selectedProduct);
GridView1.DataSource = dt.DefaultView;
GridView1.DataBind();
int sumActual = 0;
int sumTarget = 0;
biz.CalculatePackingTotals(dt, out sumActual, out sumTarget);
LabelTotal.Text = sumActual.ToString();
DateTime dtmNow = DateTime.Now;
TimeSpan tsIntoShift = dtmNow - shiftStart;
TimeSpan tsTotalShift = shiftEnd - shiftStart;
double p = tsIntoShift.TotalMinutes / tsTotalShift.TotalMinutes;
int adjustedTarget = Convert.ToInt32(sumTarget * p);
if (sumActual > sumTarget)
{
LabelTotal.ForeColor = Color.Lime;
}
else
{
LabelTotal.ForeColor = Color.Red;
}
}
catch (Exception ex)
{
ErrMsg = App.HandleError(MethodBase.GetCurrentMethod(), ex, string.Empty);
You have to distinguish between currentHour from 0 to 5 and currentHour from 22 to 23.
For example, consider currentHour is 1. Your calculation says that the shift starts at 22:00 of the current day (now.Date.AddHours(22)), which is obviously wrong because the shift already started at 22:00 of the previous day.
So from currentHour 0 to 5 you have to subtract a day from your Shiftstart.
One possible way to do that would be to set shiftHourStart to -2 for currentHour between 0 and 5.
if (currentHour < 6)
{
shiftHourStart = -2;
}
else if (currentHour >= 6 && currentHour <= 13)
{
shiftHourStart = 6;
}
else if (currentHour >= 14 && currentHour <= 21)
{
shiftHourStart = 14;
}
else
{
shiftHourStart = 22;
}
The error lies in adding the hours to the current day...
Shiftstart = now.Date.AddHours(shiftHourStart);
At 23:59 you are adding 22 hours to "2018-11-06".
One minute passes...
At 00:00 you are adding 22 hours to "2018-11-07".
That means your start time moves 24 hours over the course of that one minute.
You could distinguish this scenario as part of your if-statement. See the comments a) and b) below.
int currentHour = now.Hour;
DateTime date = now.Date; // a) This is today in most cases...
int shiftHourStart;
if (currentHour >= 6 && currentHour <= 13)
{
shiftHourStart = 6;
}
else if (currentHour >= 14 && currentHour <= 21)
{
shiftHourStart = 14;
}
else if (currentHour >= 22)
{
shiftHourStart = 22;
}
else
{
// midnight to 6am
date = date.AddDays(-1); // b) But not in this case
shiftHourStart = 22;
}
First store your time in a TimeSpan this will give you more flexibility if tomorrow shift start and ends at 30min. Then store those Timespan in a class that represent the time frame. And if you have a list of them make it a List. It will help reading and maintaining your code. For now it's a brunch of magic numbers.
To check if a time is in a Range, I used :
public static bool IsBetween(TimeSpan time, TimeSpan start, TimeSpan end)
=> (start <= end) ? time >= start && time <= end : time >= start || time <= end;
internal void TestMethod()
{
var timeSlots = new[] {
new TimeFrame { start= new TimeSpan(6,0,0) , end = new TimeSpan(13,0,0) },
new TimeFrame { start= new TimeSpan(14,0,0) , end = new TimeSpan(21,0,0) },
new TimeFrame { start= new TimeSpan(22,0,0) , end = new TimeSpan(6,0,0) }
};
var today = DateTime.Today;
var dayHours = Enumerable.Range(0, 24).Select(x => today.AddHours(x)).ToList();
foreach (var currentDateTime in dayHours)
{
var matchingRanges = timeSlots.Where(x => IsBetween(currentDateTime .TimeOfDay, x.start, x.end));
if (matchingRanges.Any())
{
var temp = matchingRanges.First();
Console.WriteLine($"-> {currentDateTime } is in range {temp.start}-{temp.end}");
Console.WriteLine($"\t ShiftHours= {temp.start}-{temp.end.Subtract(new TimeSpan(0, 0, 1))}");
}
else
{
Console.WriteLine($"no Match for {currentDateTime }");
}
}
}
As you added a comment about testing it at midnight , included a TestMethod where I create all hours of a day (from 00h to 23h) so you can easly see what going on.
I've been assigned with the task of calculating a time difference only counting working hours. After searching I was able to get this (it's kinda in Portuguese but I think it's understandable) :
if (!txt_data2.Text.Contains("_") && !string.IsNullOrEmpty(txt_data2.Text) && txt_data2.Text != null && !txt_hora2.Text.Contains("_") && !string.IsNullOrEmpty(txt_hora2.Text) && txt_hora2.Text != null)
{
TimeSpan hi = TimeSpan.Parse(txt_horainicio.Text);
TimeSpan hf = TimeSpan.Parse(txt_hora2.Text);
if (hi.Hours < 9 || hf.Hours > 18)
{
MessageBox.Show("Horas Inválidas");
}
else
{
if (MessageBox.Show("Inserir horas extraordinárias?", "Horas Extraordinárias", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes)
{
double extra;
TimeSpan horasextra;
Frm_Tempo frm1 = new Frm_Tempo();
if (frm1.ShowDialog() == DialogResult.OK)
{
horasextra = TimeSpan.Parse(frm1.txt_horasextra.Text);
extra = horasextra.TotalHours;
DateTime data1 = Convert.ToDateTime(txt_datainicio.Text);
TimeSpan hora1 = TimeSpan.Parse(txt_horainicio.Text);
DateTime dataentrega1 = Convert.ToDateTime(txt_data2.Text);
TimeSpan horaentrega1 = TimeSpan.Parse(txt_hora2.Text);
data1 = data1.Add(hora1);
dataentrega1 = dataentrega1.Add(horaentrega1);
double horas1 = 0;
double minutos1 = 0;
for (var i = data1; i < dataentrega1; i = i.AddMinutes(1))
{
if (i.DayOfWeek != DayOfWeek.Saturday && i.DayOfWeek != DayOfWeek.Sunday)
{
if (i.TimeOfDay.Hours >= 9 && i.TimeOfDay.Hours <= 18)
{
if (i.TimeOfDay.Hours >= 13 && i.TimeOfDay.Hours < 14)
{
}
else
{
minutos1++;
for (var x = data1; x < dataentrega1; x = x.AddHours(1))
{
horas1 = (minutos1 / 60) + extra;
}
}
}
}
}
TimeSpan tempo1 = TimeSpan.FromHours(horas1);
MySqlCommand UPDATE20 = new MySqlCommand("UPDATE tbl_orcamentos SET tempo ='" + tempo1 + "'WHERE id ='" + txt_cod.Text + "'", ligar);
UPDATE20.ExecuteNonQuery();
}
}
else
{
DateTime data = Convert.ToDateTime(txt_datainicio.Text);
TimeSpan hora = TimeSpan.Parse(txt_horainicio.Text);
DateTime dataentrega = Convert.ToDateTime(txt_data2.Text);
TimeSpan horaentrega = TimeSpan.Parse(txt_hora2.Text);
data = data.Add(hora);
dataentrega = dataentrega.Add(horaentrega);
float horas = 0;
float minutos = 0;
for (var i = data; i < dataentrega; i = i.AddMinutes(1))
{
if (i.DayOfWeek != DayOfWeek.Saturday && i.DayOfWeek != DayOfWeek.Sunday)
{
if (i.TimeOfDay.Hours >= 9 && i.TimeOfDay.Hours < 18)
{
if (i.TimeOfDay.Hours >= 13 && i.TimeOfDay.Hours < 14)
{
}
else
{
minutos++;
for (var x = data; x < dataentrega; x = x.AddHours(1))
{
horas = minutos / 60;
}
}
}
}
}
TimeSpan tempo = TimeSpan.FromHours(horas);
MySqlCommand UPDATE21 = new MySqlCommand("UPDATE tbl_orcamentos SET tempo ='" + tempo + "'WHERE id ='" + txt_cod.Text + "'", ligar);
UPDATE21.ExecuteNonQuery();
}
}
}
I'm using c# and a mysql database.
It seems to work but when the result was 48h, instead of "48:00:00", it's trying to update it to "2.00:00:00" which isn't valid as "tempo" it's a time field in mysql. I don't really know how to solve it and so far I've tried to make "horas" a datetime and then formatting it to the right format but it didn't work.
I'd really appreciate any help and I'm sorry if it's hard to understand, just ask and I'll try to explain further.
EDIT:
Adding the float "horas" which contains the number of hours into the Timespan:
TimeSpan tempo = TimeSpan.FromHours(horas);
The standard SQL data type for a difference in time is "interval". MySQL doesn't support the "interval" data type.
It can be confusing, because times of day and intervals use the same notation, but have different meanings. The value '1:00' means 1 o'clock if it's a time of day ("time" or "timestamp"). But the same value means one hour if it's an interval.
Also, "48:00:00" is a valid interval (48 hours), but it's not a valid time of day.
If you're using MySQL, calculate and store the interval in an integer representing the number of hours, minutes, or seconds, and format for display. For example, store two hours as the integer 7200 (seconds) or as the integer 120 (minutes), depending on the application's requirements. Format that integer as "2:00" for display. C#'s TimeSpan.FromMinutes and TimeSpan.FromSeconds will help.
If you want to play around with an open source dbms that supports intervals, look at PostgreSQL.
I want to allow the user to enter the year in blank but I don´t know where to put \s\s\s\s in the following expression.
Here is an example of what I need to do: if the user inserts 03-07-_____ the program must be executed every 3Th of July of each year(and it proceeds the same way if the user inserts blank date, month or year or twoo of this three)
System.Text.RegularExpressions.Regex rdate =
new System.Text.RegularExpressions.Regex(#"^((((0?[1-9]|[12]\d|3[01]|\s\s|\s\d)[\-](0?[13578]|1[02]|\s\s)[\-]((1[6-9]|[2-9]\d)?\d{2}))|((0?[1-9]|[12]\d|30)[\-](0?[13456789]|1[012])[\-]((1[6-9]|[2-9]\d)?\d{2}))|((0?[1-9]|1\d|2[0-8])[\-]0?2[\-]((1[6-9]|[2-9]\d)?\d{2}))|(29[\-]0?2[\-]((1[6-9]|[2-9]\d)?(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)|00)))|(((0[1-9]|[12]\d|3[01])(0[13578]|1[02])((1[6-9]|[2-9]\d)?\d{2}))|((0[1-9]|[12]\d|30)(0[13456789]|1[012])((1[6-9]|[2-9]\d)?\d{2}))|((0[1-9]|1\d|2[0-8])02((1[6-9]|[2-9]\d)?\d{2}))|(2902((1[6-9]|[2-9]\d)?(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)|00))))$");
Could someone help me?(this expression validates even a leap year)
Here is what You asked for
System.Text.RegularExpressions.Regex rdate =
new System.Text.RegularExpressions.Regex(#"^((((0?[1-9]|[12]\d|3[01]|\s\s|\s\d)[\-](0?[13578]|1[02]|\s\s)[\-]((1[6-9]|[2-9]\d)?\d{2}|\s\s\s\s))|((0?[1-9]|[12]\d|30)[\-](0?[13456789]|1[012])[\-]((1[6-9]|[2-9]\d)?\d{2}|\s\s\s\s))|((0?[1-9]|1\d|2[0-8])[\-]0?2[\-]((1[6-9]|[2-9]\d)?\d{2}|\s\s\s\s))|(29[\-]0?2[\-]((1[6-9]|[2-9]\d)?(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)|00|\s\s\s\s)))|(((0[1-9]|[12]\d|3[01])(0[13578]|1[02])((1[6-9]|[2-9]\d)?\d{2}))|((0[1-9]|[12]\d|30)(0[13456789]|1[012])((1[6-9]|[2-9]\d)?\d{2}))|((0[1-9]|1\d|2[0-8])02((1[6-9]|[2-9]\d)?\d{2}))|(2902((1[6-9]|[2-9]\d)?(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)|00))))$");
But I am not sure it is what You really want. The regex was already patched, I continued the way. But not only it is maintenance nightmare, it behaves strange. It accepts empty day, but only when month is january, march, may... or empty. Less lines of code is not always better. I would suggest to rewrite it completely. Something simmilar to this:
protected DateTime? getDateTimeFromParts(string day, string month, string year)
{
DateTime now = DateTime.Now;
int iyear;
if (string.IsNullOrWhiteSpace(year))
{
iyear = now.Year;
}
else
{
iyear = int.Parse(year);
if (iyear >= 0 && iyear < 100) { iyear += 2000; }
}
int imonth;
if (string.IsNullOrWhiteSpace(month))
{
imonth = now.Month;
}
else
{
imonth = int.Parse(month);
}
int iday;
if (string.IsNullOrWhiteSpace(day))
{
iday = now.Day;
}
else
{
iday = int.Parse(day);
}
if (iyear <= DateTime.MaxValue.Year && iyear >= DateTime.MinValue.Year)
{
if (imonth >= 1 && imonth <= 12)
{
if (DateTime.DaysInMonth(iyear, imonth) >= iday && iday >= 1)
return new DateTime(iyear, imonth, iday);
}
}
return null;
}
protected DateTime? getDateTime(string dateStr)
{
Regex r = new Regex(#"^(\d\d)(\d\d)((\d\d)?\d\d)$");
Match m = r.Match(dateStr);
if (m.Success)
{
return getDateTimeFromParts(m.Groups[1].Value, m.Groups[2].Value, m.Groups[3].Value);
}
r = new Regex(#"^(\d?\d|\s\d|\s\s)[-](\d?\d|\s\s)[-]((\d\d)?\d\d|\s\s\s\s)$");
m = r.Match(dateStr);
if (m.Success)
{
return getDateTimeFromParts(m.Groups[1].Value, m.Groups[2].Value, m.Groups[3].Value);
}
return null;
}
Let's say I have a list of dates in a table. Now I want to find all rows, which is in the same week as the date provided as an argument.
Let's say I have a table with:
2014-09-11
2014-09-12
2014-09-15
And I give this function the argument 2014-09-09, it has to look from monday->sunday, and realize that 09-11 and 09-12 is part of the week, but not 09-15.
How on earth to solve this?
I have thought on making a check on year, month and weeknumber, but you cannot guarantee that month is the same...
So how do you solve a problem like this?
DxCk's comment is valid. This solution will work even if the first or last week of the year cross two different years:
Check that the first day of the week for both dates fall on the same day.
Here is the code:
private bool DatesAreInTheSameWeek(DateTime date1, DateTime date2)
{
var cal = System.Globalization.DateTimeFormatInfo.CurrentInfo.Calendar;
var d1 = date1.Date.AddDays(-1 * (int)cal.GetDayOfWeek(date1));
var d2 = date2.Date.AddDays(-1 * (int)cal.GetDayOfWeek(date2));
return d1 == d2;
}
why not?
bool AreFallingInSameWeek(DateTime date1, DateTime date2)
{
return date1.AddDays(-(int)date1.DayOfWeek) == date2.AddDays(-(int)date2.DayOfWeek);
}
if you want to use any day other than Sunday as start of the week
bool AreFallingInSameWeek(DateTime date1, DateTime date2, DayOfWeek weekStartsOn)
{
return date1.AddDays(-GetOffsetedDayofWeek(date1.DayOfWeek, (int)weekStartsOn)) == date2.AddDays(-GetOffsetedDayofWeek(date2.DayOfWeek, (int)weekStartsOn));
}
int GetOffsetedDayofWeek(DayOfWeek dayOfWeek, int offsetBy)
{
return (((int)dayOfWeek - offsetBy + 7) % 7)
}
Check the DateTime.Year and Calendar.GetWeekOfYear(DateTime, ...). No need to check for the month.
EDIT: This is wrong but I can't delete it. See #Sparrow's answer below.
Use: public virtual int GetWeekOfYear(DateTime time,CalendarWeekRule rule,DayOfWeek firstDayOfWeek) of Calendar class
My requirement was to find DOBs falling on the current week. Hope this helps with your doubt. Basically the idea behind this code is as follows:
Change the DOB to current year birthday (eg; 24-02-1988 to 24-02-2018(current year).
Add a year, if the brithday week contains both dec and jan
Get the first day of today's week.
Get last day of today's week.
check if the current year birthday falls between first day and last day of today's week.
private bool DatesAreInTheSameWeek(DateTime birthday)
{
if (birthday == DateTime.MinValue)
{
return false;
}
else
{
var birtdayWithCurrentYear = new DateTime(DateTime.Today.Year, birthday.Month, birthday.Day);
if (birthday.Month == 1 && DateTime.Today.Month != 1)
{
birtdayWithCurrentYear = birtdayWithCurrentYear.AddYears(1);
}
DateTime firstDayInWeek = DateTime.Today.Date;
while (firstDayInWeek.DayOfWeek != CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek)
firstDayInWeek = firstDayInWeek.AddDays(-1);var lastDayInWeek = firstDayInWeek.AddDays(7);
return birtdayWithCurrentYear < lastDayInWeek && birtdayWithCurrentYear >= firstDayInWeek;
}
}
Since the accepted answer contains error as #DxCK mentioned in comment, here is my solution for this:
public static class DateExtensions
{
private static void Swap<T>(ref T one, ref T two)
{
var temp = one;
one = two;
two = temp;
}
public static bool IsFromSameWeek(this DateTime first, DateTime second, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
{
// sort dates
if (first > second)
{
Swap(ref first, ref second);
}
var daysDiff = (second - first).TotalDays;
if (daysDiff >= 7)
{
return false;
}
const int TotalDaysInWeek = 7;
var adjustedDayOfWeekFirst = (int)first.DayOfWeek + (first.DayOfWeek < firstDayOfWeek ? TotalDaysInWeek : 0);
var adjustedDayOfWeekSecond = (int)second.DayOfWeek + (second.DayOfWeek < firstDayOfWeek ? TotalDaysInWeek : 0);
return adjustedDayOfWeekSecond >= adjustedDayOfWeekFirst;
}
}
Also here is link to another correct solution with slightly different approach.
Find start and end dates for the first date's week. Then check if the second date is between those two.
public static bool DateInsideOneWeek(DateTime date1, DateTime date2)
{
DayOfWeek firstDayOfWeek = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
DateTime startDateOfWeek = date1.Date;
while(startDateOfWeek.DayOfWeek != firstWeekDay)
{ startDateOfWeek = startDateOfWeek.AddDays(-1d); }
DateTime endDateOfWeek = startDateOfWeek.AddDays(6d);
return date2 >= startDateOfWeek && date2 <= endDateOfWeek;
}
If you don't want to use the Calendar class you can use this function:
public static int WeekOfYear(DateTime dt)
{
int startDays = 0;
// first day of the year
DateTime firstJanuary = new DateTime(dt.Year, 1, 1);
if (firstJanuary.DayOfWeek == DayOfWeek.Tuesday)
{
startDays = 1;
}
else if (firstJanuary.DayOfWeek == DayOfWeek.Wednesday)
{
startDays = 2;
}
else if (firstJanuary.DayOfWeek == DayOfWeek.Thursday)
{
startDays = 3;
}
else if (firstJanuary.DayOfWeek == DayOfWeek.Friday)
{
startDays = 4;
}
else if (firstJanuary.DayOfWeek == DayOfWeek.Saturday)
{
startDays = 5;
}
else if (firstJanuary.DayOfWeek == DayOfWeek.Sunday)
{
startDays = 6;
}
if (DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek == DayOfWeek.Sunday)
{
startDays++;
startDays = startDays % 7;
}
return ((dt.DayOfYear + startDays - 1) / 7) + 1;
}
Accepted answer doesn't work for a french calendar and when the dates are 03/10/2022 and 09/10/2022.
This worked for me :
public static partial class DateTimeExtensions
{
public static DateTime FirstDayOfWeek(this DateTime dt)
{
var culture = System.Threading.Thread.CurrentThread.CurrentCulture;
var diff = dt.DayOfWeek - culture.DateTimeFormat.FirstDayOfWeek;
if (diff < 0)
{
diff += 7;
}
return dt.AddDays(-diff).Date;
}
public static DateTime LastDayOfWeek(this DateTime dt) =>
dt.FirstDayOfWeek().AddDays(6);
public static DateTime FirstDayOfMonth(this DateTime dt) =>
new DateTime(dt.Year, dt.Month, 1);
public static DateTime LastDayOfMonth(this DateTime dt) =>
dt.FirstDayOfMonth().AddMonths(1).AddDays(-1);
public static DateTime FirstDayOfNextMonth(this DateTime dt) =>
dt.FirstDayOfMonth().AddMonths(1);
public static int GetWeeekNumber(this DateTime dt)
{
CultureInfo culture = System.Threading.Thread.CurrentThread.CurrentCulture;
Calendar myCal = culture.Calendar;
// Gets the DTFI properties required by GetWeekOfYear.
CalendarWeekRule myCWR = culture.DateTimeFormat.CalendarWeekRule;
DayOfWeek myFirstDOW = culture.DateTimeFormat.FirstDayOfWeek;
return myCal.GetWeekOfYear(dt, myCWR, myFirstDOW);
}
public static bool IsInTheSameWeek(this DateTime date1, DateTime date2)
{
return date1.GetWeeekNumber() == date2.GetWeeekNumber();
}
}
Usage :
item.Week.IsInTheSameWeek(Week)
I need a function which gives me the nth DateTime of the next Month.
For Example: I need the 4th Wednesday of next Month.
My code delivers a wrong Date:
private static DateTime FindNextDay(DayOfWeek dayOfWeek, DateTime fday, Int32 instance)
{
DateTime day = new DateTime(fday.Year, fday.Month, 1, fday.Hour, fday.Minute, fday.Second);
// DateTime day is in this Example= 2014-08.01 11.00 AM
if (instance == 2)
{
day = day.AddDays(7);
}
else if (instance == 3)
{
day = day.AddDays(14);
}
else if (instance == 4) //if the 4th week is requested
{
day = day.AddDays(28); // i add 28 days
}
while (day.DayOfWeek != dayOfWeek)
{
day = day.AddDays(1); // and search the wednesday and return it back
}
return day;
}
Could you show me a better solution?
Something like this..
Get the first day of the next month, keep a count of the number of wednesdays you've encountered adding to it when you find one. Return when the count is 4.
private static DateTime GetFourthWednesday()
{
DateTime firstOfMonth = new DateTime(DateTime.Now.AddMonths(1).Year, DateTime.Now.AddMonths(1).Month, 1);
int count = 0;
while (count < 4)
{
if (firstOfMonth.DayOfWeek == DayOfWeek.Wednesday)
{
count++;
}
if (count == 4)
{
return firstOfMonth;
}
firstOfMonth = firstOfMonth.AddDays(1);
}
return firstOfMonth;
}
Gives 27/08/2014 if run today
Change the last else if to:
else if (instance == 4) //if the 4th week is requested
{
day = day.AddDays(21); // i add 28 days
}
You should add 21 days not 28.
Since because I'm too lazy, I wrote my code like;
DateTime firstDayOfNextMonth = new DateTime(2014, DateTime.Now.Month + 1, 1);
int count = 0;
if (firstDayOfNextMonth.DayOfWeek == DayOfWeek.Wednesday)
count = 1;
while (count != 4)
{
firstDayOfNextMonth = firstDayOfNextMonth.AddDays(1);
if (firstDayOfNextMonth.DayOfWeek == DayOfWeek.Wednesday)
count++;
}
Console.WriteLine(firstDayOfNextMonth);
Basicly, I check if the first day of next month is Wednesday or not, then I iterate my DateTime to found 4. Wednesday in next month.
It is working for today and it prints 27.08.2014 which is fourth Wednesday of next month.
You can write an extension method like;
public static class DateTimeExtensions
{
public static void FindInstanceNextMonth(DateTime Now, DayOfWeek day, int instance)
{
DateTime firstDayOfNextMonth = new DateTime(Now.Year, Now.Month + 1, 1);
int count = 0;
if (firstDayOfNextMonth.DayOfWeek == day)
count = 1;
while (count != instance)
{
firstDayOfNextMonth = firstDayOfNextMonth.AddDays(1);
if (firstDayOfNextMonth.DayOfWeek == day)
count++;
}
Console.WriteLine(firstDayOfNextMonth);
}
}
And call it as;
DateTimeExtensions.FindInstanceNextMonth(DateTime.Now,
DayOfWeek.Wednesday,
4);