Sprache parser with custom fields - c#

I have a report server that needs to parse a string with some arguments controlling what is in the report.
I am using the parser library sprache to help with this. All is working fine except for one thing I'm stuck on.
I have a time filter that can be one of the following values: today, yesterday, last week, last month, none or custom.
It is custom that is giving me some grief. All of the others are just simple strings. Custom also has a from and to properties afterwards.
private static readonly Parser<DataFilterEntity> TimeFilter =
from filter in Parse.String("today").Return(DataFilterEntity.Today)
.Or(Parse.String("yesterday").Return(DataFilterEntity.Yesterday)
.Or(Parse.String("last week").Return(DataFilterEntity.LastWeek)
.Or(Parse.String("last month").Return(DataFilterEntity.LastMonth)
.Or(Parse.String("none").Return(DataFilterEntity.None))
.Or(Parse.String("custom").Return(DataFilterEntity.Custom())))))
select filter;
The custom line is the problem. I need to parse the "custom" string but then parse the from and to DateTime fields as well and pass them through to DataFilterEntity.Custom(from, to)
Any ideas much appreciated.

You need to create a parser for DateTime first and then a parser for your custom type. Here's a 'simplest thing that could possibly work' example. You'd probably want to make the DateTimeParser a bit more specific in the values it accepts. I don't know what the constructor for your DataFilterEntity looks like, so I guessed :)
public static readonly Parser<DateTime> DateTimeParser =
from day in Parse.Number
from s1 in Parse.Char('/')
from month in Parse.Number
from s2 in Parse.Char('/')
from year in Parse.Number
select new DateTime(int.Parse(year), int.Parse(month), int.Parse(day));
public static readonly Parser<DataFilterEntity> CustomParser =
from a1 in Parse.String("custom").Token()
from fromDateTime in DateTimeParser.Token()
from toDateTime in DateTimeParser.Token()
select new DataFilterEntity(fromDateTime.ToShortDateString() + " -> " + toDateTime.ToShortDateString());
public static readonly Parser<DataFilterEntity> TimeFilter =
Parse.String("today").Return(DataFilterEntity.Today)
.Or(Parse.String("yesterday").Return(DataFilterEntity.Yesterday)
.Or(Parse.String("last week").Return(DataFilterEntity.LastWeek)
.Or(Parse.String("last month").Return(DataFilterEntity.LastMonth)
.Or(Parse.String("none").Return(DataFilterEntity.None))
.Or(CustomParser))));
public void TestIt()
{
var result = TimeFilter.Parse("custom 21/3/2013 10/4/2013");
Console.Out.WriteLine("result.Value = {0}", result.Value);
}

Related

How can I prevent Excel from converting values to date format?

Recently (nobody noticed it happening before, if it did) some "ID"-type values are being converted by Excel to dates. There is some logic to which values are being converted, as you can see here, where the user added a column to show what the underlying values really are, and how they should be represented (raw, no conversion):
So because there's a "-" after "01" Excel is thinking that "01-" should be January, an assuming the final two characters represent the year.
In Item codes that contain no dash, they are left alone. How can I prevent Excel from being "helpful" in this way and converting these values to dates?
UPDATE
In response to Scott Craner's comment, this is the code I have to write out that value:
using (var memberItemCodeCell = priceComplianceWorksheet.Cells[rowToPopulate, DETAIL_MEMBERITEMCODE_COL])
{
memberItemCodeCell.Style.Font.Size = DATA_FONT_SIZE;
memberItemCodeCell.Value = _memberItemCode;
memberItemCodeCell.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
}
...and when I try to figure out how to format the value as Text or General by typing "for" after the cells name, I see these options:
So what do I need to use - FormatedText (sic) or ConditionalFormatting, and how specifically do I set those up to format the column as Text or General, and which if any of these two are preferred?
I generally when preparing the table to put data, I define data type so Excel don't try to find the kind of data.
In this case I would use Text data type. Example:
I think you want to change the number format to text.
The text format is specified with the NumberFormat property using the "at" character.
memberItemCodeCell.Style.Font.Size = DATA_FONT_SIZE;
memberItemCodeCell.NumberFormat = "#";
memberItemCodeCell.Value = _memberItemCode;
memberItemCodeCell.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
Also, for what it's worth, take a look at articles / questions regarding the difference between Value and Value2. It's good to understand the difference and use the proper one. In your case, I doubt it matters.
What ended up working for me was using this technique to assign the proper data type to the value in the cell (with the call to ConvertValueToAppropriateTypeAndAssign()) and then formatting as necessary after the fact:
public static readonly string NUMBER_FORMAT_CURRENCY = "$#,##0.00;($#,##0.00)";
public static readonly string NUMBER_FORMAT_THOUSANDS = "#,##0";
public static readonly string PERCENTAGE_FORMAT = "0.00%;[Red]-0.00%";
public static readonly string NUMBER_FORMAT_TEXT = "#";
public static readonly string NUMBER_FORMAT_DOUBLE = "0.00";
. . .
using (var percentageCell = priceComplianceWorksheet.Cells[rowToPopulate, SUMMARY_PERCENTAGE_COL])
{
ConvertValueToAppropriateTypeAndAssign(percentageCell, totalPercentage);
percentageCell.Style.Numberformat.Format = PERCENTAGE_FORMAT;
}
. . .
// Adapted from https://stackoverflow.com/questions/26483496/is-it-possible-to-ignore-excel-warnings-when-generating-spreadsheets-using-epplu
public static void ConvertValueToAppropriateTypeAndAssign(this ExcelRangeBase range, object value)
{
string strVal = value.ToString();
if (!String.IsNullOrEmpty(strVal))
{
decimal decVal;
double dVal;
int iVal;
if (decimal.TryParse(strVal, out decVal))
range.Value = decVal;
if (double.TryParse(strVal, out dVal))
range.Value = dVal;
else if (Int32.TryParse(strVal, out iVal))
range.Value = iVal;
else
range.Value = strVal;
}
else
range.Value = null;
}

Matching a Pattern in C#

This may seem like an obvious thing, but I want to know know if there are wildcards in C# for matching strings.
I want to write a program so that users can search for something based on the date value in the database. What I have so far works as long as the whole date is entered, but entering half a date causes a parsing error
I'm using this code now
" ... where Date like " DateTime.Parse(textbox.text.trim()) " + %;"
I want to see if there is an way to see what the user input (only the year, or month and year without day, or for it not to crash if only half the year is entered)
A solution that involves doing this in SQL itself doesn't matter
Solution 1:
public static class MyStringExtensions
{
public static bool Like(this string toSearch, string toFind)
{
return new Regex(#"\A" + new Regex(#"\.|\$|\^|\{|\[|\(|\||\)|\*|\+|\?|\\").Replace(toFind, ch => #"\" + ch).Replace('_', '.').Replace("%", ".*") + #"\z", RegexOptions.Singleline).IsMatch(toSearch);
}
}
examples:
bool willBeTrue = "abcdefg".Like("abcd_fg");
bool willAlsoBeTrue = "abcdefg".Like("ab%f%");
bool willBeFalse = "abcdefghi".Like("abcd_fg");
Solution2:
madate.ToString().Contain("myvalue")
madate.ToString().StartWith("myvalue")
madate.ToString().EndWith("myvalue")
or use sql :
where CONVERT(VARCHAR(24),yourdate, 103) like '%yourvalue%'
where CONVERT(VARCHAR(24),yourdate, 103) like '%yourvalue%'

String format proper using

Do you know another more proper way to do the same things ?
string initialTemplate = "{0}-{1}";
string template = string.Format(initialTemplate, "first", "{0}");
string answer = string.Format(template, "second");
Also the following way has actually known, but in my current case unfortunatelyI can't use that method(i think that that way more proper and the logic more clear):
string initialTemplate = "{0}-{{0}}";
string template = string.Format(initialTemplate, "first");
string answer = string.Format(template, "second");
Maybe is there another hint how to do that?
UPDATE
I'm so sorry but from yours answers I've learnt that my question wasn't enough clear. So I've added a little bit more description.
My situation:
//that template is actually placed in *.resx file
//I want storing only one template and use that in different situations
public const string InitialTemplate = "{0}-{1}";
public static string GetMessage(string one, string two)
{
return string.Format(InitialTemplate, one, two);
}
public static string GetTemplate(string one)
{
return string.Format(InitialTemplate, one, "{0}");
}
//or morew universal way
public static string GetTemplate(params object[] args)
{
return string.Format(InitialTemplate, args, "{0}");
}
static void Main(string[] args)
{
//in almost all cases in my project i need to use string.format like this
string message = GetMessage("one", "two");
//but there is another way where i have to use
//the template have already been assigned first argument
//the result must be "one-{0}"
string getTemplateWithAssignedFirstArg = GetTemplate("one");
}
Do you know more proper way for that kind of situation ?
If you are using C# 6 you can also use string interpolation.
https://msdn.microsoft.com/en-us/library/dn961160.aspx
var answer = $"{firstVar}-{secondVar}";
string initialTemplate = "{0}-{1}";
string answer = string.Format(initialTemplate, "first", "second");
Should do the trick. Or cut out the middle man with:
string answer = string.Format("{0}-{1}", "first", "second");
String.Format is a very useful convenience, but I'd be wary of using it to build format strings that you're going to use to create other format strings. Someone trying to maintain that code, figure out what's going on, and perhaps modify it will be baffled. Using String.Format that way is technically possible, and there could even be scenarios where it's useful, but it's probably just going to result in something that works but is very difficult to understand and debug.
My first suggestion would be to use a StringBuilder. Even when you're appending to the StringBuilder you can use String.Format if needed to create the individual strings.
I wonder if perhaps what you describe in the question is taking place across multiple methods (which is why you might be building your format string in steps.) If that's the case, I recommend not building the string in steps like that. Don't actually start building the string until you have all of the data that you need together, and then build the string at once.

replace a JSON date in a string to a more readable date

We want to show some JSON to a user who is testing our application. So we call our REST service in the ASP.NET code behind file and return a string, which holds a lot of JSON.
We then put it in a PRE element in the page, call beautify to create nice readable JSON and all is good: sort of human readable content is shown.
Good but for one thing: all the dates are shown in the normal JSON format like this "/Date(1319266795390+0800)/"
What I want to do is replace those JSON dates with 'normal' dates, in the JSON (C#) string, so in the code behind that is, before I add the string to the PRE element.
I was thinking about some regex, but i couldn't figure out how...
I'v been dealing with dates in JSON string for some time now, there's no standard way for that and which is why there are so many different ways to do it! Maybe it was better if JSON specification could specify an standard format for dates in the first place!
Microsoft is doing it in its own way, counting the msecs since 1970 in UTC format this is something like "/Date(1319266795390+0800)/"
We've been changing the above string to ISO-8601 format ever since using Regular Expressions on top of ASP.Net JavaScriptSerializer output. It is a W3C standard, human readable and the way most browsers serialize Date to string, here's how:
static readonly long DATE1970_TICKS = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).Ticks;
static readonly Regex DATE_SERIALIZATION_REGEX = new Regex(#"\\/Date\((?<ticks>-?\d+)\)\\/", RegexOptions.Compiled);
static string ISO8601Serialization(string input)
{
return DATE_SERIALIZATION_REGEX.Replace(input, match =>
{
var ticks = long.Parse(match.Groups["ticks"].Value) * 10000;
return new DateTime(ticks + DATE1970_TICKS).ToLocalTime().ToString("yyyy-MM-ddTHH:mm:ss.fff");
});
}
You can easily change the format to satisfy your needs, to see custom Date and Time formats check out MSDN article here
Here's how it's used:
JavaScriptSerializer ser = new JavaScriptSerializer();
var JsonSrt = ISO8601Serialization(ser.Serialize(DateTime.Now)); // "\"2012-05-09T14:51:38.333\""
Update:
There's an alternative to tweak the JSON string returned from the server in JavaScript to more readable form using Regex:
var str = "/Date(1319266795390+0800)/";
str.replace(/\/Date\((\d+)\+\d+\)\//, function (str, date) {
return new Date(Number(date)).toString();
});
The solution is within the string shown in the question. The JavaScript Date object will parse that format and produce a readable version so Date(1319266795390+0800) returns "Wed Apr 18 2012 08:13:22 GMT-0500 (Central Daylight Time)".
To remove the forward slash from the string you could use the replace function with a regular expression: "/Date(1319266795390+0800)/".replace(/\//g, '').
You can use this:
string date = "/Date(1319266795390+0800)/";
string regex = #"/Date\((.*?)\+(.*?)\)/";
Match match = Regex.Match(date, regex);
DateTime d = new DateTime(1970, 01, 01).AddMilliseconds(long.Parse(match.Result("$1")));
suppose the class you want to serialize looks like this:
public class Something
{
public int ID;
public string Name;
public DateTime Date;
}
change it to:
public class Something
{
public int ID;
public string Name;
public DateTime Date;
public string HumanReadableDate { get { return Date.ToLongDateString(); } }
}
or, if you want that extra property to display only in test enviroment:
public class Something
{
public int ID;
public string Name;
public DateTime Date;
#if DEBUG
public string HumanReadableDate { get { return Date.ToLongDateString(); } }
#endif
}
also, instead of .ToLongDateString() you can use .ToString("yyyy-MM-dd HH:mm") or any other format
Use as regex something like:
(?<= /Date\( )
(?<ticks>[0-9]+)
((?<zonesign>[+-])
(?<zonehour>[0-9]{2})
(?<zoneminutes>[0-9]{2})
)?
(?= \)/ )
This will match the part inside the parentheses of /Date(1319266795390+0800)/. You can then call Regex.Replace on the whole JSON string to replace the numbers with a nicely formatted DateTime:
Use the Match object you get in the match evaluator delegate and extract the ticks, zonesign, zonehour and zoneminutes part, convert it to integers.
Then convert the javascript ticks to .NET ticks (should be *10000), construct the .NET DateTime out of ticks and add/substract the hours and minutes for the time zone.
Convert the DateTime to a string and return it as the replacement.
If your JSON is a serialised representation of a .NET class, maybe you could use the DataContractJsonSerializer to deserialise it on the server, or perhaps you could just define a stub class for your JSON object if you don't need a generic solution to handle multiple datasets:
string json = "{\"Test\": \"This is the content\"}";
DataContractJsonSerializer ds = new DataContractJsonSerializer(typeof(TestJson));
var deserialisedContent = ds.ReadObject(new MemoryStream(Encoding.ASCII.GetBytes(json)));
foreach (var field in typeof (TestJson).GetFields())
{
Console.WriteLine("{0}:{1}", field.Name, field.GetValue(deserialisedContent));
}
...
[DataContract]
private class TestJson
{
[DataMember]
public string Test;
}
Use Newtonsoft.JSON. You can provide your own serializers per type, and serialize dates however you want.
http://james.newtonking.com/projects/json-net.aspx
Make a string property for example dateofbirth I am defining here, and return your datetime variable as:
public string DateOfBirthString
{
get { return DateOfBirth.ToUniversalTime().ToString("yyyy-MM-dd HH:mm:ss"); }
set { DateOfBirth = string.IsNullOrEmpty(value) ? new DateTime(1900, 1, 1) : Convert.ToDateTime(value); }
}
because this will return string so it will be same at client side so and aslo take string dateTime from user and convert it.
string input = [yourjsonstring];
MatchEvaluator me = new MatchEvaluator(MTListServicePage.MatchDate);
string json = Regex.Replace(input, "\\\\/\\Date[(](-?\\d+)[)]\\\\/", me, RegexOptions.None)

Convert DateTime with TypeDescriptor.GetConverter.ConvertFromString (using custom format)

Is there any way to make
TypeDescriptor.GetConverter.ConvertFromString convert to DateTime using a custom format e.g. "2011-04-21 13-03-56"?
If not, is there any DateTime-format out there that can be applied to a folder name (/,\,:,etc. are not allowed as you know)?
Help is very much appreciated. Thanks in advance
Random-I-Am
Edit:
Since my request still seems to not be understood correctly I am again trying to elaborate on my question. My users are creating folders with specific names. For example "1000_Link_Topic1_2011-01-25 14-12-10". They are free to combine their information as they like and omit information where needed. They don't even have to care about case sensitivity. So I could face another folder named "1000_link_Topic2".
What I have is a single class with a single property for each possible fragment of information. In this case I would have (Since I had to find a way of checking each properties default value regardless of the property type I am using nullable types):
Short? short_val;
EContentType? enum_val;
String string_val;
DateTime? datetime_val;
My code obviously splits the folder name at "_" and then tells for each fragment to which of the above property types it belongs. As soon as I know the corresponding type I am trying to convert to the Type, lets say t, using TypeDescriptor.GetConverter(t).ConvertFromString(info_frag[i]). I hope you now understand why I cannot use another conversion Method.
The code works for all the types mentioned above. My problem is to find a custom DateTime format that can be used on a folder name. All of the formats I know are using colons to separate hours from minutes and seconds.
What I want is a way to convert from a custom DateTime-format to a DateTime-object using TypeDescriptor.GetConverter.ConvertFromString. If that is not possible I either need to find a standard DateTime-format which can be assigned as a folder name without any further conversion or somehow extend the built-in DateTime-formats by my custom format. If it is going to be the latter I do not care about being able to use seconds or minutes. Hours will do the job.
Thanks again for being as patient as you are and helping me out with this one. Feel free to ask as many further questions as you like. I hope you can help me getting this one to work.
Random-I-Am
I have looked into your problem a little and found that the standard DateTimeTypeConverter uses DateTime.Parse internally which doesn't listen to CurrentCulture info at all. Instead you can trick the conversion process with your own TypeConverter!
I do not know how to attach the new typeconverter to the existing DateTime structure, if you even can, so I made a miniture AdvancedDateTime structure .. which is empty. Noone said your custom TypeConvert should even return such a thing! This one returns a regular DateTime. I provided the code below:
public class CustomDateTimeTypeConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return DateTime.ParseExact(value.ToString(), "yyyy-MM-dd HH-mm-ss", culture);
}
}
[TypeConverter(typeof(CustomDateTimeTypeConverter))]
struct AdvancedDateTime
{
}
[TestFixture]
public class DateTime
{
[Test]
public void TypeConvert_StrangeFormat_ConvertsWithoutProblem()
{
string datetime = "2011-04-21 23-12-13";
TypeConverter converter = TypeDescriptor.GetConverter( typeof (AdvancedDateTime) );
var convertedFromString = converter.ConvertFromString(datetime);
Assert.AreEqual(new DateTime(2011,4,21, 23,12,13), convertedFromString);
}
}
Try this
string m_strDate = DateTime.Now.ToString("MM/dd/yyyy");
m_strDate = m_strDate.Replace("/", "");
Append this m_strDate to your folder
A sample i used for text file is as follows
strPath += "/FileHeader_" + m_strDate + ".txt";
Check this sample
DateTime dt=new DateTime(1990,5,6);
Console.WriteLine(TypeDescriptor.GetConverter(dt).ConvertTo(dt, typeof(string)));
string myStr="1991-10-10";
Console.WriteLine(TypeDescriptor.GetConverter(dt).ConvertFrom(myStr));
The sample code i written just to display as per you need try as per your requirement
string s = "Folder";
DateTime dt = new DateTime(1990, 5, 6);
string str = TypeDescriptor.GetConverter(dt).ConvertTo(dt, typeof(string)).ToString();
string myStr = "1991-10-10";
string str1 = TypeDescriptor.GetConverter(dt).ConvertFrom(myStr).ToString();
s = s + str1.Replace("/", "").Replace(":", "");
textBox1.Text = s;

Categories

Resources