using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Vrh.OneReport.Lib.Areas.OneReport.Helpers { /// /// Parse a date/time string. /// /// Can handle time expressions like: /// - "yesterday": Yesterday /// - "today+12D": Today twelve days later /// - "today": This day at midnight. /// - "now": Right now (date and time). /// - "2010-12-31" /// /// In the expression the folowing words can be used: now, today, tommorrow, yesterday. /// The units are: /// - Y = year /// - M = month /// - D = day /// - h = hour /// - m = minute /// - s = second /// public class RelativeDateParser { private const string ValidUnits = "Y|M|D|h|m|s"; private const string ValidItems = "now|today|tomorrow|yesterday|DAYSBACK"; private static readonly Regex _completeRelativeRegex = new Regex(@"^(" + ValidItems + @")?(\s*(\+|-)?(\d)*(" + ValidUnits + "))*$"); /// /// Parses the given text and converts it to a DateTime if possible. /// /// The text to be parsed. /// Datetime public static DateTime Parse(string input, string dateFormat, string timeFormat) { DateTime? result = TryParseCompleteRelativeDateTime(input.Trim()); if (result.HasValue) { return result.Value; } // Try parse fixed dates like "01/01/2000". DateTime dt; List formats = new List(); if (dateFormat != null) { dateFormat = dateFormat.Replace("\\/", "/").Replace("/", "\\/"); formats.Add(dateFormat); if (timeFormat != null) { formats.Add(dateFormat + " " + timeFormat); formats.Add(dateFormat + "T" + timeFormat); } } if (timeFormat != null) { formats.Add("T" + timeFormat); formats.Add(timeFormat); } if (formats.Count == 0 || !DateTime.TryParseExact(input, formats.ToArray(), null, System.Globalization.DateTimeStyles.NoCurrentDateDefault | System.Globalization.DateTimeStyles.AssumeLocal, out dt)) { dt = DateTime.Parse(input); } return dt; } /// /// Parses the given text, chacks if it is an expression and converts it to a DateTime if possible. /// /// The text to be parsed. /// Datetime or null if the text is not an expression. private static DateTime? TryParseCompleteRelativeDateTime(string input) { var match = _completeRelativeRegex.Match(input); if (!match.Success) return null; DateTime dt = DateTime.Now; if (match.Groups[1].Success) { switch (match.Groups[1].Captures[0].Value) { case "now": dt = DateTime.Now; break; case "today": dt = DateTime.Today; break; case "yesterday": dt = DateTime.Today.AddDays(-1); break; case "tomorrow": dt = DateTime.Today.AddDays(1); break; } } var items = match.Groups[2].Captures; var signs = match.Groups[3].Captures; var values = match.Groups[4].Captures; var units = match.Groups[5].Captures; //Assumes that each item has a unit member. Debug.Assert(items.Count == units.Count); int unitidx, signidx, valueidx; int value; for (unitidx = signidx = valueidx = 0; unitidx < units.Count; ++unitidx) { var unit = units[unitidx].Value; var ui = units[unitidx].Index; if (values[valueidx].Index < ui) //OK, we have a value; { value = Convert.ToInt32(values[valueidx++].Value); } else { value = 1; } if (signs[signidx].Index < ui) //OK, we have a sign; { value *= (signs[signidx++].Value == "-" ? -1 : 1); } dt = AddOffset(unit, value, dt); } return dt; } /// /// Add/Remove years/days/hours... to a datetime. /// /// Must be one of ValidUnits /// Value in given unit to add to the datetime /// Relative datetime /// Relative datetime private static DateTime AddOffset(string unit, int value, DateTime dateTime) { switch (unit) { case "Y": return dateTime.AddYears(value); case "M": return dateTime.AddMonths(value); case "D": return dateTime.AddDays(value); case "h": return dateTime.AddHours(value); case "m": return dateTime.AddMinutes(value); case "s": return dateTime.AddSeconds(value); default: throw new Exception("Internal error: Unhandled relative date/time case."); } } } }