Split string into struct - c#

Is there a way assign split parts into struct?
I have strings that show times:
10:25, 10:55
11:05, 11:50
12:20, 13:10
I am iterating theses lines from text file and want to assign to ParkTime struct
public struct ParkTime
{
public string startTime { get; set; }
public string endTime { get; set; }
}
I tried to use LINQ but, first I need to validate lines using regular expressions.

I suggest to use DateTime in your struct (or better a class but this is not important here). In this way the validation of the input is more focused to the expected value and you could avoid using regex expressions
List<ParkTime> parktimes = new List<ParkTime>();
foreach (string line in File.ReadLines("file.txt"))
{
bool isValid = true;
string[] times = line.Split(',');
DateTime dtInit;
if(!DateTime.TryParseExact(times[0].Trim(), "HH:mm",
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.NoCurrentDateDefault,
out dtInit))
isValid = false;
DateTime dtEnd;
if (!DateTime.TryParseExact(times[1].Trim(), "HH:mm",
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.NoCurrentDateDefault,
out dtEnd))
isValid = false;
if (isValid)
parktimes.Add(new ParkTime() { startTime = dtInit, endTime = dtEnd });
}
public struct ParkTime
{
public DateTime startTime { get; set; }
public DateTime endTime { get; set; }
}
And about using class or struct to model your data, this is a classical answer from E.Lippert (main developer for the C# Language)

You can use Linq Where to only select valid lines and then Select to create structs out of the lines.
lines
.Where(line => validate(line))
.Select(line => new ParkTime() {startTime = getStartTime(line), endTime = getEndTime(line));
Here are the methods used and their signature:
public bool validate(string line){
return true; //validate if the line is correct
}
public string getStartTime(string line){
return null; //select the start time from the line
}
public string getEndTime(string line){
return null; //select the end time from the line
}

For task like this, I use a simple TimeSlot struct which holds a timespan rather than an end time. Some operations are easier or more direct but, of course, that will depend on your planned usage for the parking hours.
public struct TimeSlot
{
private DateTime _start;
private TimeSpan _span;
public DateTime Start
{
get
{
if (_start == null)
{
_start = DateTime.Today;
}
return _start;
}
set
{
_start = value;
}
}
public TimeSpan Span
{
get
{
if (_span == null)
{
_span = new TimeSpan(0);
}
return _span;
}
set
{
if (value.Ticks >= 0)
{
_span = value;
}
}
}
public DateTime End
{
get
{
return Start.Add(Span);
}
}
public TimeSlot(DateTime start, TimeSpan span)
{
_start = start;
_span = span.Ticks >= 0 ? span : new TimeSpan(0);
}
}

If you know how ti iterate your lines you just need to split each line and then assing trimmed values to properties.
ParkTime pt = new ParkTime();
string[] arr = "10:25, 10:55".Split(",");
pt.startTime = arr[0].Trim();
pt.endTime = arr[1].Trim();

Related

How to pass time value from page.cs to ViewModel

How to paas time value from page.cs to ViewModel.
timevalue is string like "14h:15m"
Here I'm trying...
Page.cs
_model.StartTime = int.Parse(timevalue);
ViewModel
public DateTime StartTime { get; set; } = DateTime.Today;
public DateTime EndTime { get; set; }
public int Duration { get; set; }
private TimeSpan[] _StartTimeValues { get; set; }
private int _StartTimeIndex = -1;
public int StartTime
{
get { return _StartTime; }
set
{
_StartTimeIndex = value;
if (value >= 0)
{
StartTime =StartTime.Date.Add(_StartTimeValues[value]);
EndTime = StartTime.AddMinutes(Duration);
}
OnPropertyChanged(nameof(StartTime));
}
}
At first, please change the _StartTimeIndex's type to SpanTime, because the TimeDate.Add need a parameter type of SpanTime. Such as:
string time = "12h45m";
char[] hourandminute = new char[] { 'h', 'm' };
string[] temp = time.Split(hourandminute);
int hour = int.Parse(temp[0]);
int minutes = int.Parse(temp[1]);
TimeSpan timeSpan = new TimeSpan(hour, minutes, 0);

Extracting information out of a dictionary...An alternative to if/else statements

I have a dictionary that looks like this:
Dictionary<string, DateTime> Seasons = new Dictionary<string, DateTime>
{
{ "WINTER_START", Date1},
{ "WINTER_END", Date2 },
{ "SUMMER_START", Date3 },
{ "SUMMER_END", Date4 }
};
Let's say I have today's date var today = DateTime.Today.Date and I want to know if today's date falls in winter or summer. I've done this using an If/Else statement, but something inside me tells me there is a better way..
string currentSeason = "";
if (today >= Seasons["WINTER_START"] && today <= Seasons["WINTER_END"])
{
currentSeason = "WINTER";
}
else if (today >= Seasons["SUMMER_START"] && today <= Seasons["SUMMER_END"])
{
currentSeason = "SUMMER";
}
Not sure if you are locked down to using a Dictionary for this, but i think this is a friendlier version and how i would've tackled the problem:
void Main()
{
var seasons = new List<Season>
{
new Season("Winter", Date1, Date2),
new Season("Summer", Date3, Date4)
};
var today = DateTime.Today;
// null if no matching season was found
string currentSeason = seasons.FirstOrDefault(season => season.InSeason(today))?.Name;
}
public class Season
{
public Season(string name, DateTime start, DateTime end)
{
Name = name;
Start = start;
End = end;
}
public string Name { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
public bool InSeason(DateTime input)
{
return input >= Start && input <= End;
}
}
Also a minor remark: Current_Season is not a good variable name for a local variable. This might help you improve your naming.

How can I safely return a null when an invalid record is found?

I've got this code:
private static QueuedReports GetSingleReportForUnit(string unit, int RptID, DateTime nextExDate)
{
ReportSchedule rs = ReportSchedulerSQL.GetReportSchedulerRecord(unit, RptID);
DateTime d8 = new DateTime(0001, 1, 1); //DateTime.Now.AddYears(1);
if (rs.NextExecution.Date == d8.Date)
{
;// return null; <= I get, "cannot access a closed stream" with this
}
QueuedReports qr = new QueuedReports();
qr.Unit = unit;
qr.ReportName = GetReportNameForID(RptID);
List<String> emailAddresses = ReportSchedulerSQL.GetEmailAddressesForUnitRpt(unit, RptID);
qr.AllEmailAddresses = string.Join(",", emailAddresses.ToArray());
qr.NextExecution = nextExDate;
qr.NextExecutionsBeginDateArg = GetNextExecutionsBeginDateArg(unit, RptID, nextExDate);
qr.NextExecutionsEndDateArg = GetNextExecutionsEndDateArg(unit, RptID, nextExDate);
return qr;
}
...which is called from here:
private static IEnumerable<QueuedReports> GetAllFutureReportsForUnit(string unit, int RptID, DateTime finalDate)
{
List<QueuedReports> listToReturn = new List<QueuedReports>();
DateTime currentDate = DateTime.Now;
while (currentDate <= finalDate)
{
currentDate = ReportSchedulerConstsAndUtils.GetNextDateForUnitReportAfter(unit, RptID, currentDate);
var qr = GetSingleReportForUnit(unit, RptID, currentDate);
listToReturn.Add(qr);
}
return listToReturn;
}
If a valid record is found in GetSingleReportForUnit(), that is to say, a "NextExecution" of a value other than "0001, 1, 1", all is well; however, if not, my attempt to return null compiles, but fails at runtime with "cannot access a closed stream"
How can I short-circuit execution of GetSingleReportForUnit() when an invalid date (January 1st of the year 1) inhabits rs.NextExecution?
QueuedReports is a custom class:
public class QueuedReports
{
public string Unit { get; set; }
public string ReportName { get; set; }
public DateTime NextExecution { get; set; }
public string AllEmailAddresses { get; set; }
public DateTime NextExecutionsBeginDateArg { get; set; }
public DateTime NextExecutionsEndDateArg { get; set; }
}
Why would the act of returning null attempt to access a stream? AFAICT, there isn't any stream in this code, so WTH?
I think you are looking for continue :
private static IEnumerable<QueuedReports> GetAllFutureReportsForUnit(string unit, int RptID, DateTime finalDate)
{
List<QueuedReports> listToReturn = new List<QueuedReports>();
DateTime currentDate = DateTime.Now;
while (currentDate <= finalDate)
{
currentDate = ReportSchedulerConstsAndUtils.GetNextDateForUnitReportAfter(unit, RptID, currentDate);
if (currentDate == DateTime.MinValue)
continue;
var qr = GetSingleReportForUnit(unit, RptID, currentDate);
listToReturn.Add(qr);
}
return listToReturn;
}
If your currentDate is January 1st of the year 1 then continue will pass control to the next iteration of your while loop.
This all depends on what rs.NextExecution is doing and how it works. If some sort of stream is being opened in there and it can't return the object that has the Date property on it then your if statement isn't going to work.
You can use a try catch block to catch whatever exception is being thrown and return null within the catch statement block. You could check if the stream is still valid within that NextExecution object instead of checking the date property if that's available.

How to make an if statement to show xml attribute based on current time

I have a XML like this:
<PrayerTime
Day ="1"
Month="5"
Fajr="07:00"
Sunrise="09:00"
Zuhr="14:00"
/>
A class like this:
public class PrayerTime
{
public string Fajr { get; set; }
public string Sunrise { get; set; }
public string Zuhr { get; set; }
}
And something to get the value like this:
XDocument loadedCustomData = XDocument.Load("WimPrayerTime.xml");
var filteredData = from c in loadedCustomData.Descendants("PrayerTime")
where c.Attribute("Day").Value == myDay.Day.ToString()
&& c.Attribute("Moth").Value == myDay.Month.ToString()
select new PrayerTime()
{
Fajr = c.Attribute("Fajr").Value,
Sunrise = c.Attribute("Sunrise").Value,
};
myTextBox.Text = filteredData.First().Fajr;
How can i based by current time of day say that if time is between the value of Fajr and the Value of Sunrise, then myTextBox should show the value of Fajr.
If value of current time is between sunrise and Zuhr, show Zuhr?
How can i get it to show the attribute name in myTextBox2?
For example, myTextBox shows value "07:00", and myTextBox2 shows "Fajr"?
First modify the class as per #abatischcev
public class PrayerTime
{
public TimeSpan Fajr { get; set; }
public TimeSpan Sunrise { get; set; }
public TimeSpan Zuhr { get; set; }
}
Then modify the linq query select part as:
select new PrayerTime()
{
Fajr = TimeSpan.Parse(c.Attribute("Fajr").Value),
Sunrise = TimeSpan.Parse(c.Attribute("Sunrise").Value),
Zuhr = TimeSpan.Parse(c.Attribute("Zuhr").Value)
};
then your check should be:
var obj = filteredData.First();
TimeSpan currentTime = myDay.TimeOfDay;
string result = String.Empty;
if (currentTime >= obj.Fajr && currentTime < obj.Sunrise)
{
result = "Fajar";
}
else if (currentTime >= obj.Sunrise && currentTime < obj.Zuhr)
{
result = "Zuhar";
}
textbox1.Text = result;
(By the way, Zuhr time should be between Zuhr and Asar :))
First, keep not string but TimeSpan object:
public TimeSpan Fajr { get; set; }
public TimeSpan Sunrise { get; set; }
To do this parse XML into DateTime:
TimeSpan ts = TimeSpan.Parse(c.Attribute("attr"));
So:
TimeSpan now = DateTime.Now.TimeOfDay; // time part only
var data = filteredData.First();
string result = null;
if (data.Fajr <= now && now < data.Sunrise); // notice operators greed
result = data.Fajr;
else if (data.Sunrise <= now && now <= data.Zuhr)
result = data.Zuhr;
myTextBox.Text = result;
The problem here is your code is "stringly typed". I would be better to use type that is design for time e.g. DateTime, but to quick fix it:
// don't you need a third one here?
select new PrayerTime()
{
Fajr = c.Attribute("Fajr").Value,
Sunrise = c.Attribute("Sunrise").Value,
};
Tu get current hour:
int currentHour = DateTime.Now.Hour;
Then is just simple comparison of two integers.
var data = filteredData.First();
int fajrHour = int.Parse(data.Fajr.Substring(0, 2), CultureInfo.InvariantCulture);
int sunriseHour = int.Parse(data.Sunrise.Substring(0, 2), CultureInfo.InvariantCulture);
int zuhrHour = int.Parse(data.Zuhr.Substring(0, 2), CultureInfo.InvariantCulture);
if(fajrHour <= currenHour && currenHour < sunriseHour)
{
myTextBox.Text = data.Fajr; // or to show value fajrHour.ToString()
}
if(sunriseHour <= currenHour && currenHour < zuhrHour)
{
myTextBox.Text = data.Zuhr; // zuhrHour.ToString()
}
// don't you need a third one here?

Enumerate Class Properties in C#

I have a class Similar to this
public class Model
{
public TimeSpan Time1 {get; set;}
public TimeSpan Time2 { get; set; }
public TimeSpan Time3 { get; set; }
public TimeSpan Time4 { get; set; }
}
Now Let's Imagine I have to populate the times during runtime and then Figure out the time remaining between Time 1 and Time 2, then when that passes Find the time remaining between Time2 and Time3 and so on. However, I need to take into account what the time is right now.
For Example:
Now it is 1:00 PM
Time1=5:00 AM
Time 2 = 12:00 PM
Time 3= 4:00 PM
Time 4 = 6:00 PM
So since the time is 1:00PM, I need to find the difference between Time 2 and Time 3
Now is there a smarter way other than reflection to determine this? Should i add something in my class
If you need to keep the existing structure of your class, you could add a method to enumerate through the times:
public class Model
{
public TimeSpan Time1 {get; set;}
public TimeSpan Time2 { get; set; }
public TimeSpan Time3 { get; set; }
public TimeSpan Time4 { get; set; }
public IEnumerable<TimeSpan> GetTimes()
{
yield return Time1;
yield return Time2;
yield return Time3;
yield return Time4;
}
}
And use it like this:
foreach (TimeSpan time in model.GetTimes())
{
// use the TimeSpan
}
Why would you not use an array or list?
public class Model
{
public List<DateTime> Dates { get; set; }
}
If I'm understanding your question correctly, why not have a Dictionary<TimeSpan, string> where the key is actual TimeSpan, and the value is the name, e.g. "Time1"? This way, you can sort the keys, find the pair that you need, and then get at their names.
You could add a property or method that would return the times as a list for easier processing. Then your external code could access the times as either a list or as individual properties. You still have to explicitly access each property, but at least it's from within the same class, so you're not coupling your external code to the data structure as tightly.
This is untested and needs some optimization, but just as a thought example, use an array and iterate through it.
public class Model
{
private TimeSpan[] _timeSpans = new TimeSpan[4] { new TimeSpan(), new TimeSpan(), new TimeSpan(), new TimeSpan() };
public TimeSpan Time1
{
get { return _timeSpans[0]; }
set { _timeSpans[0] = value; }
}
public TimeSpan Time2
{
get { return _timeSpans[1]; }
set { _timeSpans[1] = value; }
}
public TimeSpan Time3
{
get { return _timeSpans[2]; }
set { _timeSpans[2] = value; }
}
public TimeSpan Time4
{
get { return _timeSpans[3]; }
set { _timeSpans[3] = value; }
}
// DateTime.TimeOfDay holds the time portion of a time
public TimeSpan GetDifference(TimeSpan currentTime)
{
int start = -1;
for(int i = 0; i<_timeSpans.Length;i++)
{
if(_timeSpans[i] >= currentTime)
{
start = i;
break;
}
}
if(start == -1) throw new ArgumentException("handle the case where currentTime is smaller than all of them");
int end = (start + 1 < _timeSpans.Length) ? start + 1 : 0;
return _timeSpans[end] - _timeSpans[start];
}
Constructing an array provides for a simple linq statement to calculate the time span:
public class Model
{
public TimeSpan Time1 { get; set; }
public TimeSpan Time2 { get; set; }
public TimeSpan Time3 { get; set; }
public TimeSpan Time4 { get; set; }
public TimeSpan GetSpan(TimeSpan time)
{
var times = new[] { Time1, Time2, Time3, Time4 };
return Enumerable.Range(1, times.Length - 1)
.Select(i => new[] { times[i - 1], times[i] })
.Where(t => t[0] <= time && time < t[1])
.Select(t => t[1] - t[0])
.FirstOrDefault();
}
}

Categories