Is it possible to make a pattern for NodaTime's LocalTime, which matches the ISO 8601 standard for times? That is, can you make (minutes and) seconds optional, like you can make fractional seconds optional? I wish to be able to always have hours and minutes, but add everything else only when needed.
This doesn't exist in NodaTime currently, nor does it exist as an option for the built in DateTime and DateTimeOffset objects.
Probably the best you could do is to create two patterns and add some logic for which to use.
var p1 = LocalTimePattern.ExtendedIsoPattern;
var p2 = LocalTimePattern.CreateWithInvariantCulture("HH:mm");
// formatting
LocalTime t = // your input
var p = t.Second == 0 && t.TickOfSecond == 0 ? p2 : p1;
string s = t.Format(p);
// parsing
string s = // your input
var result = p1.Parse(s);
if (!result.Success)
result = p2.Parse(s);
if (!result.Success)
// throw some exception, etc.
LocalTime t = result.Value;
Related
I have a string that contains day of the week, time and duration. E.g. Monday,10:00 AM,45m
The duration could be in either of the following formats:
45m
1h45m
1h
Now I need to convert this into a date with time for both the start of the event and end of the event based on the duration.
I managed to convert this piece "Monday,10:00 AM" into the upcoming date and time for whatever the day of the week is so now I have a datetime as let's say "05/30/2022 10:00:00 AM".
Now I need to create a datetime object for the end time of the event by adding e.g. "45m" to the previous datetime. I don't know the format of the duration piece but it will be one of three from the list above.
How do I convert this into a standard timespan to add to the previous time? Is the above format a standard format that perhaps has a built in way to parse? It's coming from an API.
I have tried this and it works but I'm not sure how to detect and handle the formats.
\\split the original string so now I have duration
\\when I have just the hour duraton e.g. 1h
t = TimeSpan.ParseExact(durationString, "h\\h", CultureInfo.InvariantCulture);
var finalDate = dt.Add(t);
\\when I have just the minute format e.g. 45m
t = TimeSpan.ParseExact(durationString, "m\\m", CultureInfo.InvariantCulture);
Use a ParseExact method overload that accepts an array of formats.
var values = new string[] { "45m", "1h45m", "1h" };
var formats = new string[] { #"m\m", #"h\hm\m", #"h\h" };
foreach (var value in values)
{
var ts = TimeSpan.ParseExact(value, formats, CultureInfo.InvariantCulture);
Console.WriteLine(ts);
}
You can use REGEX to check for matching patterns:
using System.Text.RegularExpressions;
Regex HourOnly = new Regex("^[0-9]+h$");
Regex MinuteOnly = new Regex("^[[0-9]+m");
Regex HourAndMinute = new Regex("^[0-9]+h[0-9]+m$");
List<string> conditions = new List<string>();
string Condition1 = "Monday,10:00 AM,45m";
string Condition2 = "Monday,10:00 AM,1h45m";
string Condition3 = "Monday,10:00 AM,1h";
conditions.Add(Condition1);
conditions.Add(Condition2);
conditions.Add(Condition3);
foreach(string condition in conditions)
{
if (HourOnly.IsMatch(condition.Split(',').Last()))
{
Console.WriteLine($"Hour only: {condition}");
}
else if (HourAndMinute.IsMatch(condition.Split(',').Last()))
{
Console.WriteLine($"Hour and minute: {condition}");
}
else if (MinuteOnly.IsMatch(condition.Split(',').Last()))
{
Console.WriteLine($"Minute only: {condition}");
}
}
Granted users can enter 99999h99999m but if you are reasonably sure that won't happen the above regex should suit you just fine.
Here is some additional documentation to aid you on your quest: https://learn.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference
I have issue that I cant solve. I am trying to add minutes and subtract minutes. basically be in that 30 minutes block range. However, in my code below I am trying to get my key value and add 30 minutes but for some reason AddMinutes gives an error:
Cannot resolve symbol AddMinutes.
here is my code:
var results = JsonConvert.DeserializeObject<dynamic>(barCodeValue);
var gettingTheName = (string) results.Evaluation.Value;
TextHeader.text = gettingTheName;
var qrCodeString = $"https://**************.firebaseio.com/Evaluations/.json?orderBy=\"$key\"&startAt=\"{gettingTheName}\"&limitToFirst=1";
var matchingsLink = new WebClient().DownloadString(qrCodeString);
var objs = JObject.Parse(matchingsLink);
var someId = objs.First.First["Active"].ToString();
var data = objs[gettingTheName];
try
{
if (!((bool)data["Active"] == false && (bool)data["Completed"] &&
DateTime.Now < data["ScheduleStartTime"].AddMinutes(30) &&
DateTime.Now > data["ScheduleStartTime"].AddMinutes(-30)))
You are using AddMinutes() method to the JToken type object. AddMinutes only works with DateTime.
DateTime.Parse("dateInStringFormat") will parse the string to DateTime format.. to which you can then add Minutes to.
DateTime.Now < DateTime.Parse(data["ScheduleStartTime"].ToString()).AddMinutes(30) &&
DateTime.Now > DateTime.Parse(data["ScheduleStartTime"].StoString()).AddMinutes(-30)))
This is probably because data["ScheduleStartTime"] is not an instance of DateTime, since JSON has no built in representation of dates or times. What is most likely is data["ScheduleStartTime"] is an ISO 8601 date represented as a string, in which case you should parse it before comparing it to DateTime.Now:
// ...
var scheduleStartTime = DateTime.Parse(data["ScheduleStartTime"].ToString());
if (!((bool)data["Active"] == false && (bool)data["Completed"] &&
DateTime.Now < scheduleStartTime.AddMinutes(30) &&
DateTime.Now > scheduleStartTime.AddMinutes(-30)))
// ...
I have a date value that I want to strip the time from. I want the return type to be a date type since I want to order the list of date I have. having a list to string representing Date does not return a correct order.
I know that DateTime always returns the date with the time. What are my options here? How can I better my code and have a list of items of Date type without the time?
Edit: I would like to have the date only. nothing after that. Something like 8/7/2016 not 8/7/2016 00:00:00 or anything after date. and in a date object.
Here is my code:
using (var db = new MyModel.Context())
{
var cert = (
from tr in db.uspTranscripts(personId)
from a in db.PersonTranscripts.Where(x => x.UPID == personId)
from b in db.LU_CreditType.Where(x => x.ID == a.CreditTypeID)
select new CertViewModel
{
ActivityTitle = tr.ActivityTitle,
Score = tr.Score,
Status = tr.Status,
CompletionDate = tr.CompletionDate,
CretitTypeName = b.ShortName,
CompletedDateSorted = a.HK_CreatedOn
}).OrderByDescending(x => x.CompletedDateSorted).ToList();
List<CertViewModel> certlist = cert;
foreach (var item in certlist)
{
string itemWithoutHour = item.CompletionDate.Value.ToShortDateString();
var itemConverted = DateTime.ParseExact(itemWithoutHour, "M/d/yyyy", null);
item.CompletionDate = itemConverted;
}
return certificateslist.GroupBy(x => x.ActivityTitle).Select(e => e.First()).ToList();
}
For any given DateTime object, you can reference its Date property to strip out the time values:
var withTime = DateTime.Now; // 8/7/2016 22:11:43
var withoutTime = withTime.Date; // 8/7/2016 00:00:00
The .NET framework does not have a date-only object.
It may be worth understanding how the DateTime structure works. Internally, it stores an offset in ticks (1 tick = 100 nanoseconds) since 1/01/0001 12:00 am in a single 64-bit unsigned integer. (1 tick = 100 nanoseconds)
The DateTime structure then provides many useful methods and properties for dealing with dates and times, such as adding some days to an existing date, or calculating the difference of two times. One useful property is Date, which rounds a DateTime object down to the nearest day (12:00 am).
Dates, times and dates-with-times are all very similar, the main difference is how you format them, a date-with-time where you omit the time is just a date.
What David has suggested is that you work with the DateTime structure internally, strip any times using the Date property, sort on the DateTime, compare them and modify them as DateTime objects.
Only convert them to a string when they need to be displayed, at which point you can use methods such as ToShortDateString() or ToString(string format) to display only the date.
I am writing C# code for a business rule in a program called Prophet 21.
The code essentially is saying, if a product price has been updated in the last year, put that price into a field called PO Price.
Everything works solid except for one tiny problem, when making sure that the price was updated within the last year, it looks at the PO Price update date. At some point in the past, a lot of these were set to 00/00/00 and using an algorithm with date doesn't seem to understand it. My best guess is that I need to convert the date somehow, but I am quite a C# novice, any help would be appreciated. Here's the code I am working with.
public class SetPOCost : Rule //this rule sets the PO cost to the other cost when the item is stockable and the disposition is B
{
public override RuleResult Execute()
{
RuleResult result = new RuleResult();
result.Success = true;
string s = Data.Fields["other_cost"].FieldValue;
decimal ldcOtherCost = Convert.ToDecimal(s);
s = Data.Fields["po_cost"].FieldValue;
decimal ldcPOCost = Convert.ToDecimal(s);
string stockable = Data.Fields["stockable"].FieldValue;
string disposition = Data.Fields["disposition"].FieldValue;
s = Data.Fields["ufc_inventory_supplier_date_cost_last_modified"].FieldValue;
//this next line is added from the SuggestPromiseDate rule because it was running into errors doing both rules
Data.Fields.GetFieldByAlias("suggested_promise_date").FieldValue = Data.Fields.GetFieldByAlias("line_max_promise_date").FieldValue;
DateTime dtLastModified = Convert.ToDateTime(s);
DateTime thisDate = DateTime.Today;
DateTime thisDateLastYear = thisDate.AddYears(-1);
Boolean dateIsGood = dtLastModified.CompareTo(thisDateLastYear) >= 0;
if (stockable == "N" && disposition == "B" && ldcPOCost == 0 && ldcOtherCost > 0 && dateIsGood)
{
Data.Fields["po_cost"].FieldValue = ldcOtherCost.ToString();
}
return result;
}
You can use TryParse to check the date coming in.
s = Data.Fields["ufc_inventory_supplier_date_cost_last_modified"].FieldValue;
//this next line is added from the SuggestPromiseDate rule because it was running into errors doing both rules
Data.Fields.GetFieldByAlias("suggested_promise_date").FieldValue = Data.Fields.GetFieldByAlias("line_max_promise_date").FieldValue;
DateTime dtLastModified;
if (DateTime.TryParse(s, out dtLastModified) == false)
dtLastModified = DateTime.MinValue;
How about just:
s = Data.Fields["ufc_inventory_supplier_date_cost_last_modified"].FieldValue;
if (s == "00/00/00") {
s="01/01/0001";
}
00/00/00 is not a valid date in .NET; dates start counting from January 1st, 0001. Detect when this date occurs in your original data source, and then create a new date as new DateTime(0);.
I need to convert
20141013T155544.673-04/0
To a DateTime type.
Presently I am manually parsing the string out
//20130605T154727.683-04/0
//20130806T143808.018-04
//var a = new DateTime();
var year = segmentDate[0].ToString(CultureInfo.InvariantCulture) + segmentDate[1].ToString(CultureInfo.InvariantCulture) + segmentDate[2].ToString(CultureInfo.InvariantCulture) + segmentDate[3].ToString(CultureInfo.InvariantCulture);
var month = segmentDate[4].ToString(CultureInfo.InvariantCulture) + segmentDate[5].ToString(CultureInfo.InvariantCulture);
var day = segmentDate[6].ToString(CultureInfo.InvariantCulture) + segmentDate[7].ToString(CultureInfo.InvariantCulture);
//s[8] == "T";
var hours = segmentDate[9].ToString(CultureInfo.InvariantCulture) + segmentDate[10].ToString(CultureInfo.InvariantCulture);
var minutes = segmentDate[11].ToString(CultureInfo.InvariantCulture) + segmentDate[12].ToString(CultureInfo.InvariantCulture);
var seconds = segmentDate[13].ToString(CultureInfo.InvariantCulture) + segmentDate[14].ToString(CultureInfo.InvariantCulture);
string milliseconds = null;
if (segmentDate.Contains("."))
milliseconds = segmentDate.Split('.')[1].Split('-')[0];
if (milliseconds != null && milliseconds.Contains((" ")))
{
milliseconds = milliseconds.Split(' ')[0];
}
var offset = Convert.ToInt32(segmentDate.Split('-')[1].Split('/')[0]);
var a = new DateTime(Convert.ToInt32(year), Convert.ToInt32(month),
Convert.ToInt32(day), Convert.ToInt32(hours), Convert.ToInt32(minutes),
Convert.ToInt32(seconds), Convert.ToInt32((milliseconds ?? "0"))).AddHours(offset);
But that is a bad idea - and I cannot believe that this format isnt specified somewhere (that I have been able to find).
Any help is appreciated.
Thank you!
Update
4 digit year
2 digit month
2 digit day
T - denotes start of the time portion
2 digit hour
2 digit minute
2 digit second
. - denotes start of MS
3 digit ms
TZ offset (-04)
/0 I believe is offset minutes
Update2
So I have been playing with TryParseExact and ParseExact - and cannot come up with a format string that will pull this into a DateTime/DateTimeOffset type.
I also consulted with the supplier of this value and they also have a manual process to parse it out, like I posted already.
I cannot accept that this is the only way to achieve the desired result, and as such, will continue to play with it.
But if anyone else has suggestions, they are welcome here.
Here's the closest I've gotten:
string s = "20141013T155544.673-04/0";
var dt = DateTime.ParseExact(s,"yyyyMMddTHHmmss.fffzz/0",CultureInfo.InvariantCulture);
If the /0 represents minutes (and can be 0 or 30 then you could do a little manipulation to convert that to a "standard" time zone indicator:
string s = "20141013T155544.673-04/30";
string s2 = s.Replace("/0",":00").Replace("/30",":30");
var dt = DateTime.ParseExact(s2,"yyyyMMddTHHmmss.fffzzz",CultureInfo.InvariantCulture);