1

I've got an ASP.net C# application which creates a list of all the weeks in a given year. e.g. selected year 2019, and will produce 31/12/2019 to 06/01/2019 and so on. see attached image. enter image description here

To produce this I am borrowing some code from an example I found on Stack Overflow here

Now I also have another list containing dates in the format dd/MM/yyyy, this is generated from an XML file, so I wanted to only show the weeks that match dates in the weeks of the year list and populate the drop down list when a date in my XML generated list is contained within it.

For example if I had a full week or even a day in my XML generated list which fell between the 31/12/2018 to 06/01/2019 I want to show it in the drop down list. Similarly if the XML generated list doesn't contain at least a day from that week then don't show it.

I've pasted the code I used to get the weeks of a given year below.

I'm not sure of any easy way to compare both lists. Any help would be greatly appreciated.

public List<string> FetchWeeks(int year)
{
    List<string> weeks = new List<string>();
    DateTime startDate = new DateTime(year, 1, 1);
    startDate = startDate.AddDays(1 - (int)startDate.DayOfWeek);
    DateTime endDate = startDate.AddDays(6);
    while (startDate.Year < 1 + year)
    {
        weeks.Add(string.Format("{0:dd/MM/yyyy} to {1:dd/MM/yyyy}", startDate, endDate));
        startDate = startDate.AddDays(7);
        endDate = endDate.AddDays(7);

    }
    //DropDownList1.Items.Add(weeks);
    return weeks;
}
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
cfcorp
  • 99
  • 2
  • 10
  • 1
    You got a problem there. The "start of the week" is not internationally defined. For some it starts on Monday, for others on Saturday. Welcome to Time, Timzones and Internationalsiation. Try not to rip your hair out: https://www.youtube.com/watch?v=-5wpm-gesOY | https://www.youtube.com/watch?v=0j74jcxSunY | Also how certain are you, that you actually **need** this function? Is there somebody elses code that could do it for you? – Christopher Oct 29 '19 at 17:03
  • i know i tried to avoid it, the current config is fine for what i am doing. – cfcorp Oct 29 '19 at 17:05
  • So does your list from XML contain strings that represent dates formatted as...? – NetMage Oct 29 '19 at 18:10

4 Answers4

0

instead of a list of string for dates, use a list of object that contains the week dates and a boolean defaulted to false.

public class WeekObj
{
    public string Week { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
    public bool IsValid { get; set; }
};

List<WeekObj> weeks= new WeekObj();
weeks.add(new WeekObj { "week string", startDate, endDate, false });

Loop through your list of days, and for each day go through the list of weeks and set it to true if the day is between startDate and endDate (which is now in DateTime obj), do that for false dates, no need to recompare true dates.

public static bool Between(DateTime input, DateTime date1, DateTime date2)
{
    return (input >= date1 && input <= date2);
}
terrencep
  • 675
  • 5
  • 16
0

If were trying to compare lists to determine a set of valid weeks, I would try to determine an absolute week index and use that in my work. Since weeks are not impacted by things like leap years or other date oddities, we can just count in 7-day intervals from the beginning of a known of date range. Forgive me if my C# is rusty, but something to the effect of:

public int ToWeekIndex(DateTime date)
{
   // Takes any date and maps it to a value that represents the week it resides in.
   Timespan ts = date - DateTime.MinValue // Monday, January 1, 0001;
   return ts.Days / 7; // Integer divide, drops the remainder.
}

public DateTime FromWeekIndex(int weekIndex)
{
   // Takes a week index and returns the Monday from it.
   Timespan ts = new Timespan(weekIndex * 7, 0, 0, 0); // Days, hours, minutes, seconds
   return DateTime.MinValue + ts;
}

Then to build out your weeks, you could do something to the effect of pseudocode:

all_weeks = []
for date in January 1, 2019 to December 31, 2019 step 7 days:
   week_index = ToWeekIndex(date)
   week_start = FromWeekIndex(week_index)
   week_end = week_start + 7 days - 1 second
   all_weeks += [week_start, week_end]
Chris Zacharias
  • 608
  • 5
  • 7
0

This is the weeks in the Year data you already have -

        class WeekData
        {
            public DateTime WeekStartDate { get; set; }
            public DateTime WeekEndDate { get; set; }

            public int WeekStartDay //Gets Day in the year for the Week Start Date
            {
                get { return WeekStartDate.DayOfYear; }
            }

            public int WeekEndDay //Gets Day in the year for the Week End Date
            {
                get { return WeekEndDate.DayOfYear; }
            }
        }

Dummy WeeksInTheYear data

            List<WeekData> weeks = new List<WeekData>
            {
                new WeekData{WeekStartDate = new DateTime(2019,10,6), WeekEndDate = new DateTime(2019,10,12)},
                new WeekData{WeekStartDate = new DateTime(2019,10,13), WeekEndDate = new DateTime(2019,10,19)},
                new WeekData{WeekStartDate = new DateTime(2019,10,20), WeekEndDate = new DateTime(2019,10,26)},
                new WeekData{WeekStartDate = new DateTime(2019,10,27), WeekEndDate = new DateTime(2019,11,2)}
            };

Dummy Dates from the XML feed

List<DateTime> xmlDates = new List<DateTime> { new DateTime(2019, 11, 1), new DateTime(2019, 10, 12), new DateTime(2019, 10, 31) };

Filtering

            var weeksINeed = new List<WeekData>();
            foreach (var date in xmlDates)
            {
                var weekINeed = weeks.Where(x => x.WeekStartDay <= date.DayOfYear && x.WeekEndDay >= date.DayOfYear)
                    .FirstOrDefault();
                if (!weeksINeed.Any(x => x.WeekStartDay == weekINeed.WeekStartDay))
                {
                    weeksINeed.Add(weekINeed);
                }
            }

Output -

            foreach (var weekdata in weeksINeed.OrderBy(x=>x.WeekStartDay))
            {
                Console.WriteLine($"WeekStartDate - {weekdata.WeekStartDate} WeekEndDate - {weekdata.WeekEndDate}");
            }

test output

Rakesh
  • 654
  • 4
  • 10
  • 23
0

Using some extension functions and LINQ, you can just generate the list directly from the XML Date List<string>.

First, an IEnumerable<> extension to select distinct by a lambda function:

public static class IEnumerableExt {
    public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> src, Func<T, TKey> keySelector, IEqualityComparer<TKey> comparer = null) {
        var seenKeys = new HashSet<TKey>(comparer);
        foreach (var e in src)
            if (seenKeys.Add(keySelector(e)))
                yield return e;
    }
}

Then some calendar extensions using the built-in ISOWeek methods to get the week of year (Based on your week date ranges, I assume you are using ISO 8601 Weeks):

public static class CalendarExt {
    public static int GetISO8601WeekOfYear(this DateTime aDate) => ISOWeek.GetWeekOfYear(aDate);
    public static DateTime FirstDateOfYear(this DateTime d) => new DateTime(d.Year, 1, 1);
    public static DateTime FirstDateOfISO8601Week(this DateTime aDate) => aDate.AddDays(-(((int)aDate.DayOfWeek + 6) % 7));
    public static DateTime LastDateofISO8601Week(this DateTime aDate) => aDate.FirstDateOfISO8601Week().AddDays(6);    
    public static DateTime FirstDateOfISO8601Week(int year, int weekNum) => ISOWeek.ToDateTime(year, weekNum, DayOfWeek.Monday);
    public static DateTime LastDateofISO8601Week(int year, int weekNum) => FirstDateOfISO8601Week(year, weekNum).AddDays(6);    

    // for .Net without ISOWeek
    //public static DateTime FirstDateOfISO8601Week(this DateTime aDate) => aDate.AddDays(-(((int)aDate.DayOfWeek + 6) % 7));
    //public static int GetISO8601WeekOfYear(this DateTime aDate) =>
    //    CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(aDate.AddDays(DayOfWeek.Monday <= aDate.DayOfWeek && aDate.DayOfWeek <= DayOfWeek.Wednesday ? 3 : 0), CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
}

Finally, given your list of string dates from XML in xmlDateStrings, you can compute the week ranges list:

var currentCulture = CultureInfo.CurrentCulture;
var ans = xmlDateStrings.Select(ds => DateTime.ParseExact(ds, "dd/MM/yyyy", currentCulture))
                        .DistinctBy(d => d.GetISO8601WeekOfYear())
                        .OrderBy(d => d) // assume XML is unsorted
                        .Select(d => $"{d.FirstDateOfISO8601Week().ToString("dd/MM/yyyy")} to {d.LastDateofISO8601Week().ToString("dd/MM/yyyy")}")
                        .ToList();
NetMage
  • 26,163
  • 3
  • 34
  • 55