Sequelize set timezone to query - postgresql

I'm currently using the Sequelize with postgres in my project. I need to change the query, so it return created_at column with timezone offset.
var sequelize = new Sequelize(connStr, {
dialectOptions: {
useUTC: false //for reading from database
},
timezone: '+08:00' //for writing to database
});
But this affects on entire database. But I need to use timezone for select queries only. Does anyone know how to do that?

This is how I configured it:
dialectOptions: {
dateStrings: true,
typeCast: true,
},
timezone: 'America/Los_Angeles',
http://docs.sequelizejs.com/class/lib/sequelize.js~Sequelize.html

I suggest combining moment.js with one of the following methods:
If you need to parameterize the timezone, then you will probably want to add the offset for each individual query or add an additional field to your table that indicates the timezone, as it does not seem as though sequelize allows parameterized getters.
For example:
const moment = require('moment.js');
const YourModel = sequelize.define('your_model', {
created_at: {
type: Sequelize.DATE,
allowNull: false,
get() {
return moment(this.getDataValue('created_at'))
.utcOffset(this.getDataValue('offset'));
},
},
});
Another possibility would be to extend the model prototype's instance methods in a similar fashion, which allows you to specify the offset at the time that you require the created_at value:
const moment = require('moment.js');
YourModel.prototype.getCreatedAtWithOffset = function (offset) {
return moment(this.created_at).utcOffset(offset);
};

For correct using queries with timezone, prepare your pg driver, see details here:
const pg = require('pg')
const { types } = pg
// we must store dates in UTC
pg.defaults.parseInputDatesAsUTC = true
// fix node-pg default transformation for date types
// https://github.com/brianc/node-pg-types
// https://github.com/brianc/node-pg-types/blob/master/lib/builtins.js
types.setTypeParser(types.builtins.DATE, str => str)
types.setTypeParser(types.builtins.TIMESTAMP, str => str)
types.setTypeParser(types.builtins.TIMESTAMPTZ, str => str)
It's must be initialized before initiating your Sequelize instance.
You can now run a query indicating the timezone for which you want to get the date.
Suppose we make a SQL query, select all User's fields, and "createdAt" in timezone 'Europe/Kiev':
SELECT *, "createdAt"::timestamptz AT TIME ZONE 'Europe/Kiev' AS "createdAt" FROM users WHERE id = 1;
# or with variables
SELECT *, "createdAt"::timestamptz AT TIME ZONE :timezone AS "createdAt" FROM users WHERE id = :id;
For Sequelize (for User model) it will be something like this:
sequelize.findOne({
where: { id: 1 },
attributes: {
include: [
[sequelize.literal(`"User"."createdAt"::timestamptz AT TIME ZONE 'Europe/Kiev'`), 'createdAt'],
// also you can use variables, of course remember about SQL injection:
// [sequelize.literal(`"User"."updatedAt"::timestamptz AT TIME ZONE ${timeZoneVariable}`), 'updatedAt'],
]
}
});

Related

Mimic COALESCE behaviour using groups of conditions when querying with Prisma client

I'm trying to replace a SQL query (Postgres) with a Prisma client query, and I'm struggling to implement this COALESCE logic:
COALESCE(date_1, date_2,'1970-01-01 00:00:00') <= CURRENT_TIMESTAMP
As fas as I can tell, Prisma client doesnt currently support COALESCE, so there's no simple way for me to do this. I think I have to group conditions together to replicate this. The logic would be along the lines of:
ANY of the following
date_1 <= CURRENT_TIMESTAMP
ALL of the following
date_1 IS NULL
date_2 <= CURRENT_TIMESTAMP
ALL of the following
date_1 IS NULL
date_2 IS NULL
But I cant work out how to group conditions like this using the Prisma client, because you cant have multiple AND's within an OR (or vis versa):
const records = await client.thing.findMany({
where: {
OR: {
date_1: { lte: new Date() },
AND: {
date_1: null,
date_2: { lte: new Date() }
},
AND: { // <--- Cant do this, because we've already used AND above
date_1: null,
date_2: null
}
}
},
});
We obviously cant repeat the same object key twice, so I cant have two AND statements at the same level, otherwise I get the error:
An object literal cannot have multiple properties with the same name
I'm probably missing something obvious, but I cannot work out how to achieve groups of conditions like this. If anyone can help I'd appreciate it!
I should mention that I'm using:
nodejs v14.19.1
prisma ^3.14.0
I was indeed missing something obvious. We can pass in an array of groups to an AND or OR group, like so:
const records = await client.thing.findMany({
where: {
OR: {
date_1: { lte: new Date() },
AND: [
{
date_1: null,
date_2: { lte: new Date() }
},
{
date_1: null,
date_2: null
}
]
}
},
});
https://www.prisma.io/docs/concepts/components/prisma-client/filtering-and-sorting#filter-conditions-and-operators

Automatically Delete a Token that was created after some milliseconds in Mongoose

I have the following Mongoose Model that I wish to auto-delete after 2mins. Unfortunately, the auto-delete is not working. Note that, I wish to keep the created_at field as a Number in milliseconds not as a date. How do I go about getting the below code to work for me.
const mongoose = require("mongoose");
const TokenSchema = new mongoose.Schema(
{
_id: mongoose.Schema.Types.ObjectId,
token: String,
deleted: Boolean,
deleted_at: Number,
created_at: { type: Number, expires: '2m', default: new Date().getTime() },//Auto-Delete after 2minutes
updated_at: Number,
}
);
TokenSchema.pre('save', function (next) {
let shadow = this;
let now = new Date().getTime();
shadow.updated_at = now;
if (!shadow.created_at) {
shadow.created_at = now;
}
next();
});
Thank you
Mongoose uses MongoDB TTL Indexes for expiring documents, which only functions on fields containing either a Date or array of Date values.
If the indexed field for a document contains any other type, it will not be automatically expired, so to get auto-expiry working, you will need to have created_at store type: Date.
MongoDB internally stores dates as the number of milliseconds since epoch, which you can extract with the valueOf() method, and the mongo query language permits querying a date field by pass a number of milliseconds.

How to get the BSON UTC datetime value?

I am learning the timeseries Database. I have created the Database in the mongoDb as a timeseries. Now when I am inserting the document then I am getting an error
'created_at' must be present and contain a valid BSON UTC datetime value
I am not able to understand how can I get this datetime. I have tried all format known to me but still I am getting the same error.
try setting timezone as UTC as :
DateTimeZone zone = DateTimeZone.UTC;
DateTimeZone.setDefault(zone);
You should try something like:
const schema = Schema(
{
timestamp: { type: Date, default: Date.now },
name: String,
metadata: Object
},
{
timeseries: {timeField: 'timestamp',
metaField: 'metadata',
granularity: 'hours'}
autoCreate: false,
});
// `Test` collection will be a timeseries collection
const Test = db.model('Test', schema);
See if works for you.
Mongoose Timeseries Docs

Aurelia converted values and dataTables

I'm using DataTables jQuery plugin in Aurelia component. using column ordering and it works well excluding columns with dates.
Inside this columns I'm using value-convertet to convert isoString value to DD.MM.YYYY date format. Value covreters usage leads to wrong date column ordering, but if I'm not using value-converter everything works well. Unfortunately I didn't find any reason why it doesn't work correctly.
Wrong filtering example: I see rows with date value like 27.05.2010 before 18.05.2017
DataTables init:
$('#searchResultsTable').dataTable({
destroy: true,
searching: false,
paging: false,
orderMulti: false,
order: [[ 2, "desc" ]],
dateFormat: 'DD.MM.YYYY'
});
Date value converter (using moment library):
import * as moment from 'moment';
export class DateFormatValueConverter {
toView(value: Date, format: string): string {
if (value) {
return moment(value).format(format);
}
return null;
}
fromView(value: string, format: string): Date {
var isValid = moment(value, format, true).isValid();
if (value && isValid) {
return moment(value, format).toDate();
}
return null;
}
}
UPDATE:
Ordered with value converter
Orderd without ValueConverter(ordered like it should 2017 year value on the top)
The ordering mechanism of the data table is working correctly - it's your understanding that's off I'm afraid.
When ordering in descending order, any that start with 27. will be at the top, as they're the "biggest". Within all the dates that start with 27, it'll order on the month, biggest first, and then the year.
The order mechanism doesn't realise you're ordering a date so we need to look at the Custom Sorting Plugins;
https://www.datatables.net/plug-ins/sorting/
And specifically the Date-De plugin - as that matches your date format;
https://www.datatables.net/plug-ins/sorting/date-de
An example taken from the above page;
$('#example').dataTable( {
columnDefs: [
{ type: 'de_datetime', targets: 0 },
{ type: 'de_date', targets: 1 }
]
});

How create a Date field with default value as the current timestamp in MongoDb?

How to create a date field with default value,the default value should be current timestamps whenever the insertion happened in the collection.
Thats pretty simple!
When you're using Mongoose for example, you can pass functions as a default value.
Mongoose then calls the function for every insertion.
So in your Schema you would do something like:
{
timestamp: { type: Date, default: Date.now},
...
}
Remember to only pass the function object itself Date.now and not the value of the function call Date.now()as this will only set the Date once to the value of when your Schema got created.
This solution applies to Mongoose & Node.Js and I hope that is your usecase because you did not specify that more precisely.
Use _id to get the timestamp.
For this particular purpose you don't really need to create an explicit field for saving timestamps. The object id i.e. "_id", that mongo creates by default can be used to serve the purpose thus, saving you an additional redundant space. I'm assuming that you are using node.js so you can do something like the following to get the time of particular document creation:
let ObjectId = require('mongodb').ObjectID
let docObjID = new ObjectId(<Your document _id>)
console.log(docObjID.getTimestamp())
And, if you are using something like mongoose, do it like this:
let mongoose = require('mongoose')
let docObjID = mongoose.Types.ObjectId(<Your document _id>)
console.log(docObjID.getTimestamp())
Read more about "_id" here.
When Creating Document, timestamps is one of few configurable options which can be passed to the constructor or set directly.
const exampleSchema = new Schema({...}, { timestamps: true });
After that, mongoose assigns createdAt and updatedAt fields to your schema, the type assigned is Date.
You would simply do this while inserting... for current timestamp.
collection.insert({ "date": datetime.now() }
Let's consider the user schema in which we are using created date, we can use the mongoose schema and pass the default value as Date.now
var UserSchema = new Schema({
name: {type: String, trim: true},
created: {type: Date, default: Date.now}
});
If we want to save timetamp instead of number then use Number isntead of number like that
var UserSchema = new Schema({
name: {type: String, trim: true},
created: {type: Number, default: Date.now}
});
Note:- When we use Date.now() in the default parameter then this will
only set the Date once to the value of when your Schema got created,
so you'll find the dates same as the that in the other document. It's better to use Date.now instead of Date.now().
Here's a command that doesn't set a default, but it inserts an object with the current timestamp:
db.foo.insert({date: new ISODate()});
These have the same effect:
db.foo.insert({date: ISODate()});
db.foo.insert({date: new Date()});
Be aware that Date() without new would be different - it doesn't return an ISODate object, but a string.
Also, these use the client's time, not the server's time, which may be different (since the time setting is never 100% precise).
I just wish to point out that in case you want the timestamp to be stored in the form of an integer instead of a date format, you can do this:
{
timestamp: { type: Number, default: Date.now},
...
}
Thanks friends ..
I found another way to get timestamp from _id field. objectid.gettimestamp() from this we can get it time stamp.
This is a little old, however I fount when using the Date.now() method, it doesn't get the current date and time, it gets stuck on the time that you started your node process running. Therefore all timestamps will be defaulted to the Date.now() of when you started your server.
One way I worked around this was to do the following:
ExampleSchema.pre('save', function (next) {
const instanceOfSchema = this;
if(!instanceOfSchema.created_at){
instanceOfSchema.created_at = Date.now();
}
instanceOfSchema.updated_at = Date.now();
next();
})
createdAt: {type: Date, default:Date.now},