System.DateTime comparison ('>') or ('<') does not give what I expected - datetime

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.

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.)

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

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.

Looks like your Where clause lambda might be incorrect. Try this.
string[] MyFiles = Directory.GetFiles(MyConfig.pathTransmittedFiles, "*.adf").Where(file => file.modified > dtCutOff).ToArray();

Related

Moment return the same value passed to it instead of utc value

Im trying to trasform a date to utc but moment return the same value i use.
for example
moment(date, 'YYYY-MM-DD HH:mm:ss').utc().format('YYYY-MM-DD HH:mm:ss')
if i use date = '2022-01-07 11:30:00' moment return 2022-01-07 11:30:00
do i have to set the timezone of the value first? why moment return the wrong value? it should return +3 hours that date.
You'll need to define the timezone in which the date is, then the offset will be as expected:
Example, using Europe/Amsterdam as timezone
const date = '2022-01-07 11:30:00';
const utc = moment(date, 'YYYY-MM-DD HH:mm:ss')
.tz('Europe/Amsterdam')
.utc()
.format('YYYY-MM-DD HH:mm:ss');
console.log(utc);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.32/moment-timezone-with-data.min.js"></script>
This will output 2022-01-07 10:30:00 since Amsterdam time is -1 compared to UTC.
Small side node, quoting MomentJS Project Status page
We now generally consider Moment to be a legacy project in maintenance mode. It is not dead, but it is indeed done.
In practice, this means:
We will not be adding new features or capabilities.
We will not be changing Moment's API to be immutable.
We will not be addressing tree shaking or bundle size issues.
We will not be making any major changes (no version 3).
We may choose to not fix bugs or behavioral quirks, especially if they are long-standing known issues.
The data you pass in doesn't have any indication of the timezone it's in, so moment is (I believe) assuming it's in utc already.
In related news, look into using the date-fns library instead of moment. Moment is getting old...
Moment github
Moment.js is a legacy project, now in maintenance mode. In most cases,
you should choose a different library.
This returns the same date since you never indicated any timezone
var time = moment("2013-08-26 16:55:00") //this creates time in my tz
You can set a timezone like this:
var time = moment("2013-08-26 16:55:00").tz("America/Los_Angeles");

Moment misunderstanding utcOffset

I'm working with moment and moment-timezone and I don't get it. Can anyone explain me why this happens?
THIS (which is what I want):
moment('2018-11-28T00:00:00.000-02:00').toISOString()
Prints 2018-11-28T02:00:00.000Z
BUT:
moment('2018-11-28T00:00:00.000').zone('-02:00').toISOString()
moment('2018-11-28T00:00:00.000').utcOffset('-02:00').toISOString()
Both print 2018-11-27T23:00:00.000Z
PD: My zone is GMT+1.
Why are different? it is not supposed to be equals?
How do I set the offset (not in the constructor but with a method after I have de moment object)?
Thanks! BR
As I can see, you want to keep the existing time of day when using utcOffset method. It accepts a second parameter, which is a boolean. As the documentation says:
Passing true will keep the same local time, but at the expense of choosing a different point in Universal Time.
// "2018-11-28T02:00:00.000Z"
moment('2018-11-28T00:00:00.000').utcOffset('-02:00', true).toISOString();
For more information check the documentation

Get Firestore Server Timestamp from the client side

I'm building an app that tracks time. It calculates the time by differentiating the seconds like so:
serverTimestamp: FieldValue // {seconds: number, milliseconds: number}.
getSeconds() {
const createdTime = new Date(this.serverTimestamp * 1000).getTime()
const currentTime = new Date().getTime();
return Math.abs((currentTime - createdTime) / 1000);
}
The problem is that Date's values equal to the current client's device clock, while firestore's timestamp equal to the firestore server, which may cause inequivalence between these two dates.
The leads me with two alternatives:
1. Save Date instead of serverTimestamp().
This way, the difference between these two dates will be accurate. Although, they won't represent the real date. Plus, if the user will change his clock on his machine, then the seconds would be changed too.
2. Create an HTTPS function that retrieves the current server timestamp.
This way, the difference between these two dates will be accurate either. Although, it feels like I'm going too far only to get the current timestamp of the server.
I am probably going to stick with alternative number two. But I was hoping if there's a better solution to achieve my goal.
PS - I'm using #angular/fire. So, solutions from this package would be welcomed.
You can also write the current timestamp with a serverTimestamp, then read the time back out of the location it was just written. Then you can make changes or calculations with that value.

adding current time to ambiguous date using momentjs

I'm currently using FullCalendar and need to add the current time to an ambiguous date moment in the select callback function.
When selecting a day in month view, the callback function parameters "start" and "end" return an ambiguous date (no time) moment. I'm using the following to add the current time to the date then convert it back to a moment but wonder if this is the proper way to do it...
if(!start.hasTime()){
start = moment(start.format('YYYY-MM-DD'+' '+ moment().format('HH:mm:ss')));
end = moment( end.format('YYYY-MM-DD'+' '+ moment().format('HH:mm:ss')));
}
As you can see, I'm converting the "start" and "end" dates and current time moments into strings then converting the concatenated string back into a moment.
I want to make sure that the moment i'm creating is in valid ISO 8601 format.
Thanks,
Any help would be appreciated.
In the official docs says if you call format() function you get the date in ISO 8601 format.
check this out: jsfiddle
Furthermore, if you dont want to handle the timezones, you can use the utc() method
I had the same issue, strangely when I upgraded to fullcalendar v3.40. According to fullcalendar docs,
the format() and toISOString functions have been overridden within fullcalendar for ambiguously timed moments to return dates only i.e. '2017-03-06'. To overcome this and set a time, you need to create a new moment object which is what you have done. To ensure consistent parsing across browsers though, you should specify the format you are parsing as recommended by moment docs. So for your scenario, it will be:
if(!start.hasTime()){
var format = 'YYYY-MM-DD HH:mm:ss';
start = moment(start.format('YYYY-MM-DD'+' '+ moment().format('HH:mm:ss')), format);
end = moment( end.format('YYYY-MM-DD'+' '+ moment().format('HH:mm:ss')), format);
}
To make the code cleaner and easily readable though, you could create a new moment using the unix timestamp (which is a number and doesn't rely on string formats) of the start moment and set the time using the moment set function. So it could now look like this:
if(!start.hasTime()){
var timestamp = start.valueOf();
var now = moment();
var startMomentWithNowTime = moment(timestamp).set({
'hour': now.hour(),
'minute': now.minute(),
'second': now.second()
});
start = startMomentWithNowTime;
end = startMomentWithNowTime;
}
Excuse the descriptive variable naming startMomentWithNowTime. A few more lines but hopefully easier to read. You could also wrap this logic in a function to use on demand. Hope it helps!

How to set local timezone in Sails.js or Express.js

When I create or update record on sails it write this at updateAt:
updatedAt: 2014-07-06T15:00:00.000Z
but I'm in GMT+2 hours (in this season) and update are performed at 16:00.
I have the same problem with all datetime fields declared in my models.
How can I set the right timezone on Sails (or eventually Express) ?
The way I handled the problem after hours of research :
Put
process.env.TZ = 'UTC'; //whatever timezone you want
in config/bootstrap.js
I solved the problem, you should setting the MySql options file to change timezone to UTC
in the config/connections.js
setting at this
devMysqlServer: {
adapter: 'sails-mysql',
host: '127.0.0.1',
user: 'root',
password: '***',
database: '**',
timezone: 'utc'
},
Trying to solve your problem by setting the timezone on your server is a bit short-sighted. What if you move? Or someone in a different country accesses your application? The important thing is that the timestamps in your database have a timezone encoded in them, so that you can translate to the correct time on the front end. That is, if you do:
new Date('2014-07-06T15:00:00.000Z')
in your browser console, you should see it display the correct date and time for wherever you are. Sails automatically encodes this timestamp for you with the default updatedAt and createdAt fields; just make sure you always use a timezone when saving any custom timestamps to the database, and you should be fine!
The best architecture planning here, IMO, is to continue using Sails.js isoDate formatting. When you're user's load your website/app the isoDate will be converted to their client/browser timezone which is usually set at the OS level.
Here's an example you can test this out with. Open a browser console and run new Date().toISOString() and look at the time it sets. It's going to be based of off the spec for isoDate 8601 (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString).
Now, change your system time to a different time zone or simply change your hour on the time and save (you shouldn't have to reload if you're using chrome console). Run your command in the console again new Date().toISOString() and you'll get an adjusted time appropriate to the time you just changed.
If you'd like to continue on proving to yourself the time Sails.js is appropriate to use, use Moment.js on an isoDate that is stored in your database (created by waterline ORM) like so moment("2016-02-05T22:36:48.800Z").fromNow() and you'll notice the time is relative to your system time.
I've come to grips with not setting a timezone at the app level (I see why the sails authors did it that way), however I've been having a rough time performing a simple date match query. I'd assume that if you create a record using the default blueprint methods (this one containing an extra datetime field over the defaults), passing in a date, that you'd be able to pass in the same date in a get query and get the same record.
For example, let's say the datetime field is called "specialdate". If I create a new record through the api with "specialdate" equaling "06-09-2014" (ignoring time), I haven't been able to run a find query in which I can pass in "06-09-2014" and get that record back. Greater than queries work fine (if I do a find for a date greater than that). I'm sure it's a timezone offset thing, but haven't been able to come up with a solution.

Resources