I'm trying to make a calendar using wpf. By using itemsPanel and more, I have a grid with 7 columns(sunday-saturday) and 6 rows(week# of month). If i can find the starting position of the first of each month by getting the weekday and week number(of the month), how can I find the week number(0-5 of each month)? Also can't I somehow just fill in the calendar from there? I'm lost and I don't know what else to try.
public partial class SchedulePage : Page
{
MainWindow _parentForm;
public int dayofweek;
public SchedulePage(MainWindow parentForm)
{
InitializeComponent();
_parentForm = parentForm;
// DateTime date = new DateTime(year, month, day);
_parentForm.bindings = new BindingCamper();
_parentForm.bindings.schedule.Add(new Schedule { WeekNo = (int) getWeekNumber(), WeekDay = dayofweek });
DataContext = _parentForm.bindings;
// lblTest.Content = dates(2011, 10, 27);
}
public double getWeekNumber()
{
dayofweek = getWeekDay(2011, 10, 31);
double h = dayofweek / 7;
double g = Math.Floor(h);
return g;
}
public int getWeekDay(int year, int month, int day)
{
//year = 2011;
//month = 10;
//day = 27;
int[] t = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
// year -= month < 3;
return (year + year / 4 - year / 100 + year / 400 + t[month - 1] + day) % 7;
}
You must use Calendar.GetDayOfWeek and Calendar.GetWeekOfYear in preference to writing yourself.
You can guarantee that if you write any date / time handling code yourself it will contain faults and won't work in different locales.
public class Row
{
public string MonthWeek { get; set; }
public string Year { get; set; }
public string Month { get; set; }
public string Day { get; set; }
public string WeekOfYear { get; set; }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var l = new List<Row>();
DateTime startDate = DateTime.Now;
DateTime d = new DateTime(startDate.Year, startDate.Month, 1);
var cal = System.Globalization.DateTimeFormatInfo.CurrentInfo.Calendar;
var ms = cal.GetWeekOfYear(new DateTime(d.Year, d.Month, 1), System.Globalization.CalendarWeekRule.FirstDay, System.DayOfWeek.Sunday);
for (var i = 1; d.Month == startDate.Month; d = d.AddDays(1))
{
var si = new Row();
var month_week = (d.Day / 7) + 1;
si.MonthWeek = month_week.ToString();
si.Month = d.Year.ToString();
si.Year = d.Month.ToString();
si.Day = d.Day.ToString();
si.WeekOfYear = cal.GetWeekOfYear(d, System.Globalization.CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString();
l.Add(si);
}
dataGrid1.ItemsSource = l;
}
}
together with the obligatory DataGrid in the XAML:
<DataGrid AutoGenerateColumns="true" Name="dataGrid1" />
You can use Calendar.GetWeekOfYear from Globalization to do this.
Here's the MSDN docs for it: http://msdn.microsoft.com/en-us/library/system.globalization.calendar.getweekofyear.aspx
You should pass the appropriate culture properties from CultureInfo.CurrentCulture to GetWeekOfYear so that you match the current culture properly.
Example:
int GetWeekOfYear(DateTime date)
{
return Calendar.GetWeekOfYear(
date,
CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule,
CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek
);
}
You could easily modify this into an extension method on DateTime:
static int GetWeekOfYear(this DateTime date)
{
return Calendar.GetWeekOfYear(
date,
CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule,
CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek
);
}
With #Polynomial answer, I have this error:
An object reference is required for the non-static field, method, or property...
If you instanciate GregorianCalendar before then you can call the method GetWeekOfYear !
private static int GetWeekNumber(DateTime time)
{
GregorianCalendar cal = new GregorianCalendar();
int week = cal.GetWeekOfYear(time, CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday);
return week;
}
Related
I have this challange that I finished which asked to print out a string according to the provided schedule. Here is an example:
var restaurant = new Restaurant(
new OpeningHour(8,16), // Sunday
new OpeningHour(8,17), // Monday
new OpeningHour(8,17), // Tuesday
new OpeningHour(8,17), // Wednesday
new OpeningHour(8,16), // Thursday
new OpeningHour(8,16), // Friday
new OpeningHour(8,16) // Saturday
);
expected output result = "Sun, Thu - Sat: 8-16, Mon - Wed: 8-17"
What I did was essentially:
Create a List of Days, OpenHours, and CloseHours
Create a HashSet of the days so that I can compare the days
Create a for loop according to HashSet and Days
Seperate the Start, Middle, and Ending
Concatenate the result according to the open and close hours as well as the gap between days
I have tried my best but I know for a fact that my code is not efficient at all, instead messy. I am trying to improve my C# skills please help. Here is my messy code:
namespace Livit
{
using System;
using System.Collections.Generic;
using System.Linq;
public class Restaurant
{
public WeekCollection<OpeningHour> OpeningHours { get; private set; }
public Restaurant() {
// No opening hours available for restaurant
}
public Restaurant(OpeningHour monday, OpeningHour tuesday, OpeningHour wednesday, OpeningHour thursday, OpeningHour friday, OpeningHour saturday, OpeningHour sunday)
{
OpeningHours = new WeekCollection<OpeningHour>(monday, tuesday, wednesday, thursday, friday, saturday, sunday);
}
// THE EMPHASIS OF THE CHALLANGE IS THIS FUNCTION RIGHT HERE!!!
// Parse the date into desired format
public string DateParser(List<DayOfWeek> days, List<TimeSpan> openHours, List<TimeSpan> closeHours)
{
HashSet<string> availableRanges = new HashSet<string>();
List<string> timeRanges = new List<string>();
DayOfWeek current = DayOfWeek.Sunday;
string result = "";
for (int i = 0 ; i < days.Count; i++){
string timeRange = openHours[i].ToString().Substring(1,1)+'-'+closeHours[i].ToString().Substring(0,2);
availableRanges.Add(timeRange);
timeRanges.Add(timeRange);
}
List<string> arToList= availableRanges.ToList();
for (int i = 0 ; i < arToList.Count; i++)
{
for (int j = 0 ; j < timeRanges.Count; j++){
if(timeRanges[j] == arToList[i]){
// First Item
if(j==0 ){
result += days[j].ToString().Substring(0,3);
}
// Last Item
else if(j==timeRanges.Count-1){
char last = result.Last();
if(last != ' '){
result += " - ";
}
result += days[j].ToString().Substring(0,3);
}
// Everything in the middle
else{
if(days[j]-current > 1){
result += ", ";
}
if(timeRanges[j] != timeRanges[j-1] ){
result += days[j].ToString().Substring(0,3);
} else if (timeRanges[j] == timeRanges[j-1]){
char last = result.Last();
if(last != ' '){
result += " - ";
}
if(timeRanges[j] != timeRanges[j+1]){
result += days[j].ToString().Substring(0,3);
}
}
}
current = days[j];
}
}
result += ": " + arToList[i];
if(i!=arToList.Count-1){
result += ", ";
}
}
Console.WriteLine(result);
return result;
}
public string GetOpeningHours()
{
// Declare List for each attribute
List<DayOfWeek> days = new List<DayOfWeek>();
List<TimeSpan> openHours = new List<TimeSpan>();
List<TimeSpan> closeHours = new List<TimeSpan>();
// Call the opening and closing hours from each day and feed into new array
foreach (DayOfWeek day in Enum.GetValues(typeof(DayOfWeek)).OfType<DayOfWeek>().ToList()) {
TimeSpan openHour = OpeningHours.Get(day).OpeningTime;
TimeSpan closeHour = OpeningHours.Get(day).ClosingTime;
days.Add(day);
openHours.Add(openHour);
closeHours.Add(closeHour);
}
return DateParser(days,openHours,closeHours);
throw new NotImplementedException();
}
}
public class OpeningHour
{
public TimeSpan OpeningTime { get; private set; }
public TimeSpan ClosingTime { get; private set; }
public OpeningHour(TimeSpan openingTime, TimeSpan closingTime)
{
OpeningTime = openingTime;
ClosingTime = closingTime;
}
public OpeningHour(int openingHour, int closingHour)
{
OpeningTime = TimeSpan.FromHours(openingHour);
ClosingTime = TimeSpan.FromHours(closingHour);
}
}
public class WeekCollection<T>
{
private Dictionary<DayOfWeek, T> _collection;
public WeekCollection(T sunday, T monday, T tuesday, T wednesday, T thursday, T friday, T saturday)
{
_collection = new Dictionary<DayOfWeek, T>();
_collection.Add(DayOfWeek.Sunday, sunday);
_collection.Add(DayOfWeek.Monday, monday);
_collection.Add(DayOfWeek.Tuesday, tuesday);
_collection.Add(DayOfWeek.Wednesday, wednesday);
_collection.Add(DayOfWeek.Thursday, thursday);
_collection.Add(DayOfWeek.Friday, friday);
_collection.Add(DayOfWeek.Saturday, saturday);
}
public T Get(DayOfWeek dayOfWeek)
{
return _collection[dayOfWeek];
}
}
}
Currently, I am still trying to find a better way in doing this challange. Any help would be appriciated.
P.S. I highlighted the part where my concatenation is occuring, this part is basically the emphasis of the whole challange
Been stuck on this task for a while now any help would be greatly appreciated.
So I have user input of Patient ID, Staff ID, visitType and DateTime from the Presentation Layer which I want to add to a list through the Business Layer.
The dateTime is inputted as a string and I can store It as a string fine but what I am trying to do is convert it into DateTime and then be able to store it in a list. this is where I encounter errors.
here is my code in presentation layer(MainWindow.cs), where I am pulling the information to be stored;
private void BtnAddVisit_Click(object sender, RoutedEventArgs e)
{
txtOutput.Text = "";
try
{
if (healthSystem.addVisit(new int[2] { 1, 3 }, 1, visitTypes.assessment, "01/01/2020 09:00")) //Should be OK
txtOutput.Text += "Visit 1 added.\n";
}
catch (Exception ex)
{
txtOutput.Text += ex.Message;
}
txtOutput.Text += healthSystem.getVisitList();
}
and here is my code in the business layer(HealthFacade);
public Boolean addVisit(int[] staff, int patient, int type, string dateTime)
{
//if the number of objects in the visit list is equal to 6, clear the list to avoid repeated output in textbox
if (visit.Count == 6)
{
visit.Clear();
}
//converting from string to dateTime
for (int i = 0; i < visit.Count; i++)
{
//the original task was to store 6 instances so thats why is says < 7
if (visit.Count < 7)
{
DateTime oDate = Convert.ToDateTime(dateTime);
}
}
//adding all the visits, regardless of if the data is valid or not
Visit v = new Visit();
v.Staff = staff;
v.Patient = patient;
v.Type = type;
v.DateTime = oDate;
//adds instance of visit to the visit list
visit.Add(v);
return true;
My understanding would be that I would then write v.DateTime = oDate; but it tells me 'oDate' does not exist in the current context.
the code for my Visit class is here;
using System;
using System.Collections.Generic;
using System.Text;
namespace BusinessLayer
{
class Visit
{
int[] staff;
int patient, type;
string dateTime;
public Visit()
{
}
// Constructor for Staff, using example from week 5 practical
public Visit(int [] aStaff, int aPatient, int aType, string aDateTime)
{
staff = aStaff;
patient = aPatient;
type = aType;
dateTime = aDateTime;
}
public int[] Staff
{
set { staff = value; }
get { return staff; }
}
public int Patient
{
set { patient = value; }
get { return patient; }
}
public int Type
{
set { type = value; }
get { return type; }
}
public string DateTime
{
set { dateTime = value; }
get { return dateTime; }
}
}
}
The reason I am trying to do this is so that I can set up a doctors appointments system and make sure that no 2 appointments are at the same time and therefore clash.
Thanks in advance for any help you can give!
The problem here is that the variable oDate only exists in the scope where you declared it.
In your case it's only usable inside your if statement.
Your function should look like this for you to access the variable when needed:
public Boolean addVisit(int[] staff, int patient, int type, string dateTime)
{
//if the number of objects in the visit list is equal to 6, clear the list to avoid repeated output in textbox
if (visit.Count == 6)
{
visit.Clear();
}
DateTime oDate = Convert.ToDateTime(dateTime);;
//converting from string to dateTime
for (int i = 0; i < visit.Count; i++)
{
//the original task was to store 6 instances so thats why is says < 7
if (visit.Count < 7)
{
//DateTime oDate = Convert.ToDateTime(dateTime);
// Because you definded oDate inside your if clause it is only accessible inside the clause
}
}
//adding all the visits, regardless of if the data is valid or not
Visit v = new Visit();
v.Staff = staff;
v.Patient = patient;
v.Type = type;
v.DateTime = oDate;
//adds instance of visit to the visit list
visit.Add(v);
return true;
Convert.ToDateTime may not work with different local settings. Because a DateTime is not allways dd/MM/yyyy HH:mm.
You can try like this if you are sure that your datetime string is allways formatted like this "01/01/2020 09:00".
var dateTime = "01/01/2020 09:00";//Assume your date is that.
DateTime dtInvariant = DateTime.ParseExact(dateTime, "dd/MM/yyyy HH:mm", CultureInfo.InvariantCulture);
//If you want to work with strings,
var datePart = dateTime.Split(' ')[0];
var timePart = dateTime.Split(' ')[1];
var day = int.Parse(datePart.Split('/')[0]);
var month = int.Parse(datePart.Split('/')[1]);
var year = int.Parse(datePart.Split('/')[2]);
var hour = int.Parse(timePart.Split(':')[0]);
var minute = int.Parse(timePart.Split(':')[1]);
var myDate = new DateTime(year, month, day, hour,minute, 0);
I have two lists Schedule(Having Total Hours) And Registered (Having Time Intervals). I want to create a list that is having registered time covered with Schedule list total hours finished.
See the Upper image. The resultant list is covering Registered Times and ENd if it doesn't find any registration for left hours. It will just create further times according to hours left with their types.
Take a look at this sample:
class Task
{
public int duration;
public string type;
public int remaining;
public void Reset()
{
remaining = duration;
}
}
class Span
{
public int from;
public int to;
public int remaining;
public int duration => to - from;
public void Reset()
{
remaining = duration;
}
}
struct Assignment
{
public Span span;
public string type;
}
static IEnumerable<Assignment> AssignTasks(List<Task> tasks, List<Span> spans)
{
// add an infinite span to the end of list
spans.Add(new Span()
{
from = spans.Last().to,
to = int.MaxValue
});
// set remainings of tasks and spans by their total duration
foreach (Task task in tasks) { task.Reset(); }
foreach (Span span in spans) { span.Reset(); }
// set current task and span
int iTask = 0;
int iSpan = 0;
while (iTask < tasks.Count)
{
//find which is smaller: remaining part of current task, or
// remaining part of current span
int assigning =
tasks[iTask].remaining <= spans[iSpan].remaining ?
tasks[iTask].remaining : spans[iSpan].remaining;
// add a new assignment to results
yield return new Assignment()
{
span = new Span()
{
from = spans[iSpan].to - spans[iSpan].remaining,
to = spans[iSpan].to - spans[iSpan].remaining + assigning,
},
type = tasks[iTask].type
};
// update remaining parts of current task and span
tasks[iTask].remaining -= assigning;
spans[iSpan].remaining -= assigning;
// update counters if nothing is left
if (tasks[iTask].remaining == 0)
iTask++;
if (spans[iSpan].remaining == 0)
iSpan++;
}
}
In this piece of code, Task is equivalent to what you call "Scheduled", and Span is equivalent to "Registered". I removed From and To from Task because they seem irrelevant to the problem. I also added a Remaining field to both Task and Span classes, I use these to keep the unassigned part of tasks or spans since a portion of a task can be assigned to a portion of a span.
The key point, to make everything much easier, is adding an infinite span to the end of list of spans. Now there are more registered spans (resources) than our demands and we just need to simply assign them.
You can test it like this:
static void Main(string[] args)
{
List<Task> tasks = new List<Task>()
{
new Task() {duration = 4, type = "A"},
new Task() {duration = 2, type = "B"},
new Task() {duration = 6, type = "C"},
new Task() {duration = 8, type = "D"}
};
List<Span> spans = new List<Span>()
{
new Span() {from = 9, to = 10},
new Span() {from = 11, to = 13},
new Span() {from = 15, to = 20}
};
IEnumerable<Assignment> assignments = AssignTasks(tasks, spans);
Console.WriteLine("Tasks: duration, type");
foreach (Task task in tasks)
{
Console.WriteLine($"{task.duration}, {task.type}");
}
Console.WriteLine("\nSpans: from, to");
foreach (Span span in spans)
{
Console.WriteLine($"{span.from}, {span.to}");
}
Console.WriteLine("\nResults: from, to, type");
foreach (Assignment assignment in assignments)
{
Console.WriteLine($"{assignment.span.from}, {assignment.span.to}, {assignment.type}");
}
Console.ReadLine();
}
The outcome is:
Tasks: duration, type
4, A
2, B
6, C
8, D
Spans: from, to
9, 10
11, 13
15, 20
Results: from, to, type
9, 10, A
11, 13, A
15, 16, A
16, 18, B
18, 20, C
20, 24, C
24, 32, D
Here's my solution:
The following enum and classes represent the input format you requested.
public enum SType
{
A,
B,
C,
D
}
public class Schedule : Register
{
public SType Type { get; set; }
public Schedule(int from, int to, SType type) : base(from, to)
{
Type = type;
}
}
public class Register
{
public int From { get; set; }
public int To { get; set; }
public int TotalHours => To - From;
public Register(int from, int to)
{
From = from;
To = to;
}
}
Then, following your sample input:
var scheduled = new List<Schedule>()
{
new Schedule(0, 4, SType.A),
new Schedule(4, 6, SType.B),
new Schedule(6, 12, SType.C),
new Schedule(12, 20, SType.D)
};
var registered = new List<Register>()
{
new Register(9, 10),
new Register(11, 13),
new Register(15, 20)
};
This will retrieve the requested output:
var result = new List<Schedule>();
int elapsedHours = 0;
foreach (var s in scheduled)
{
int hours = s.TotalHours;
foreach (var r in registered)
{
if (elapsedHours <= r.To)
{
int from = Math.Max(elapsedHours, r.From);
if (r.TotalHours <= hours)
{
elapsedHours = r.To;
result.Add(new Schedule(from, elapsedHours, s.Type));
hours -= (elapsedHours - from);
}
else
{
elapsedHours = from + hours;
result.Add(new Schedule(from, elapsedHours, s.Type));
hours = 0;
}
}
}
if (hours > 0)
{
result.Add(new Schedule(elapsedHours, elapsedHours + hours, s.Type));
elapsedHours += hours;
}
}
Using the following classes to represent your source data:
public class Schedule {
public int From;
public int To;
public int TotalHours => To - From;
public string Type;
public Schedule(int from, int to, string type) {
From = from;
To = to;
Type = type;
}
}
public class Register {
public int From;
public int To;
public Register(int from, int to) {
From = from;
To = to;
}
}
Here is a for loop implementation that spreads each Schedule member across the Registered time intervals and then outputs the rest:
var ans = new List<Schedule>();
int currentRegistered = 0;
var rp = registered[currentRegistered];
var currentFrom = rp.From;
for (int curScheduled = 0; curScheduled < scheduled.Count; ++curScheduled) {
var s = scheduled[curScheduled];
for (var hours = s.TotalHours; hours > 0;) {
if (currentFrom >= rp.To)
rp = (++currentRegistered < registered.Count)
? registered[currentRegistered]
: new Register(currentFrom, int.MaxValue);
if (rp.From > currentFrom)
currentFrom = rp.From;
var newTo = (rp.To - currentFrom > hours) ? currentFrom + hours : rp.To;
ans.Add(new Schedule(currentFrom, newTo, s.Type));
hours -= newTo - currentFrom;
currentFrom = newTo;
}
}
I have the following datetimes:
Start = 15/12/2012 13:00:00
End = 16/02/2013 14:00:00
How can I split that in 3 parts for each month?
- 15-12-2012 13:00:00 -> 01-01-2013 00:00:00
- 01-01-2013 00:00:00 -> 01-02-2013 00:00:00
- 01-02-2013 00:00:00 -> 16-02-2013 14:00:00
The total timespan must remain the same.
Can this easily be done with LINQ?
sure, try this (with little helper class included)
Process:
var Start = DateTime.Parse("15 Dec 2012 13:00:00");
var End = DateTime.Parse("16 Feb 2013 14:00:00");
var runningDate = Start;
while (runningDate < End)
{
var nextMonthSeed = runningDate.AddMonths(1);
var to = DateHelper.Min(new DateTime(nextMonthSeed.Year, nextMonthSeed.Month, 1), End);
Console.WriteLine("{0} -> {1}", runningDate.ToString("dd-MM-yyyy HH:mm:ss"), to.ToString("dd-MM-yyyy HH:mm:ss"));
runningDate = to;
}
Helper class:
public static class DateHelper
{
public static DateTime Min(DateTime date1, DateTime date2)
{
return (date1 < date2 ? date1 : date2);
}
}
You could try something like these extension methods:
public static class SomeExtensions {
public static IEnumerable<Tuple<DateTime, DateTime>> GetIntervals(
this DateTime from,
DateTime to) {
var currentFrom = from;
var currentTo = from.AdvanceToStartOfNextMonth();
while (currentTo < to) {
yield return Tuple.Create(currentFrom, currentTo);
currentFrom = currentTo;
currentTo = currentFrom.AdvanceToStartOfNextMonth();
}
yield return Tuple.Create(currentFrom, to);
}
public static DateTime AdvanceToStartOfNextMonth(this DateTime #this) {
var newMonth = #this.Month + 1;
var newYear = #this.Year;
if (newMonth == 13) {
newMonth = 1;
newYear++;
}
return new DateTime(newYear, newMonth, 1);
}
}
and then use them like so:
public class Etc {
public static void Foo() {
DateTime start = ...
DateTime stop = ....
Tuple<DateTime, DateTime>[] intervals = start.GetIntervals(stop).ToArray();
// or simply
foreach (var interval in start.GetIntervals(stop))
Console.WriteLine(interval);
}
}
EDIT
And here's a little test I just tried out (and it looks alright, I think):
class Program {
static void Main(string[] args) {
DateTime start = DateTime.Now.Subtract(TimeSpan.FromDays(170));
DateTime stop = DateTime.Now;
foreach (var interval in start.GetIntervals(stop))
Console.WriteLine(interval);
Console.ReadKey(intercept: true);
}
}
and that produced these results (in a console app):
END OF EDIT
I wanna make a lesson scheduler. For example all friday's lessons same and should show on scheduler. How can I get all friday's date in a year?
I have some datafield for this ;
-Dayofweek int
-Starthour int
-finishhour int
just like this,notice 365 may be 366. you can judge it..
DateTime dt=DateTime.Parse(DateTime.Now.ToString("yyyy-01-01"));
for (int i = 0; i < 365;i++ )
{
if (dt.AddDays(i).DayOfWeek==DayOfWeek.Friday)
{
Console.WriteLine(dt.AddDays(i).ToShortDateString());
}
}
static IEnumerable<DateTime> GetAllDaysInYearByDayOfWeek(int year, DayOfWeek dayOfWeek)
{
var startOfYear = new DateTime(year, 1, 1);
var firstMatchingDate = startOfYear.AddDays((dayOfWeek - startOfYear.DayOfWeek + 7) % 7);
for (var dt = firstMatchingDate; dt.Year == year; dt = dt.AddDays(7))
yield return dt;
}
For example:
var fridays = GetAllDaysInYearByDayOfWeek(2013, DayOfWeek.Friday);
foreach (DateTime day in fridays)
Console.WriteLine(day.ToShortDateString());
Results:
01/04/2013
01/11/2013
01/18/2013
...
12/13/2013
12/20/2013
12/27/2013