There is no calibration between MongoDB UTC time and local time - javascript

Data is stored and inquired through the API on the web page, and the API is using MongoDB.
The server space has been unified to UTC time so that the same results can be achieved in different time zones.
MongoDB uses the Mongoose schema as follows:
const userSchema = new Schema({
userId : {
type : String
},
score : {
type : Number
},
createdAt : {
type : Date,
default : Date.now
}
});
Because Date.now in the schema is in the createdAt field by default, it does not pass the Date value separately when querying the create or update of mongoose.
Considering the case where offset exists based on UTC time, the time is calculated using moment.js as follows:
// -540 offset value for KST 9 hours faster than UTC
const utc= moment.utc().add(offset, 'm').format('YYYY-MM-DDTHH:mm:ssZ');
let beginDate = new Date(utc);
let endDate = null;
let year = beginDate.getFullYear();
let month = beginDate.getMonth();
let date = beginDate.getDate();
// To view the full duration of the day
beginDate = new Date(new Date(year, month, date).setHours(0, 0, 0, 0));
endDate = new Date(new Date(year, month, date).setHours(23, 59, 59, 59));
// Find document
const user = await userSchema.aggregate([
{
$unwind : 'lists'
},
{
$match : {
'lists.createdAt' : {
$gte : beginDate,
$lte : endDate
}
}
},
...
]);
For example, if you make a query in Korea, the data inquiry may differ from the time after midnight and to 9 a.m. the next day.
What is wrong with the above parallax correction logic? I don't exactly understand the current problem.

Why so difficult? Simply use
{
$match : {
'lists.createdAt' : {
$gte : moment().startOf('day').toDate(),
$ltr : moment().endOf('day').toDate()
}
}
}
moment().startOf('day').toDate() returns the begin of current day in your local time zone. I live in Switzerland, thus it returns ISODate("2023-01-17T23:00:00.000Z")
But you can specify also a time zone, e.g.
moment.tz('Asia/Seoul').startOf('day').toDate();
ISODate("2023-01-17T15:00:00.000Z")
The problem is moment.utc().add(...) really modifies the time value. But that is not what you need to do, you like to change only the way how the time is displayed in your local time zone.
For comparison and calculations, the displayed value does not matter. All methods are done in UTC time only.

Related

explain MongoDB date format

For defining a model in node.js I am using mongoose library and I want to store the date in that.
here is the schema:
availability: [
{
day: {
type: String,
},
startTime: {
type: Date,
},
endTime: {
type: Date,
},
},
]
I am passing the value from postman like below:
{
"parkingId":"62cfa09adb8bc68f045b4d65",
"day":"Monday",
"startTime":7,
"endTime": 22
}
But it is getting saved like this from 1970. I am aware that passing a time like this will calculate time from Unix epoch But I want to save today's date or date passed by merchant user for availability
day:Monday,
startTime: ISODate("1970-01-01T00:00:00.007+00:00"),
endTime: ISODate("1970-01-01T00:00:00.022+00:00")
And also what is 007+00:00 or 022+00:00?
Should I use moment library for that?
This is unrelated to MongoDB, this is a mongoose feature.
You define startTime and endTime as Date in your schema, but you are passing a number. let's see what the mongoose code does in this scenraio:
function castDate(value) {
...
let date;
if (value instanceof Number || typeof value === 'number') {
date = new Date(value);
}
...
};
As you can see in the case the input value is a number which is the case here, the mongoose schema casts this value to date using js new Date() function which is basically:
JavaScript Date objects represent a single moment in time in a platform-independent format. Date objects contain a Number that represents milliseconds since 1 January 1970 UTC.
This means when it casts new Date(7) the result is 1 January 1970 UTC + 7 mili seconds whichi is the 7 your seeing in the result 1970-01-01T00:00:00.007+00:00
Clearly in your case all you need to do is change the Date type to Number which is actually what you're trying to save.

from `const now = new Date()`, how can I verify that I am before or equal a particular date at a given timezone and hour?

I have a timezone map with publishing hour in the local zone with news that must define when they should be published on a date using a date picker.
This is a new news article that is initialized with the following:
{ timeZoneId: 'Europe/Paris, releaseHour: 9, publishingDateTime: undefined } // 9 is the hour GMT+1
I want to know how can I from const now = new Date(), verify if this article should be
published today or the next day, the criteria are:
Is now before releaseHour? (is 9am GMT+1 in paris already passs or not)
If yes, then we should offer the next release slot at 9am GMT+1 + 1 day
If no, then we should use the release slot at 9am the same day
How is this possible?
This is how I have tried:
import { isBefore, isEqual } from 'date-fns';
import { utcToZonedTime } from 'date-fns-tz';
export const getNextPublishingDateTime = (now, timeZoneId, releaseHour) => {
const zoned = utcToZonedTime(now, timeZoneId);
const releaseTime = new Date(zoned.toISOString());
releaseTime.setHours(releaseHour, 0, 0, 0);
if (isBefore(zoned, releaseTime) || isEqual(zoned, releaseTime)) {
console.log('before');
return releaseTime;
}
releaseTime.setDate(releaseTime.getDate() + 1);
console.log('after');
return releaseTime;
};
But the hour returned by utcToZonedTime is not +01:00 offset, instead it is a date at my offset.
I have tried some other ideas, using moment-tz and vanilla Date, I found this task a bit complicated and hope to find help with the JS community as this look to be a normal date comparaison.
You can use the Intl object and formatToParts method to get the current time in any supported timezone using IANA representative locations. It's reasonably well supported.
E.g.
function getHour(date, loc) {
let d = new Intl.DateTimeFormat("en-EN", {
hour: 'numeric',
hour12: false,
timeZone: loc
});
return d.formatToParts(date)[0].value;
}
let loc = 'Europe/Paris';
let now = new Date();
console.log(`The current hour in ${loc} is ${getHour(now, loc)}.`);
The above is just for illustration, there should be validation of input and return values.

Extract specific digits from a .txt file

I have been asked to count the number of tweets per hour by day (0 - 23) in a huge text file of random tweets. The date is not interesting, only the tweet per hour. I want to return them in a new array of objects. Each object should have properties hour and count like this:
{hour: x, count: y},
I've made a function where I'm declaring an empty array, in which I will put my data:
function(tweets) {
let result = [];
and I think I need to push them like this:
result.push({hour: x, count: y});
But I don't know how to extract the specific hour from my object (key and value).
in the huge, raw data file, each tweet is logged with a date like this:
created_at: "30-06-2015 14:27",
Any suggestions or experience? I'm currently learning about regex and for loops. Should I use them in this code or is there a smarter way?
Edit: as you asked for more details:
The raw data are object in an array with the following structure:
{
time: Date-object,
created_at: "30-06-2015 14:27",
fromUsername: "victor",
text: "asyl og integration",
lang: "da",
source: "Twitter for Android",
}
About extracting text I see good answer here. Instead of console.log add parsing and saving to your array.
About regexp - I think it should be something like
var re = /created_at: \"([^\"]*)\",/g;
What I would do is work from a different angle:
create an object with a dateTimeHour for the start of each hour that you care about. It should presumably be a limited timespan like for all tweets that happened before now:
So generate something that looks like this dynamically:
{
'2019-03-01T17:22:30Z': 0, // or simply '1552667443928'
'2019-03-01T18:22:30Z': 0,
'2019-03-01T19:22:30Z': 0,
'2019-03-01T20:22:30Z': 0,
...etc
}
Which you can do using current Date and then a loop to create additional previous date times:
const now = new Date()
// you can use a generator here or simply a while loop:
const dateTimes = {}
while(now > REQUIRED_DATE)
dateTimes[new Date(now.setHours(now.getHours() - 1))] = 0
Now you have an exhausted list of all the hours.
Then, check if the given tweet is within that hour:
check if item.created_at < currentHourBeingLooked because you should loop through the Object.keys(dateTimes).
Then, loop through each item in your list and check if it fits that dateTime if so increment dateTimes[currentHour]++.
So, the hardest part will be converting created_at to a normal looking date time string:
const [datePortion, timePortion] = "30-06-2015 14:27".split(' ')
const [day, month, year] = datePortion.split('-')
const [hour, minute] = timePortion.split(':')
now with all those date, month, year, hour, and minute you can build a time object in javascript:
It follows the formula:
From MDN:
new Date(year, monthIndex [, day [, hours [, minutes [, seconds [, milliseconds]]]]]);
AKA:
new Date(year, monthIndex, day, hours, minutes, seconds);
So for December 17, 2019 # 3:24am it'll be:
const = new Date(2019, 11, 17, 3, 24, 0);
I'll assume that you already know to use regex from the post pointed by Ralkov to get all of your created_at dates, and my answer will go from that.
You said the date is not important so once you have the string
'created_at: "30-06-2015 14:27"'
we need to get rid of everything except for the hour, i did it by extracting substrings, feel free to try other approaches, this is just to get you started.
var date = obj.substr(obj.indexOf(' ') + 1);
var time = date.substr(date.indexOf(' ') + 1);
var hour = time.substr(0, time.indexOf(':'));
will get yo the hour
"14"
Note that this only works for one day, you need to do some additional changes if you'd like to store tweet hour count for different days in the same data structure
When you write your for-loop use the following function each time you find a tweet and already extracted the hour, it stores a combination of value-pairs into a map variable defined outside the function, creating a new pair if necessary or just updates it with the new tweet count.
function newTweet(hour, tweetsPerHour) {
var tweetsThisHour = tweetsPerHour.get(hour);
tweetsThisHour = tweetsThisHour === undefined ? 0 : tweetsThisHour;
tweetsPerHour.set(hour, ++tweetsThisHour);
console.log(tweetsThisHour)
}
complete code:
var obj = 'created_at: "30-06-2015 14:27"';
var date = obj.substr(obj.indexOf(' ')+1);
var time = date.substr(date.indexOf(' ')+1);
var hour = time.substr(0, time.indexOf(':'));
var tweetsPerHour = new Map();
newTweet(hour, tweetsPerHour); //this is the extracted hour
newTweet("16", tweetsPerHour); //you can try different hours as well
newTweet("17", tweetsPerHour);
function newTweet(hour, tweetsPerHour) {
var tweetsThisHour = tweetsPerHour.get(hour);
tweetsThisHour = tweetsThisHour === undefined ? 0 : tweetsThisHour;
tweetsPerHour.set(hour, ++tweetsThisHour);
console.log(hour + " tweet count: " + tweetsThisHour)
}
what the code is doing is storing the hour and count of tweets in pairs:
[{"14":1} ,{"16":1}, {17:1}]
for example if you add "14" again it would update to
[{"14":2}, {"16":1}, {17:1}]
dig into JavaScript Map Objects as well.
Your code flow is something like the following:
Read .txt file
loop through dates -> get hour from date -> newTweet(hour,
tweetsPerHour).

Saving a String in MongoDB as a date saves wrong values

I am trying to save a string in MongoDB as a date but having hard times for storing the right values.
In Mongoose schema data value is stored as Date, however, I pass the value to data as new Date("MM-dd-YYYY") but when I look up in the database the value is transformed to this format ISODate("YYYY-MM-dd-1T21:00:00Z")
The format wouldn't bother me if the date would be the same but as you notice the value in the database is one day earlier then the value which I want to be.
So instead of 2018-09-20 is 2018-08-19. My guess is that default UTC time is not the same or something like that but how can I set the correct UTC time?
Edit:
var mongoose = require('mongoose')
var dateformat =require('moment');
//Schema
var ReservationSchema = mongoose.Schema({
name : {
type:String,
required : true,
},
numberOfGuests : {
type : Number ,
required : true,}
,
email: {
type : String,
required:true,
},
phone: {
type : String,
required:true,
},
data:{
type:Date,
require:true,
},
timetables:{
type:String,
require:true,
},
furtherRequests: {
type : String,
}
});
var reservvar = module.exports = mongoose.model('Rezervari', ReservationSchema ,'Rezervari');
module.exports.createReservation = function (query,callback){
//query.data = dateformat.utc(query.data).format("MM-DD-YYYY")
reservvar.create(query,callback);
}
module.exports.getReservations = function (callback){
reservvar.find({},callback);
}
Index.js file :
app.get('/api/reservations',function(req,res) {
Rezervari.getReservations(function(err,reserv){
if(err){
throw err;
}
var changetime = reserv[1].data;
console.log(reserv[1].data)
changetime = dateformat.utc(changetime).format("MM-DD-YYYY") // this one returns the date in desired format but with wrong values as stored in db
console.log(changetime)
res.json(reserv);
});
});
app.post('/api/createrezervare', function (req,res) {
const reserv = req.body
const name = reserv.name
const numberofg = reserv.number
const phone = reserv.phone
const email = reserv.email
const data = reserv.date
const timetable = reserv.time
const furtreq = reserv.frequests
Rezervari.createReservation({name:name,numberOfGuests:numberofg,phone:phone,email:email,data:data,timetables:timetable,furtRequests:furtreq},function(err,reserv){
if(err){
throw err}
res.json({status:true})
})
})
You are inserting a Javascript Date Object from Node.js, and that same Date is being inserted in MongoDB, it's being inserted correctly.
I think you are confusing how dates are stored internally and how are they formatted when you print them.
When you check the content of your data in MongoDB it's just shown in that particular format, an ISO date. If you take a close look at the date shown you can see a Z a the end, Z means "zero hour offset" also known as "Zulu time" (UTC).
In Javascript when you create a Date object without setting the timezone, it's by default created in your system timezone. Also, Date objects are not stored with any format, nor in JS nor in MongoDB. In JS, dates are stored internally as time values (milliseconds since 1970-01-01).
Supposing we are in Japan, JST time (UTC+9):
const d = new Date("09-20-2018");
console.log(d.getTime()); // 1537369200000
console.log(d.toString()); // Thu Sep 20 2018 00:00:00 GMT+0900 (JST)
console.log(d.toISOString()); // 2018-09-19T15:00:00.000Z
First we are printing out the number of ms, after the Date including the timezone, and finally the ISO Date, almost same format that MongoDB uses to print dates in the Mongo shell (anyway, in UTC).
So, new Date("09-20-2018") is going to store the milliseconds until 09-20-2018 00:00 in Japan Time. Then, if you insert that object in MongoDB, internally it will store the correct date (I don't know internal details of MongoDB, but maybe it's storing the milliseconds as well).
If you check MongoDB you will see something like ISODate("2018-09-19T15:00:00Z").

Searching Dates and Ignoring time and date in mongoDB node.js

I am using node.js and monodb database where I want to query records based on date and I want to ignore time and date only month and year will be matched here is my code:-
collection.find({ date: { "$gte": sDate, "$lt": eDate) } }).count(function (e, c) {
});
this is working but matching date and time as well how I can match only month and year? Please help me to solve this problem.
Edit:- some data from collection:-
{
"_id" : ObjectId("5535e76f82a964d011e34fcf"),
"VisitorId" : "5535e72a82a964d011e34fcb",
"date" : ISODate("2015-01-21T06:00:15.761Z"),
}
{
"_id" : ObjectId("5535e75f82a964d011e34fcf"),
"VisitorId" : "5535e72a82a964d011e34fcb",
"date" : ISODate("2015-04-21T06:00:15.761Z"),
}
I will pass two params i.e {month:"1",year:"2015"};
and in output first docs should be display.
Thanks
You could use the $where operator in your query:
collection.find({
"$where": function() {
return this.date.getMonth() == 0 && this.date.getFullYear() == 2015
}
}).count(function (err, data) {
// Handle err
});
However, query performance is rather compromised because using $where alone requires a table scan, it takes a global lock. You should use $where only when you can't express your query using another operator. If you must use $where , try to include at least one other standard query operator to filter the result set.
Other options are to modify your schema and store the month in its own property (if it's a common field in your queries). You are guaranteed better query performance since the field can be indexed.
The other option will be when query a specific month and year, create a query object that only looks for the start and the end of that specific month.
var sDate = new Date(2015, 0, 1);
var eDate = new Date(2015, 1, 1);
collection.find({ date: { "$gte": sDate, "$lt": eDate) } }).count(function (e, c) {
});

Categories