Sorting Date&Time string - c#

I have these below strings to sort (String Format: 121030-1833 --> YYDDMM-HHMM)
String Example:
121030-1833
120823-2034
120807-2014
120627-2316
120525-1136
111226-1844
I want to sort them from latest to old format.
Which sort method will be the best to use here?
Updated problem statement
dictionary[String,string] "dictLocation" values with me, Want to sort the Dictionary according to its key (Key-YYMMDD-HHMM, latest to oldest)
{[110901-1226, sandyslocation.110901-1226]}
{[120823-2034, andyslocatio.120823-2034]}
{[110915-1720, mylocation.110915-1720]}
{[121030-1833, mylocation.121030-1833]}
I am trying this var latestToOldest = dictLocation.OrderBy(key => key.Key)
It not giving me proper result, Is there anything i am missing ?

As it happens, just sorting them in a descending way will work:
var latestToOldest = original.OrderByDescending(x => x);
That's assuming you want to assume all values are within the same century. (If you can possibly change the format, I'd suggest using a 4-digit year for clarity.)
However, I would recommend parsing the values into DateTime values as early as possible. Whenever you can, keep your data in its most "natural" form. Fundamentally these are dates and times, not strings - so convert them into that format early, and keep them in that format as long as you possibly can.

var sortedDateStrings = dateStrings.Sort().Reverse();
// or
var sortedDateStrings = dateStrings.OrderByDescending(x => x);

Related

C# .Net Linq and converting datetime to formatted sting

I have the query
var result = (from myView in db.loginSessions
where myView.machine.ToUpper().Equals(machine.ToUpper())
&& myView.start >= myStart
&& myView.end <= myEnd
orderby myView.start
select new loginSessionList {
Id = myView.id,
Machine = myView.machine.ToUpper(),
Start = myView.start.ToString("u"),
End = myView.end.ToString("u"),
User = myView.username
}).ToList();
I get ArgumentOutOfRange exceptions on the datetime conversions. I have tried different ToString conversion strings. I have tried the other To... date->string conversions offered by Intellisence. I have tried Convert.string(myView.start). Nothing has worked. I have googled and have found advice using all the things I have tried. Do I have to post-process the generated list?
What have I missed?
I have 3 rules for dealing with DateTimes that served me well:
Always store, retreive and transmit the UTC value. Translating into the proper local Timezone is the job of ToString(), wich asks Windows for the users timezone. You do not want to add timezones to your troubles.
Avoid store, retreive or transmission of DateTimes as strings. Keep it in proper types whenever possible
If you can not follow rule 2 (like when you deal with Serialsiation), at least pick a fixed Culture Format and String encoding on all endpoints. You do not want to get different Cultures or faulty implied Encodings to your existing troubles
So, the answer to my issue had nothing to do with Linq or .Net. #JoeEnos put me on the right track as mentioned in my comment to his comment. I had created a class to receive each row of query result. The date fields were initially DateTime types but I started having issues. I changed those fields to string and then ended up asking my question above.
Back when the receiving class still had the DateTime fields, all of the fields had lowercase names. I must have applied some sort of code formatting that CamelCased the field names. This meant that after serializing the results the CamelCased names could not be found and the javascript didn't care.
I fixed the field names in my js code, changed the field data types back to DateTime and all is good.
Thanks

How can I query Neo4j for nodes between dates in the standard web based client?

I have some nodes in a collection with a Date Time:
I query this using the C# cypher client thus:
GraphClient _client = new GraphClient(new Uri(url));
DateTime dt;
_client.Cypher
.Match("(e:LineStop)")
.Where((LineStop e) => e.AddedToDataWarehouse == false && e.TimeCreated >= dt)
.AndWhere((LineStop e) => e.Duration >0)
.Return<LineStop>("e")
.Results;
This works.
How can I do the same thing using the standard web based Neo4j Graph client? I can't seem to get the syntax to work at all.
All of the existing Q&As seem to talk about structuring the data differently to be more graph like and use shortestPath. I can't do this. The data already exists. I did not design this system nor do I have the abillity to change it.
I was told by the person that did design this system (a contractor who is no longer with the company) that Neo4j now supports dates(as opposed to stringfying them). On some level it must, or else how does the C# code work, right?
The C# code is treating the Dates as strings, if you look at what is being sent over the wire (using Fiddler), you'll see the JSON has it all escaped. Importantly, it's treating it as ISO format, which is sortable. If you chose to store them using US/UK format, you'd quickly find the comparison doesn't work.
The comparison is being done by Neo4j - example cypher showing the situation you have is here:
MATCH (n) RETURN
CASE WHEN ("2017-03-19T08:12:17.9680461+00:00" > "2017-03-20T08:12:17.9680461+00:00") = true
THEN "TRUE"
ELSE "FALSE"
END
If you put that into your browser, you'll get FALSE.
Your query isn't working as you are doing a string comparison and '2017-03-17' is not the same as the actual string you have created. Of the two options below - the adding of a property is the best route - and doesn't involve changing the structure of the graph, but obviously depends on the ability to set a property on the node.
If you can't add extra properties to the objects
To do a 'Between' style approach -
You have to pass in a fully defined DateTimeOffset string example: 2017-03-19T08:12:17.9680461+00:00
To do an equality comparison (with just the date specificied)
Use STARTS WITH as #InverseFalcon said
This is awkward as a full DTOffset string is long and unwieldy :/
If you can add a property
I would add a Ticks version of the DateTime properties, i.e. have a property called TimeCreatedTicks which is simply the .Ticks property of the TimeCreated property. This is my general route for storing Dates.
For manual querying this is still a pain as you need to know the Ticks for a given date - and that (for me at least) normally involves going to LinqPad and running new DateTime(2017,3,17).Ticks.Dump()
It looks like you need a string split function:
match (l.LineStop)-[:Creates]->(ls.LineStop)
where l.Name = 'M-E' AND
split(ls.TimeCreated, 'T')[0] = '2017-03-17'
return l, ls limit 100
In this case a STARTS WITH predicate should do the trick:
match (l.LineStop)-[:Creates]->(ls.LineStop)
where l.Name = 'M-E' AND ls.TimeCreated starts with '2017-03-17'
return l, ls limit 100

How to work with numeric/integer values in Redis with Booksleeve

I'm currently using an in-memory cache and looking to switch to a distributed caching mechanism with Redis. I had a look at ServiceStack's client, but the rate-limiting licensing doesn't work for me, so Booksleeve seems to be recommended.
I've set up a test program to just set and that get that same value back from Booksleeve, but it seems like the result I'm getting back isn't in my expected format, and I'm not sure what the right way to handle this is (there isn't much in the way of documentation that I can see). Here is my simple test program:
RedisConnection conn = new RedisConnection("localhost", 6379);
conn.Open();
conn.Strings.Set(1, "test", 100);
var task = conn.Strings.Get(1, "test");
task.Wait();
var x = task.Result; // byte[] result = {49, 48, 48};
var str = BitConverter.ToString(x); // string result = "31-30-30";
var l = BitConverter.ToInt64(x, 0); // Destination array is not long enough to copy all the items in the collection. Check array index and length.
As you can see, at no point do I get back the same value of "100" that I cached with my original "Set" statement. It's also interesting that I don't seem to be able to cache by numeric values (since Get and Set are members of conn.Strings). Is the recommended approach to just .ToString() all numeric key values?
Any explanation as to why I'm unable to get back the original cached value (and best practices for doing this) would be greatly appreciated.
My answer has two parts:
Redis always saves strings, even if you set a number. BUT it internally knows to do some specific actions on strings that represent numbers.
For example, if right after your first .Set() assignment you'll add:
conn.Strings.Increment(1, "test", 1);
the test key will have the value "101", which is a string, but one that is made out of an arithmetic calculation by Redis.
You need to fix your conversion function. Instead of using BitConverter, that's the right way to convert:
var str = System.Text.Encoding.UTF8.GetString(x);
var value = int.Parse(str);
Of course, this snippet doesn't include any kind of error checking, which is fairly easy to apply (e.g. what if the value is empty or contains something that is not a number).
As for your last question, is using .ToString() the recommended approach - yes. That's the way to work with Redis. But of course, you can make your own utility wrappers that take care of converting values that suppose to contian numbers, to numbers. Something like GetIntValue(string key) or so.

Automatic incrementation with format string

As everyone would be aware the typical way to use the Format method is
string res = string.format("<Field Name='Title'>{0}</Field>", "SomeValue");
Where {0} will become SomeValue
And for each subsequent parameter you simply increment the number so the next one might look like this
string res = string.format("<Field Name='Title'>{0}</Field><Field Name='FirstName'>{1}</Field>", "SomeValue", "SomeValue1");
The problem I am having is that any time I make a change to the parameters in the xml I am trying to build I need to go through and reorder the format string values ie: {1} becomes {2}, {2} becomes {3} and so forth.
What I am wondering is if there is some way where I can omit the integer value from the placeholder and the format function might be able to work out the order based on the index of the parameters passed in?
I am not sure about a method, but you could use some sort of regex and have it replace based off of named strings instead of indexes, however that is not as pretty.
Another solution would be to just fill your parameters differently. For example, if the 0 spot is changing, then instead of renumbering, just add your new parameter at the beginning of the parameters list instead of just adding it to the end. This will still require a re-write of the code, though.
Lastly, why are you manually formatting the XML, when you can use XMLDocuments and XMLWriters? That will allow you to enter these values in a more controlled way, possibly (I cannot say for sure without seeing the code around it)
var fields = new Dictionary<string, string>
{
{"Title", "someTitle"},
{"FirstName", "Johnny"},
{"LastName", "Depp"}
};
var res = string.Join("", fields.Select(kvp => string.Format("<Field Name='{0}'>{1}</Field>",
kvp.Key,
kvp.Value)));
Use a refactoring tool such as Resharper. It will do the reformatting in String.Format for you. There is no inherent way to do what you ask.
Resharper will allow you to insert elements in arbitrary places and reorder the indices. It will allow you remove as well.
Alternatively, you could use StringTemplate

Sorting a file by the content of it's filename

I have a list of files I would like to sort based on the date. The caveat is that each file in the list contains the date as part of it's file name, I want to sort the files based on this date. The reason for this is because the date in the file name string correlates with the content that is in the file, i.e. The actual date property of each file, date created, modified, accessed, etc, is subject to change whenever the file is moved or modified and so I cannot rely on it for my purposes. I'm creating a custom comparer to use in the list sorting method, but I wanted to see if anyone out there has any other better or unique approaches to this. Thanks in advance.
UPDATE:
In answer to Saeed's comment, the file name follows this format:
{Test Name}_{YYYYMMDD}_{HHmmSS}.txt
Where the YYYYMMDD and HHmmSS are the date and time, respectively.
UPDATE 2:
Ok, I got a comparer written up that seems to do the job just fine, here it is if it helps anyone else; it wouldn't take much effort to alter it to search for any other elements in the file name, just need to change the regular expression. Thanks everyone for the suggestions, constructive criticism is always welcome.
public class TDRDateTimeComparer : IComparer<FileInfo>
{
public int Compare(FileInfo x, FileInfo y)
{
//Return if both strings are null or empty
if (string.IsNullOrEmpty(x.Name) && string.IsNullOrEmpty(y.Name))
{
return 0;
}
Regex rDateTime = new Regex(#"(19|20|21)\d\d(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])_([0-1]\d|2[0-3])([0-5]\d)([0-5]\d)");
/* NOTE: File names will already have been validated at this point, so no need to validate the date/time again.*/
string date1 = rDateTime.Match(x.Name).Value, date2 = rDateTime.Match(y.Name).Value;
DateTime d1 = DateTime.ParseExact(date1, "yyyyMMdd_HHmmss", null), d2 = DateTime.ParseExact(date2, "yyyyMMdd_HHmmss", null);
return d1.CompareTo(d2);
}
}
I'd say a custom comparer is the way to go. Just use regex to extract the date parts, then create a DateTime out of it and compare those.
You can use the OrderBy extension method and specify the value to sort on. Example:
list = list.OrderBy(f => DateTime.Parse(Path.GetFileName(f.Name))).ToList();
I used to work on a system that generated files with a date & time encoded into the filename. The date format fitted into the old DOS 8.2 filename format.
Because of the naming convention when you list the files in the file system they were naturally sorted correctly. If you have that amount of control you could consider doing it that way. i.e. naming it YYYYMMDD... will sort them by date by default.
No extra code required.

Categories

Resources