-3

I want to get files from a list for all the files whose filedate > today's cutOff - so, I have the following codelet

string[] MyFiles = Directory.GetFiles(MyConfig.pathTransmittedFiles, "*.adf")
        .Where(file => new FileInfo(file).LastWriteTime > dtCutOff).ToArray();

I have a file whose LastWriteTime is "{11/3/2015 1:33:26 PM}" being picked up by my collection with dtCutOff == "{11/3/2015 1:33:26 PM}"! So '>' didn't seem to work.

Jenna Leaf
  • 2,255
  • 21
  • 29
  • 1
    Please show the complete code and examples. We don't know how you've defined `fn_fileInfo` or `dtCutOff`, and we don't know what value is in `dtCutOff` or the file dates you're comparing to. – Matt Johnson-Pint Nov 05 '15 at 17:50
  • fn_fileInfo(file).modified is System.DateTime and also dtCutOff is System.DateTime – Jenna Leaf Nov 05 '15 at 17:56
  • 2
    That doesn't help. `fn_fileInfo` is not a standard function of .NET, and as far as I know, it's not provided by LinqPad either. You need to edit your question to be a [MVCE](http://stackoverflow.com/help/mcve). Otherwise it's going to be voted as closed. I have some suspicions of what may be at work here, but I cannot answer in the question's current state because such an answer would be highly speculative. – Matt Johnson-Pint Nov 05 '15 at 17:59
  • Thanks for updating your question! I'll answer shortly. – Matt Johnson-Pint Nov 05 '15 at 18:39
  • Question: is `dtCutOff` always a time calculated from `DateTime.Now` ("three hours ago" in your sample) Or does it sometimes come from somwhere else, such as user input or database? – Matt Johnson-Pint Nov 05 '15 at 18:43
  • 1
    Some of the worst C# code I've seen in a while... casing, unnecessary structures ... ugh... – Sten Petrov Nov 05 '15 at 18:45
  • 1
    Dear @JennaLeaf, You totally missed the point. What I was telling you is that the problem you have has nothing to do with LINQ or lambdas – Ivan Stoev Nov 05 '15 at 18:47
  • So you are effectively cutting the time portion? If you want to compare just dates, why not use just `.Where(file => fn_fileInfo(file).modified.Date > dtCutOff.Date)` then? – Ivan Stoev Nov 05 '15 at 18:48
  • 1
    Take a look at this SO thread http://stackoverflow.com/questions/9992223/file-getlastwritetime-seems-to-be-returning-out-of-date-value. Good luck. – Ivan Stoev Nov 05 '15 at 19:03
  • Regarding the workaround, I don't think you meant to compare the time of day without regard to date. Even then, date and time comparisons should never be done through strings. Unless you are explicitly using a format that is lexicographically comparable (such as ISO 8601's `YYYY-MM-DDTHH:MM:SS`) then many values will be out of sequence chronologically, such as `11/1/2015` coming before `2/1/2015` in `M/D/YYYY` format. – Matt Johnson-Pint Nov 05 '15 at 19:23
  • 2
    Actually, we could be doing something else with our time, but most of us are trying to help you. – Matt Johnson-Pint Nov 05 '15 at 21:03
  • Ivan Stoev, Sten Petrov and Matt Johnson,thankyou for all good intentions to help. As you have seen, I have made correction to my question so it is accurate. As for your part, please update your comments and evaluation. And I'll appreciate! Thankyou guys! – Jenna Leaf Nov 11 '15 at 18:52

4 Answers4

1

First, I would try running it without the Where clause, just to make sure that all files you expect are indeed part of the initial array returned from Directory.GetFiles. It's entirely possible that date/time comparison is not the source of the discrepancy. It may be more related to the issue Ivan linked to in the question comments, or it may be permission related, or some other thing.

Next, be aware that DateTime violates SRP in that it has a Kind property, which is one of the three DateTimeKind enumeration values. It's either Local, Utc, or Unspecified.

In the case of DateTime.Now, the Kind will be DateTimeKind.Local. File.GetLastWriteTime also returns its value with local kind. Therfore, if you always derive your dtCutOff from DateTime.Now in the manner you showed in the question, then it will almost always be the correct comparison function.

The "almost" stems from the fact that DateTimeKind.Local can actually represent two different kinds under the covers. In other words, there are actually four kinds, but two of them are exposed by one. This is described as "DateTime's Deep Dark Secret" in Jon Skeet's blog post More Fun with DateTime, and is also mentioned in the comments in the .NET Framework Reference Source. In practice, you should only encounter this in the ambiguous hour during a fall-back daylight saving time transition (such as just occurred last Sunday 2015-11-01 in the US).

Now, to the more likely case that your dtCutOff is actually derived not from DateTime.Now, but rather from user input or database lookup or some other mechanism, then its possible that it actually represents the local time in some other time zone than the one on your local computer. In other words, if the dtCutOff has a Kind of DateTimeKind.Utc, then the value is in terms of UTC. If it has a Kind of DateTimeKind.Unspecified, then the value might be in terms of UTC, or the local time zone, or some other time zone entirely.

Here's the kicker: Comparison of two DateTime values only evaluates the value underlying the Ticks property. It does not consider Kind.

Since file times are absolute points in universal time (on NTFS anyway), then you really should use the File.GetLastWriteTimeUtc method, rather than the methods that work in local time.

There are two approaches you could use:

  • Load the modified property as UTC, using:

    myResult.modified = File.GetLastWriteTimeUtc(myFile);
    
  • Populate dtOffset appropriately.

    • If you're loading from the current time, then use DateTime.UtcNow.
    • If you're loading from other input, ensure the value is converted to UTC to match the input scenario. For example, use .ToUniversalTime() if the value is in terms of the local time zone, or use the conversion functions in the TimeZoneInfo class if the value is in another time zone.

OR

  • Change your modified property to be a DateTimeOffset instead of a DateTime.
  • Load that using:

    myResult.modified = new DateTimeOffset(File.GetLastWriteTimeUtc(myFile));
    
  • Define dtCutOff as a DateTimeOffset, and populate appropriately.

    • If you're loading from the current time, then use DateTimeOffset.UtcNow.
    • If you're loading from other input, ensure the offset is set to match the input scenario. Use TimeZoneInfo functions if you need to convert from another time zone.

DateTimeOffset has many advantages over DateTime, such as not violating SRP. It's always representing an absolute moment in time. In this scenario, it helps to know that comparison operators on DateTimeOffset always reflect that absolute moment. (In other words, it internally adjusts to UTC before doing the comparison.)

Community
  • 1
  • 1
Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
0

Since the files were fed into the queue once a day so the scale of precision is not required down to a millisecond or something. So that one-second TimeSpan difference is acceptable to do the trick and make my case work.

string[] MyFiles = Directory.GetFiles(MyConfig.pathTransmittedFiles, "*.adf")
  .Where(file => new FileInfo(file).LastWriteTime - TimeSpan.FromSeconds(1) > dtCutOff)
  .ToArray();

Now my file with modified date "{11/3/2015 1:33:26 PM}" didn't go into my collection when my cutOffDate is "{11/3/2015 1:33:26 PM}" while my other file with modified date "{11/3/2015 1:33:27 PM}" have successfully passed into my collection as expected!! So, it works and that's how it should work after all these advises! Thanks ye-all.

Jenna Leaf
  • 2,255
  • 21
  • 29
  • Matt: I have corrected my answer already, please edit or adjust your evaluation, thank you. – Jenna Leaf Nov 05 '15 at 20:44
  • 1
    I think it's likely the file time you're looking at is actually greater than you think it is, by some fraction of a second. So by subtracting a second you get that response you were expecting, but it's fibbing a bit since the real time is actually greater. It's just a question of how much precision do you care about in your scenario. (Some might go even further, such as a whole minute) – Matt Johnson-Pint Nov 05 '15 at 21:02
0

This code works:

var cutffDate = new DateTime(2015,1,1); // or whatever
var allFiles = Directory.GetFiles(MyConfig.pathTransmittedFiles, "*.adf");
var datedFiles = allFiles.Where(f => (new FileInfo(f)).LastWriteTime > cutffDate);

Update: Since your issue seems to be a precision-related one you could change the comparison to:

const long precision = 10; // vary this as needed
allFiles.Where(f =>
  (new FileInfo(f)).LastWriteTime.ToFileTime()/precision > cutffDate.ToFileTime()/precision);

Alternatively you could use ...LastAccessTime.Ticks/TimeSpan.TicksPerMillisecond In addition to that you may need to convert all DateTime values to UTC (LastAccessTimeUtc and DateTime.UtcNow) to make sure it's not some weird timezone issue

Sten Petrov
  • 10,943
  • 1
  • 41
  • 61
  • Sorry Sten your version doesn't work for me! I have a file whose modified date is "{11/3/2015 1:33:26 PM}" from FileInfo and it went right through into my collection when my cutOffDate is "{11/3/2015 1:33:26 PM}" !!! I have to fall back to my own solution as I have given as comment above, sorry! – Jenna Leaf Nov 05 '15 at 19:28
  • @JennaLeaf - Sounds like a precision issue. Both `DateTime` and NTFS file times have 7 decimal places of fractions (100-ns precision). You are only seeing whole seconds, but that doesn't mean the underlying value isn't larger by a fraction of a second. – Matt Johnson-Pint Nov 05 '15 at 20:58
-1

Looks like your Where clause lambda might be incorrect. Try this.

string[] MyFiles = Directory.GetFiles(MyConfig.pathTransmittedFiles, "*.adf").Where(file => file.modified > dtCutOff).ToArray();