Query date with offset in AQL - arangodb

I have this document:
{
paymentDate: '2015-08-08T23:41:23.909Z'
}
my local time is GMT+7 hence the date above is 2015-08-09 6:41:23 in my local time.
I want to send this query below, and receive above document
{
date: '2015-08-09',
offset: '+7'
}
What is the best way to achive that in AQL ?

as can be read in the documentation about dates, ArangoDBs native format JSON doesn't know a special date format, and thus its suggested to store dates as strings.
Best practice is to store UTC in the Database and convert it into the users timezone in the application.
Therefore a query would use FILTER and string comparison to select ranges:
arangosh> db._create("exampleTime");
[ArangoCollection 729616498254, "exampleTime" (type document, status loaded)]
arangosh> var timestamps = ["2014-05-07T14:19:09.522","2014-05-07T21:19:09.522","2014-05-08T04:19:09.522","2014-05-08T11:19:09.522","2014-05-08T18:19:09.522"];
arangosh> for (i = 0; i < 5; i++) db.exampleTime.save({value:i, ts: timestamps[i]})
arangosh> db._query("FOR d IN exampleTime FILTER d.ts > '2014-05-07T14:19:09.522' and d.ts < '2014-05-08T18:19:09.522' RETURN d").toArray()
[
{
"value" : 2,
"ts" : "2014-05-08T04:19:09.522",
"_id" : "exampleTime/729617284686",
"_rev" : "729617284686",
"_key" : "729617284686"
},
{
"value" : 1,
"ts" : "2014-05-07T21:19:09.522",
"_id" : "exampleTime/729617088078",
"_rev" : "729617088078",
"_key" : "729617088078"
},
{
"value" : 3,
"ts" : "2014-05-08T11:19:09.522",
"_id" : "exampleTime/729617481294",
"_rev" : "729617481294",
"_key" : "729617481294"
}
]

If your local time in timezone GMT+7 is 2015-08-09 6:41:23, the JavaScript code new Date().toISOString() would return "2015-08-08T23:41:23.000Z" in that moment. As you can see, it returns UTC time. Your computer needs to have the correct date, time and timezone configured of course.
If you want to query for a date in the past or future, and that date is in local time, you can construct an ISO8601 string with timezone offset specified. Let's say we want to know what 2011-01-01 2:00:00 in GMT+7 is in UTC time:
// timezone offset: 07 hours, 00 minutes (+0700)
new Date("2011-01-01T02:00:00+0700").toISOString()
// result: "2010-12-31T19:00:00.000Z"
The same works in AQL:
RETURN DATE_ISO8601("2011-01-01T02:00:00+0700")
// result: "2010-12-31T19:00:00.000Z"
If you already have a datetime string without timezone offset (2011-01-01T02:00:00), but want to assume it's your local time, you can do the following in JS to append the timezone offset:
// Should return -420 for your timezone GMT+7.
// You can supply an offset in minutes manually as well of course.
var offset = new Date().getTimezoneOffset()
var offsetHours = offset / 60 | 0
var offsetMinutes = Math.abs(offset % 60)
var offsetStr = ((offsetHours < 0) ? "+" : "-") + // GMT + or -?
((Math.abs(offsetHours) < 10) ? "0" : "") + // leading zero for single digit
Math.abs(offsetHours) + // hour portion
((offsetMinutes < 10) ? "0" : "") + // leading zero for single digit
offsetMinutes // minute portion
var dateStr = "2011-01-01T02:00:00" + offsetStr
console.log(dateStr)
console.log(new Date(dateStr).toISOString())
// on a GMT+7 machine, result should be:
// "2011-01-01T02:00:00+0700"
// "2010-12-31T19:00:00.000Z"
If the date string is in local time, but Zulu timezone offset was somehow added, you could correct it by 7 hours like this:
// It should had been +0700 and not +0000
var d = new Date("2015-08-09T06:41:23Z").getTime() - 7 * 60 * 60 * 1000
// result: 1439077283000, which is 2015-08-08T23:41:23.000Z
// or in a really hacky way:
new Date("2015-08-09T06:41:23Z".replace("Z", "+0700"))
//edit
this seems to work too:
var d = new Date("2015-08-09T06:41:23Z")
d.setHours(d.getHours() - 7)
This seems to work reliably even if you cross start or end datetime of DST, at least in Firefox. There was a bug in Chrome however, which led to completely off date calculations: https://code.google.com/p/v8/issues/detail?id=3116

Related

Convert user define time to UTC using moment

We have application where we ask user to define time when they want to publish some content. In our application, user will select that content should publish X(0-5) day before target date and y time (0-23).
So if user create a content with publish date = "09/21/2021" and publish time as 3 it means this content needs to publish at 3 AM on 09/21/2021.
In our Mongo DB we need to store the calculated date as "2021-09-21T07:00:00" So it can be pulled by our Cron Job.
We tried will following without any success
var promotedDate = "2021-08-19 04:00:00"
var fmt = "YYYY-MM-DD hh:mm:ss";
var m = moment.utc(promotedDate, fmt);
console.log('promoted date ', tempDate)
console.log(m.local().format(fmt));
console.log(m.utc().format(fmt));
But it gives 2021-08-19 12:00:00
Any help ?
You can use moment.toISOString(). That will give you UTC regardless of the timezone of the moment in question thus: 2013-02-04T22:44:30.652Z.
Since that comes with milliseconds and a Z for Zulu time, we need to strip off the last 5 characters with string.slice():
const promotedDate = "2021-08-19 04:00:00";
const m = moment.utc(promotedDate, "YYYY-MM-DD hh:mm:ss");
const iso8601 = m.toISOString()
.slice(0,-5); // removes milliseconds and `Z` for Zulu
console.log( `promoted: ${promotedDate}` );
console.log( `iso8601: ${iso8601} );

How to save a birthdate in Mongoose without throwing validation error

In my User model in Mongoose, I have a simple dob field with no format validation:
dob: {
type: Date,
alias: 'birthdate'
},
Elsewhere in my code I am formatting three separate user-entered fields for day, month, and year. I am returning this as a new Date() but with a format of YYYY-MM-DD to avoid any weird timezone complications. The date is the date and time doesn't matter.
exports.formatDob = function (day, month, year) {
let dob = new Date( parseInt(year), parseInt(month) - 1, parseInt(day), 0, 0, 0, 0 );
return new Date(dob, '<YYYY-MM-DD>');
}
When I try to save this formatted date into Mongoose, I get the following error in my console:
(node:32307) UnhandledPromiseRejectionWarning: ValidationError: User
validation failed: dob: Cast to Date failed for value "Invalid Date"
at path "dob"
What am I doing wrong?
You can use Momentjs it's always recommended when it comes to working with dates.
However we can work around this just as easy as working with the Date obj api available methods for example if we want to return the format YYYY-MM-DD from a Date object we can just do the following:
let dob = new Date( parseInt(year), parseInt(month) - 1, parseInt(day));
return dob.getFullYear() + '-' + (dob.getMonth() + 1) + '-' + dob.getDate();
let dob = new Date();
console.log( dob.getFullYear() + '-' + (dob.getMonth() + 1) + '-' + dob.getDate() );
Notice we have added + 1 to getMonth method because it returns a value for 0-11 and also we have added this expression inside parentheses so it is not evaluated as a string.

Which method ideal to query Cloudant for latest doc having same value for a field and again filter on that result?

I am very new to NoSQL. My usecase is related to this.... Many users post messages and we store it in cloudant as different documents
{
id:random,
userid:xxx,
timestamp: 1449216912282,
msg: "Hi..."
}
I want to find out users who have not posted anything for last 5 days - Additionally I want to know if they have posted anything between last five and 10 days. If they have, then send a reminder mail to user to be active.
Which option would be better - views, search, cloudant query? Assume we will be having 1000s of posts per hour
I though of creating view - map(userid,timestamp) reduce as _stats and get max timestamp of each user. Then iterating through this list - we get users who did not post in last 5 days.
Other option was to use search index, get all userids between required timestamps. Compare both lists in application.
Is there any way to do it in a single query without overloading the application? Would Changing data format or creating appropriate index or view help?
If your data looked like this:
{
"_id": "abcdefghijklmon1234",
"userid" : "user1",
"timestamp": 1449739485035,
"msg": "Hi"
}
You could create a MapReduce view that created an index with a key consisting of [ 2015, 50, "user1" ], where "2015" is the year, "50" is the week number and "user1" is the document's userid. This can be achieved with a Map function like this:
function (doc) {
var getWeek = function(t) {
var date = new Date(t);
date.setHours(0, 0, 0, 0);
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
var week1 = new Date(date.getFullYear(), 0, 4);
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7);
};
if (typeof doc.timestamp == "number") {
var d = new Date(doc.timestamp);
var weeknum = getWeek(d.getTime());
var year = d.getFullYear();
emit( [ year, weeknum, doc.userid], null);
}
}
with a reduce of "_count". This allows queries such as ?startkey=[2015,49]&endkey=[2015,50]&group_level=3 to get a list of users who DID post last week. The list of users who didn't are the users who don't appear in the above list.
This isn't a solution to your problem in terms of "in the last 5 days", but uses week numbers instead.

How to determine two date is same day at local time in nodejs?

Here are two date, 2015-11-10 09:00:00+08:00 and 2015-11-10 01:00:00+08:00 is same day in China. But they are different day in UTC.
So... How can I determine two UTC date is same day at different timezone in node.js?
Sorry for my ambiguous description and poor English.
I have two strings: start_time and end_time. And clint upload two other strings: timezone_str, utc_time_str.
I want to determine the client time is between start and end time.
e.g
start_time: 2015-11-10 00:00:00
end_time: 2015-11-11 00:00:00
utc_time_str: 2015-11-10 02:00:00Z
timezone_str: America/New_York
except client_time.isBetween(start_time, end_time) == false.
Thanks to #Matt Johnson's answer, moment-timezone can solve my problem.
client_time = moment(utc_time_str).tz(timezone_str);
start = moment.tz(start_time, timezone_str);
end = moment.tz(end, timezone_str);
client_time.isBetween(start, end) === false
The most reliable way would be to use the parseZone function in moment.js to retain the provided offset. Then use the isSame function with the 'day' argument, which tests for same year, month, and day.
var m1 = moment.parseZone("2015-11-10 09:00:00+08:00");
var m2 = moment.parseZone("2015-11-10 01:00:00+08:00");
if (m1.isSame(m2, 'day')) {
// ...
}
With this approach, it does not matter what offset was given, nor does it matter what the time zone is on the computer where the code is running. It simply compares the two dates as provided.
calculate local "hours" like this
var d1 = new Date('2015-11-10 09:00:00+08:00')
var d2 = new Date('2015-11-10 01:00:00+08:00')
var h1 = d1.getHours() - d1.getTimezoneOffset() / 60
var h2 = d2.getHours() - d2.getTimezoneOffset() / 60
var date1 = d1.getDate() + (h1 >= 24? 1: 0)
var date2 = d2.getDate() + (h2 >= 24? 1: 0)
just compare date1 and date2
Solution without MomentJS
If you have the timezone string of the user that is in accordance with the tz database, such as "America/New_York" — which you can get client side for example by doing: const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
Then you could, for each date, create a new date with the timezone offset added to the original date, for example by doing const dateInTimezone = new Date(new Date(myDate).toLocaleString("en-US", { timeZone: timezone }));
And then reset the time values to exactly midnight of the same day by doing dateInTimezone.setHours(0, 0, 0, 0);
Then you could compare the milliseconds since Epoch values (date we start counting UTC from) of the two dates to see whether they start in the same day or not: const isSameDay = startOfDate1.valueOf() === startOfDate2.valueOf();
Ideally, your starting dates would always be in UTC, since both your server and client could be in all sorts of timezones. So before storing the data, convert the original date to its UTC version in milliseconds since Epoch, by doing myOrignalDate.valueOf() or myOrignalDate.getTime() — this way, you will ensure that timezone offsets added later will be true.
Example
function isSameDayInTimezone(d1, d2, timezone) {
const start_of_d1 = new Date(new Date(d1).toLocaleString("en-US", { timeZone: timezone })).setHours(0, 0, 0, 0).valueOf();
const start_of_d2 = new Date(new Date(d2).toLocaleString("en-US", { timeZone: timezone })).setHours(0, 0, 0, 0).valueOf();
return start_of_d1 === start_of_d2;
}
/* Initialize dates. Note that this will be the UTC time as milliseconds since
Epoch of the date in whichever timezone it was originally captured in. If I
capture it in my timezone, it would currently be UTC/GMT+3, hence if I were
to want to perform server-side actions taking into account my timezone, I'd
also need to capture my tz database timezone string first, to then be able
to, server-side, add my timezone offset to the captured UTC time. */
const date1 = new Date('August 31, 2022 20:16:00').valueOf();
const date2 = new Date('August 31, 2022 23:50:00').valueOf();
const date3 = new Date('August 31, 2022 00:00:00').valueOf();
const date4 = new Date('August 30, 2022 15:00:00').valueOf();
const fixedTimezone = "Europe/Tallinn";
const localTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
/* With fixed timezone */
console.log(isSameDayInTimezone(date1, date2, fixedTimezone));
console.log(isSameDayInTimezone(date1, date3, fixedTimezone));
console.log(isSameDayInTimezone(date1, date4, fixedTimezone));
/* With local timezone */
console.log(isSameDayInTimezone(date1, date2, localTimezone));
console.log(isSameDayInTimezone(date1, date3, localTimezone));
console.log(isSameDayInTimezone(date1, date4, localTimezone));
Voila, no MomentJS needed.

How do I display todays date in Node.js Jade?

I am new to Node.js and Jade and I have tried to use #{Date.now()} and it's giving me numbers. How do I display the date in mm/dd/yy format?
You can use moment.js
First, add moment to your express application locals
express = require('express');
...
app = express();
app.locals.moment = require('moment');
Then you can use moment within a jade template like this:
p #{moment(Date.now()).format('MM/DD/YYYY')}
Moment takes Date.now() by default, so yo can also write:
p #{moment().format('MM/DD/YYYY')}
Sources:
Making use of utility libraries in server-side Jade templates
Moment.js
This is old, but I do the following:
return (new Date()).toLocaleDateString()
Returns a date 'mm/dd/yyyy' format, in no additional libs required.
Or you could use the moment.js library to handle dates and format them accordingly to your needs
I actually started using date-util which is 2 parts both clientside and server side.
URL https://github.com/JerrySievert/node-date-utils
Using within a Browser
<script type="text/javascript" src="date-utils.min.js"></script>
Using with Node.js
$ npm install date-utils
require('date-utils');
Note: This did not work in the REPL before Node.js 0.6 due to how Node.js handles context in the REPL.
Static Methods
Date.today(); // today, 00:00:00
Date.yesterday(); // yesterday, 00:00:00
Date.tomorrow(); // tomorrow, 00:00:00
Date.validateDay(day, year, month); // true/false whether a date is valid
Date.validateYear(year); // true/false whether a year is valid
Date.validateMonth(month); // true/false whether a month is valid
Date.validateHour(hour); // true/false whether an hour is valid
Date.validateMinute(minute); // true/false whether a minute is valid
Date.validateSecond(second); // true/false whether a second is valid
Date.validateMillisecond(millisecond); // true/false whether a millisecond is valid
Date.compare(date1, date2); // -1 if date1 is smaller than date2, 0 if equal, 1 if date2 is smaller than date1
Date.equals(date1, date2); // true/false if date1 is equal to date2
Date.getDayNumberFromName(name); // su/sun/sunday - 0, mo/mon/monday - 1, etc
Date.getMonthNumberFromName(name); // jan/january - 0, feb/february - 1, etc
Date.isLeapYear(year); // true/false whether the year is a leap year
Date.getDaysInMonth(monthNumber); // number of days in the month
Instance Methods
d.clone(); // returns a new copy of date object set to the same time
d.getMonthAbbr(); // abreviated month name, Jan, Feb, etc
d.getMonthName(); // fill month name, January, February, etc
d.getUTCOffset(); // returns the UTC offset
d.getOrdinalNumber(); // day number of the year, 1-366 (leap year)
d.clearTime(); // sets time to 00:00:00
d.setTimeToNow(); // sets time to current time
d.toFormat(format); // returns date formatted with:
// YYYY - Four digit year
// MMMM - Full month name. ie January
// MMM - Short month name. ie Jan
// MM - Zero padded month ie 01
// M - Month ie 1
// DDDD - Full day or week name ie Tuesday
// DDD - Abbreviated day of the week ie Tue
// DD - Zero padded day ie 08
// D - Day ie 8
// HH24 - Hours in 24 notation ie 18
// HH - Padded Hours ie 06
// H - Hours ie 6
// MI - Padded Minutes
// SS - Padded Seconds
// PP - AM or PM
// P - am or pm
d.toYMD(separator); // returns YYYY-MM-DD by default, separator changes delimiter
d.between(date1, date2); // true/false if the date/time is between date1 and date2
d.compareTo(date); // -1 if date is smaller than this, 0 if equal, 1 if date is larger than this
d.equals(date); // true/false, true if dates are equal
d.isBefore(date); // true/false, true if this is before date passed
d.isAfter(date); // true/false, true if this is after date passed
d.getDaysBetween(date); // returns number of full days between this and passed
d.getHoursBetween(date); // returns number of hours days between this and passed
d.getMinutesBetween(date); // returns number of full minutes between this and passed
d.getSecondsBetween(date); // returns number of full seconds between this and passed
d.add({ milliseconds: 30,
minutes: 1,
hours: 4,
seconds: 30,
days: 2,
weeks: 1,
months: 3,
years: 2}); // adds time to existing time
d.addMilliseconds(number); // add milliseconds to existing time
d.addSeconds(number); // add seconds to existing time
d.addMinutes(number); // add minutes to existing time
d.addHours(number); // add hours to existing time
d.addDays(number); // add days to existing time
d.addWeeks(number); // add weeks to existing time
d.addMonths(number); // add months to existing time
d.addYears(number); // add years to existing time
You will need to use the methods on the Date object to achieve what you're after. See https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date
For example, the code below should do what you need:
var dateNow = new Date();
var dd = dateNow.getDate();
var monthSingleDigit = dateNow.getMonth() + 1,
mm = monthSingleDigit < 10 ? '0' + monthSingleDigit : monthSingleDigit;
var yy = dateNow.getFullYear().toString().substr(2);
var formattedDate = mm + '/' + dd + '/' + yy;
So if you were using say jade with express and node you could do something like:
res.render('jadeTemplateName', {
dateNow: function() {
var dateNow = new Date();
var dd = dateNow.getDate();
var monthSingleDigit = dateNow.getMonth() + 1,
mm = monthSingleDigit < 10 ? '0' + monthSingleDigit : monthSingleDigit;
var yy = dateNow.getFullYear().toString().substr(2);
return (mm + '/' + dd + '/' + yy);
}
})
and in your jade template say if you wanted to add the date to a span:
span Today's date is #{dateNow()}
add the class .post-date to a tag holding the date
document.addEventListener('DOMContentLoaded', function() {
var list = document.getElementsByClassName('post-date');
// get the number of selected elements
// iterate over elements and output their HTML content
for (var i=0; i<list.length; i++){
//console.log(list[i].innerHTML);
var string=list[i].innerHTML;
var length = 15;
var trimmedString = string.substring(0, length);
list[i].innerHTML=trimmedString ;
}
})
I found a solution
1.Create a function in pug using - syntax
2.pass the varible to the function when pug.js is binding variables to the pug template
-function prettyDate(dateString){
-var date = new Date(dateString);
-var d = date.getDate();
-var monthNames = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun","Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
-var m = monthNames[date.getMonth()];
-var y = date.getFullYear();
-return d+' '+m+' '+y;
-}
3.span.post-date #{prettyDate(val.date)};

Resources