Why is .ToLocalTime not converting UTC time to local time? - javascript

I have code in my razor view:
#Model.DateUpdated.ToLocalTime().ToString("dd/MM/yyyy HH:mm:ss")
In my model the property is as follows:
public DateTime DateUpdated { get; set; }
This is populated from a date stored in the database as a UTC DateTime.
So when the time in the database comes back as 01/01/2018 08:00, I expect it to render in the view as 01/01/2018 09:00 as my local time (the machine I'm running this on) is UTC+1.
However, it remains as it's original time, retrieved from the database.
How can I get it to convert to the time zone of the server the application is running on?

Before you output the string I'd suggest logging the DateTimeKind of the DateUpdated field.
e.g.
Console.WriteLine(DateUpdated.Kind);
To ensure the field DateUpdated is actually UTC you can try:
DateUpdated = new DateTime(DateUpdated.Ticks, DateTimeKind.Utc);
Then when you output, the result should actually be in local time.
If the DateTimeKind of the original DateUpdated field was DateTimeKind.Local, the function .ToLocalTime() will have no effect. This can happen when you load a DateTime from an external source, e.g. Database, parsing etc.
If the .Kind property of the DateUpdated field was either UTC or Unspecified, the ToLocalTime() call should work as expected.

Related

MongoDB Stored my date with the wrong time

I recently tried to assign a new date in MongoDB, but I have a problem with that, it stored the date I give but it's not correct
userSchema.methods.createPasswordResetToken = async function () {
this.passwordResetToken = crypto.randomBytes(20).toString('hex')
this.passwordResetExpires = moment().format(this.createAt)
await this.save()
console.log(moment().format(this.createAt)) // 2021-12-21T19:01:54+02:00
console.log(this.passwordResetExpires) // 2021-12-21T17:01:54.000Z
return { token: this.passwordResetToken, userId: this._id }
}
mongoDb remove 2 hours when storing it
and when I try to catch the type of two values
I got
console.log(moment().format(this.createAt)) // string
console.log(this.passwordResetExpires) // object
:
user Schema
...
passwordResetToken: String,
passwordResetExpires: Date
...
From the docs:
MongoDB stores times in UTC by default, and will convert any local time representations into this form. Applications that must operate or report on some unmodified local time value may store the time zone alongside the UTC timestamp, and compute the original local time in their application logic.
It seems your server just sits in GMT timezone ( utc +2, you could also see it from your date value // 2021-12-21T19:01:54+02:00 ). I would usually offer some hacky way to get around the issue but this is actually a best practice. Hence I recommend you do your date calculations in UTC and not in machine time.
note your other date is in UTC (2021-12-21T17:01:54.000Z), make sure your comparing apples to apples.

Get JavaScript local date from Moment JS

Consider the code :
let now = moment();
console.log(now.format()); // 2019-11-25T20:23:50+02:00
console.log(now.toDate()); // 2019-11-25T18:23:50.916Z
This is the output on my local machine , and when I check the app on Heroku
it gives the same values , even though I changed the TZ like this :
heroku config:add TZ="Asia/Jerusalem"
How can I get a JavaScript Date (Not a String !) object of my localtime , meaning 2019-11-25T20:23:50 ?
Let's walk through your code example:
let now = moment();
You create a Moment object. You don't pass any parameters, so it is initialized using the current timestamp (as if you called Date.now()) and set to "local mode".
console.log(now.format()); // 2019-11-25T20:23:50+02:00
By calling format, you ask the Moment object to produce a String. Since it's in local mode, the offset that applies to that moment in time for the local time zone is emitted in the result, and the wall time shown in the result is adjusted for that offset. In this case, the local time is two hours ahead of UTC. You then pass that string to console.log, which emits it to the console.
console.log(now.toDate()); // 2019-11-25T18:23:50.916Z
By calling toDate, you ask the Moment object to create a Date object. The "mode" of the moment object is no longer relevant because Date objects don't track anything other than a timestamp. Thus, the timestamp within the Moment object becomes the timestamp for the resulting Date object. Since you derived the Moment object from the current time, the result is the same as if you just called new Date() to begin with.
You then pass the string to console.log - except one can't just log an object, so it first has to convert it to something so you can see it. Here's the interesting part: There is no spec for this behavior. Implementations of ECMAScript can do whatever they like in this regard. Some implementations, like in your example, will call .toISOString() and log the result. Since .toISOString() displays the result in UTC, the result of logging a Date object is also shown in UTC. But other implementations will call .toString() on the Date object and log that, the result being in local time. It's entirely possible some future implementation could show the result in some graphical or interactive output. The point being, you can't rely on the behavior of console.log(Date) to be consistent.
No amount of changing your time zone settings will change this result. The Date object is inherently UTC-based, your output is also UTC-based, and UTC is the same over the whole planet (by design).
If you want the time zone reflected in the string output, you must use a function that produces a string with respect to local time. As you showed, you'll get that with .format() on a Moment object in local mode. You can also get one from calling .toString() on a Date object (but the resulting string is not in the same ISO 8601 format).
i would try,
moment().local().toDate()
but if you are planning to save date into db it's good practice to save time in UTC format for easier global conversion.
If you want to work timezones you may require also moment timezone package - https://momentjs.com/timezone/docs/
npm install moment-timezone
Hope this helps :)

Knex silently converts Postgres timestamps with timezone and returns incorrect time

I have a table in my psql database with a "trigger_time" column of type "TIMESTAMP WITH TIME ZONE DEFAULT now()"
I data in the row is this 2018-06-27 15:45:00-03.
When running from psql console
SELECT trigger_time AT TIME ZONE 'UTC'
FROM tasks
WHERE task_id = 1;
this query returns "2018-06-27 18:45:00".
Similarly when I run
SELECT trigger_time AT TIME ZONE 'America/Glace_Bay'
FROM tasks
WHERE task_id = 1;
I get 2018-06-27 15:45:00
Using knex.raw("SELECT trigger_time AT TIME ZONE 'America/Glace_Bay' FROM tasks WHERE task_id = 1") I get 2018-06-27T18:45:00.000Z and when running knex.raw("SELECT trigger_time AT TIME ZONE 'UTC' FROM tasks WHERE task_id = 1") I get 2018-06-27T21:45:00.000Z
Both of these results from knex are incorrect, how do I get knex to stop silently altering my data?
Probably things are failing because when you are querying datetimes from database in certain timezone and effectively converting type of timestamp to be timestamp without timezone. In that case database will not send information to knex about in which timezone that returned time was.
So knex (or rather pg driver which knex is using) interprets your timestamp as local time, which depends of timezone setup of your application server running knex.
You could fetch time just as UTC and do timezone conversion in JavaScript side with moment or luxon libraries (IMO latter is better for timezone handling).
Other solution would be to tell pg driver that timestamp and timestamp with timezone types should not be converted to JavaScript Date objects.
It can be done like this (https://github.com/brianc/node-pg-types):
const types = require('pg').types;
const TIMESTAMPTZ_OID = 1184;
const TIMESTAMP_OID = 1114;
types.setTypeParser(TIMESTAMPTZ_OID, val => val);
types.setTypeParser(TIMESTAMP_OID, val => val);
This code which makes all timestamps to be returned as strings may be added to for example in start of knexfile.js. Those returned strings will be exactly in the same format that they were returned by database server itself.
EDIT:
In code in the original post, when timestamp is converted to be in time zone UTC database server converts timestamp with time zone type to be normal timestamp without time zone so returned value doesn't have timezone information. To add timezone information back you can for example append +02 to the end of returned time stamp like this:
select ('2010-01-01T00:00:00.000Z'::timestamptz AT TIME ZONE 'UTC')::text || '+00';
Which returns 2010-01-01 00:00:00+00 to the driver which can be read correctly by pg driver too.
This will effectively do the same thing that just setting SET TIME ZONE 'UTC'; in db server when connection is created and just returning timestamptz column directly:
SET TIME ZONE 'UTC';
select '2010-01-01T00:00:00.000+02:00'::timestamptz;
Which will return 2009-12-31 22:00:00+00.

ColumnMapping.DataTableToObjectList gives me DateTime properties lacking timezone information

I have a webapi function that retrieves data from a T-SQL database using SqlConnection, SqlCommand etc. I get a DataTable which I convert to a data structure using
var foobars = ColumnMapping.DataTableToObjectList<Foobar>(dt)
The Foobar class contains DateTime fields.
When I return my foobars from the webapi method, those datetimes will be sent over the network in the following format:
2016-10-12T12:00:00
which seems to be a timezone-less datetime. AngularJS will treat it as 12:00, and javascript´s Date.getHours() will return 14 (I am in the +2 timezone).
If I do this operation before I return from my Web api method:
foobar.d = foobar.d.ToUniversalTime();
the string will get an additional Z
2016-10-12T12:00:00Z
and both AngularJS and javascript will agree about which time that is (12:00, which is also the indended time).
If I do
foobar.d = foobar.d.DateStart.ToLocalTime();
the string will be
2016-10-12T14:00:00+02:00
and AngularJS and javascript both reports it is 14:00, two hours more than the intended time).
From this, I conclude that
A DateTime object can be timezoneless, but if you try to convert it, it will be treated as if it was UTC.
ColumnMapping.DataTableToObjectList creates structures where DateTime objects are timezoneless.
I do not want to "patch" every DateTime object, that would be an error source (that would involve calling ToUniversalTime() on every DateTime object).
Can I specify a locale when I call ColumnMapping.DataTableToObjectList?
In webapi you can configure your JsonFormatter to serialize your datetimes how you want:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Formatters.JsonFormatter.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
}
}
This way you don't have to do it for every datetime, assuming I'm understanding your issue...

OData Date Filtering from JS

I am using the DXTREME framework from Devexpress to connect a HTML mobile app to an OData source.
One of my tables in SQL Server, exposed through the OData service is a table with a date (not datetime) field in it. It is exposed through OData like this:
<d:TaskDate m:type="Edm.DateTime">2010-04-01T00:00:00</d:TaskDate>
I am trying to filter the data on this field through a calendar control, but when I try to filter the datasource on the JS side, I get no matches. This is because the date is passed to the OData service, I believe, in UTC format, so if I query for TaskDate = '10/JUL/2013', I believe the date is passed as "09/JUL/2013 14:00". If I filter on TaskDate > '10/JUL/2013' I get results back from after "09/JUL/2013 14:00" at any rate.
I have tried declaring a new date with no time part:
filterDate = new Date(2013, 6, 10)
but is still doesn't work, it still subtracts 10 formy time zone on the JS side.
What I want to do is to return a lists of Tasks valid on that particular date. How can I achieve this?
I think my problem was the confusion around the dxDateBox control returning just a date, and that date being changed when passed to my odata service.
I solved the issue by converting the date to UTC myself, but just using the Date parts from the control, (where filterDate came from the control):
var paramDate = new Date(Date.UTC(this.filterDate().getFullYear(), this.filterDate().getMonth(), this.filterDate().getDate()));
this.dataSource.filter(["TaskDate", "=", paramDate]);
This works nicely, but seems rather verbose.

Categories

Resources