I'm trying to make a parse query and put a date constraint on the query via javascript API.
This is the Result table/object (on parse.com) which I'm making a query for:
Column Data type
objectId String
createdDate Date
updatedAt Date
obtainedPonts Number
relatedDriver Relation
relatedTest Relation
ACL ACL
resultCeasesToBeValid Date
didPassTheTest Boolean
Some example data from resultCeasesToBeValid:
This is my goal: I want the query to give me a set of Result where today < resultCeasesToBeValid.
My problem is that I always recieve an Result array with .length = 0 when I'm trying to put date constraints on the query.
function IsApprovedAndHasValidResults(currentDriverObjectId) {
var Driver = Parse.Object.extend('Driver');
var currentDriverObject = new Driver();
currentDriverObject.id = currentDriverObjectId;
var queryResult = new Parse.Query(Result);
//set the constraints
queryResult.equalTo('relatedDriver', currentDriverObject); //this constraint works as expected
/****************************************************************************************************
*var today = new Date(); // "today" is as time of writing 3 oct 2014 *
* *
* //will give me a parseResults[] of .length=0: *
*queryResult.lessThan('resultCeasesToBeValid', today); *
* *
* //will give me a parseResults[] of .length=0: *
*queryResult.lessThan('resultCeasesToBeValid', { "__type": "Date", "iso": today.toISOString() }); *
* *
*****************************************************************************************************/
queryResult.find({
success: function (parseResults) {
// results is an array of Parse.Object.
/*when the code gets here parseResults array.lenght equals 0*/
},
error: function (error) {
// error is an instance of Parse.Error.
/*will never be here*/
}
});
}
Other developers seem to have the same problem, user Abhishek suspects a bug. According to Héctor Ramos (Parse), long time ago: "You should use a Date object, not a string, when dealing with dates in JavaScript.". Obviously this doesn't work!
Is my code wrong in some way?
Workaround:
queryResult._where.resultCeasesToBeValid = {'$lt' :{ "__type": "Date", "iso": today}}};
_where is a "private property" though and i wouldn't rely on it too much. Parse should fix this.
If you can [change the column type], i would suggest storing the date as a unix timestamp to avoid this kind of issues:
var timestamp = +new Date();
result.save({'resultCeasesToBeValid': timestamp});
Related
All I am trying to do is this.
Let's say in Google Sheets, I have a page which has the following columns:
In column I3 I have the date of 11/30/19.
In column J3, I have Today's date of 4/30/20.
I want to be able to calculate the difference between these two dates and tell me how many months has it been since 11/30/19.
Currently, I think the code is working somewhat but the result I get is:
[20-05-02 01:43:18:650 MDT] 5 months, 6 days
[20-05-02 01:43:18:656 MDT] 5 months, 6 days
[20-05-02 01:43:18:660 MDT] 5 months, 6 days
But the date calculations are still wrong. For example from Jan 1st - Jan 25, 2020, it shows 5 month and 6 days.
Also the loop I have inside Filter 1, is just calculating the first available date and then it does this three times instead of going to the next record and calculate.
I have the following code so far:
function myFunction() {
}
var moment = Moment.load();
/**
* #summary gets date difference
* #param {Date} startDate
* #param {Date} endDate
* #returns {string}
*/
function getDuration(startDate, endDate) {
const start = moment(startDate);
const end = moment(endDate);
const units = ['years', 'months', 'days'];
const lastIndex = units.length - 1;
const parts = units
.map((unit,i) => {
const diff = Math.floor(end.diff(start, unit, true));
if (diff > 0 || i === lastIndex) {
end.subtract(unit, diff);
return `${diff} ${unit}`;
}
})
.filter(Boolean);
return parts.join(', ');
}
function Filter2() { // Calculate the Time
const spread = SpreadsheetApp.getActiveSpreadsheet();
const sheets = spread.getSheets();
const [ sheet_1, sheet_2 ] = sheets;
const row = sheet_1.getRange("A:M");
const arr_col = sheet_1.getRange("I3:I50");
const lastSeen_col = sheet_1.getRange("J3:J50");
const startDate = arr_col.getValue();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var headerRowNumber = 2;
var rows = sheet.getDataRange().offset(headerRowNumber, 0, sheet.getLastRow() - headerRowNumber).getValues();
var filter = rows.filter(row => row[9] !== '');
// var digest = filter[0][9];
for(var i=0;i<filter.length; i++){
var res = getDuration(startDate, lastSeen_col);
Logger.log(res);
}
}
Why Dec, 31 1969
31st of December, 1969 in GMT-7 timezone offset is January 1st, 1970 00:00:00 UTC, which is the unix epoch. Therefore it is likely to be caused by an invalid date format passed to the Moment class instance.
Problem
getRange() method call returns an instance of Range from which you have to extract value via getValue() / getValues() before being able to use it. With that in mind, let's track what's going on in your script:
var ArrRow = sheet_1.getRange("I3:I") returns an instance of Range
mydata1 is defined somewhere globally (let's assume it holds an instance of Date)
getDuration is thus called like this: getDuration( <Range>, <Date> )
startDate and endDate are respectively an instance of Range and Date
start and end hold results of invoking the moment factory that creates a wrapper around Date object.
Step 5 is most likely to be the culprit, as per Moment library docs, moment() can accept String, Number, Date instances as well as format options, but start recieves a Range instance instead.
Possible solution
First, you need to ensure you pass in correct types (this why a lot of folks prefer TypeScript - if that's not your cup of tea, at least start using JSDoc - it will save you a ton of debug time).
I don't know how big the I3:I range is, so I assumed from the context that it is a single cell containing start date. Also note that I removed the var sld = new Date(dateString) assignment since you return a human readable string from getDuration() of format Y years, M months, D days which is not a dateString that Date constructor can accept.
Additionally, I would suggest changing forEach() to map() method for cleaner and less side effect prone code. General rule of thumb is that if input and output are of the same type, you likely want to use a mapping function.
function myFunction() {
Filter2();
}
function Filter2() {
const spread = SpreadsheetApp.getActiveSpreadsheet();
const sheets = spread.getSheets();
const [ sheet_1, sheet_2 ] = sheets;
const arr_col = sheet_1.getRange("I3:I");
const startDate = arr_col.getValue();
var dateString = getDuration(startDate, mydata1);
Logger.log(dateString);
}
var moment = Moment.load();
/**
* #summary gets date difference
* #param {Date} startDate
* #param {Date} endDate
* #returns {string}
*/
function getDuration(startDate, endDate) {
const start = moment(startDate);
const end = moment(endDate);
const units = ['years', 'months', 'days'];
const lastIndex = units.length - 1;
const parts = units
.map((unit,i) => {
const diff = Math.floor(end.diff(start, unit, true));
if (diff > 5 || i === lastIndex) {
end.subtract(unit, diff);
return `${diff} ${unit}`;
}
})
.filter(Boolean);
return parts.join(', ');
}
Notes
The answer assumes you use V8 engine (if you don't - switch to it, old one will be deprecated some time in the future, and as a bonus you get to use all the juciest language features [well, most of them]).
References
Date constructor docs on MDN
Range class docs
getValue() method docs
getValues() method docs
I am storing birthdays in my SQLite database, they are dates converted to my own timezone from another timezone, every hour I want to query my database to get the dates that are the same day and hour
// Stored in DB:
const date = parseFromTimeZone(`2020-${bdayReply} 13:42:00`, { timeZone });
// Query
const birthday = await Birthday.findOne({ where: { date: new Date() } });
// Generated query
query: SELECT "Birthday"."id" AS "Birthday_id", "Birthday"."userId" AS "Birthday_userId", "Birthday"."date" AS "Birthday_date", "Birthday"."birthdayConfigGuildId" AS "Birthday_birthdayConfigGuildId" FROM "birthday" "Birthday" WHERE "Birthday"."date" = ? LIMIT 1 -- PARAMETERS: ["2020-02-03T07:42:00.005Z"]
// Output of new Date() at the time of running the query
2020-02-03T07:42:00.023Z
// Example of record active in my DB at that point (date var from earlier)
2020-02-03 07:42:00.000
The ORM I'm using allows for raw queries as well, now I was wondering what my query should look like to return the above record in the example, or maybe I can use a date library like date-fns to convert the new Date() to match the format of the database, what would that look like?
If you want to use an SQL raw query, it should looks like this :
SELECT * FROM my_table WHERE MONTH(my_date) = MONTH(NOW()) AND DAY(my_date) = DAY(NOW())
In SQLite, the syntax is :
SELECT * FROM my_table WHERE strftime('%m',my_date) = strftime('%m','now') AND strftime('%d',my_date) = strftime('%d','now')
I have changed the datePattern of dijit/form/DateTextBox by providing an attribute
<form dojoType="dijit.form.Form" data-dojo-id="calc_form" id="calc_form">
<input type="text" data-dojo-type="dijit/form/DateTextBox" data-dojo-id="CONTRACT_DATE"
id="CONTRACT_DATE" name="CONTRACT_DATE"
constraints="{datePattern:'MM-dd-yyyy', strict:true}" />
</form>
i.e the attribute is constraints="{datePattern:'MM-dd-yyyy', strict:true}" and I got the date pattern shown correctly in the page as '01-28-2016'.
But when I tried to get the JSON of the form containing the dijit/form/DateTextBox using dojo.formToJson("formID"), I am getting a different value then the assigned pattern: '2016-01-28'
Why? Is there any solution for that?
The sole purpose of datePattern is to format the way the user types the date into the DateTextBox.
No matter what format the user types the date in, internally Dojo works in the ISO date format by design. This also makes it easier on you, the programmer.
If you're looking into converting ISO into another proprietary format, there's a module for that.
require(['dojo/date/locale'], function(locale) {
var date = locale.parse('2016-01-28', {
datePattern: 'yyyy-MM-dd',
selector: 'date'
});
var formattedDate = locale.format(date, {
datePattern: 'MM-dd-yyyy',
selector: 'date'
});
console.log(formattedDate);
});
The probleme is in the dojo.formToJson it return the default date format
whatever you specify the format in the dijit/form/DateTextBox input.
So , I suggest to format the date inside the generated jsonForm ,
here is a solution :
First import the required js ,
//AMD loading
require(["dojo/date/locale","dojo/json"],
function(locale,JSON){
......
})
"dojo/date/locale" used here to change date pattern
"dojo/json" used to Parse o json Object or reverse (Object to String)
then declare the formatJsonFormDates function
( params are explained in the code it return a new jsonForm with formatted date)
helps you to convert all date at ones if there is many dates in the Form, by
passing the name attribute of the input date in an Array parameter
//helper function
/**
* The function get generated 'form' from dojo.formToJson(HTML from),
* and replaces all string Date to the desired format
* ("YYYY-MM-dd" to "MM-dd-YYYY" by exemple)
*
* #param string jsonForm Value of generated jsonForm(HTML from)
* #param Object form Value of the Dijit.form.Form
* #param Array dateFieldsNames string array Values of date form fields to be formatted
* #param string datepattern Values of the wanted Dateformat // "mm-dd-YYYY"
*
* #return string jsonFormObject Value of new Returned jsonForm with desired date format
*/
var formatJsonFormDates = function(jsonForm,form,dateFieldsNames,datepattern){
//if no field passed to the function return default
if(!fieldsNames.length && fieldsNames.length < 1 ) return jsonForm;
jsonFormObject = JSON.parse(jsonForm);
for(var i = 0; i<fieldsNames.length ;i++){
//check if field is an instance of Date
if(form.getValues()[fieldsNames[i]] instanceof Date) {
newDate = locale.format(form.getValues()[fieldsNames[i]],{datePattern: datepattern, selector: "date"});
jsonFormObject[fieldsNames[i]] = newDate;
}
}
return JSON.stringify(jsonFormObject);
}
finnaly , after getting your jsonForm apply the function on it :
var formData = dojo.formToJson("yourFormID");
//I recomoand to use dijit/registry instead of dijit
formData = formatJsonFormDates(formData,dijit.byId("yourFormID"),["CONTRACT_DATE"],"MM-dd-yyyy");
.
I'm not quite understanding the timestamp usage,
e.g.
User create article and they can choose PublishDate , the system also store CreateDate automatically.
a. Should I make PublishDate and CreateDate timestamp with time zone and set utc?
b. User post string and then I convert like below use momentjs to utc timestamp and store, when someone select this row , show them as user client time reverse use momentjs
c. I use CURRENT_TIMESTAMP to the CreateDate, the CURRENT_TIMESTAMP does that mean the server time? am I doing correct?
My thinking is I always insert utc timezone timestamp to the database, and wherever the place user/client read, convert the data to user/client timezone? am I doing correct?
a. my database(postgres) created by
CREATE TABLE IF NOT EXISTS "Article"(
"ArticleId" SERIAL NOT NULL,
"PublishDate" timestamp with time zone,
"Active" bit NOT NULL,
"CreateByUserId" integer,
"CreateDate" timestamp with time zone,
PRIMARY KEY ("ArticleId")
);
SET timezone = 'UTC';
b. user submit post to store (nodejs)
// publishDate: '{"y":2015,"m":8,"d":16,"h":15,"mi":46,"s":24}
var publishDate = JSON.parse(req.body.publishDate);
var leadingZeroAndDateFormat = function(publishDate) {
return new Promise(function (fulfill, reject){
if (publishDate.m < 10) { publishDate.m = '0'+publishDate.m; }
if (publishDate.d < 10) { publishDate.d = '0'+publishDate.d; }
if (publishDate.h < 10) { publishDate.h = '0'+publishDate.h; }
if (publishDate.mi < 10) { publishDate.mi = '0'+publishDate.mi; }
if (publishDate.s < 10) { publishDate.s = '0'+publishDate.s; }
var str = publishDate.y+'-'+publishDate.m+'-'+publishDate.d+' '+publishDate.h+':'+publishDate.mi+':'+publishDate.s;
var utc = moment(str).unix();
fulfill(utc);
});
};
c. insert to database the CreateDate use CURRENT_TIMESTAMP
var insertArticle = function(publishDate, active, createByUserId) {
return new Promise(function (fulfill, reject){
var query = 'INSERT INTO "Article" ("PublishDate","Active","CreateByUserId","CreateDate") VALUES ($1,$2,$3,CURRENT_TIMESTAMP) RETURNING "ArticleId"';
dbClient.query(query,[publishDate,active,createByUserId], function(error, result) {
if (error) {
reject(error);
} else {
fulfill(result);
}
});
});
};
Update
When I change all column without timezone then I execute insertArticle shows the error
{ [error: date/time field value out of range: "1439717298"]
name: 'error',
length: 158,
severity: 'ERROR',
code: '22008',
detail: undefined,
hint: 'Perhaps you need a different "datestyle" setting.',
position: undefined,
internalPosition: undefined,
internalQuery: undefined,
where: undefined,
schema: undefined,
table: undefined,
column: undefined,
dataType: undefined,
constraint: undefined,
file: 'datetime.c',
line: '3775',
routine: 'DateTimeParseError' }
var insertArticle = function(publishDate, active, createByUserId) {
return new Promise(function (fulfill, reject){
var query = 'INSERT INTO "Article" ("PublishDate","Active","CreateByUserId","CreateDate") VALUES ($1,$2,$3,$4) RETURNING "ArticleId"';
dbClient.query(query,[publishDate,active,createByUserId,moment.utc().unix()], function(error, result) {
if (error) {
reject(error);
} else {
fulfill(result);
}
});
});
};
The simplest way is to always store time stamps without time zone and in UTC. This way it is always easy to use them for display and calculations. A difference is just a subtraction and comparisons go directly.
If the column was a time stamp with time zone, then the input would be converted to UTC anyway, but the output would be in the currently set time zone and if it's not set properly it might show wrong values. It also makes the database less efficient.
In the presentation layer the timestamps can be shown in the proper time zone and values input can also be converted to UTC for storage.
This is the simplest, most efficient and most flexible way of handling timestamps.
Using CURRENT_TIMESTAMP will get the timestamp from the server at the time of execution.
The incoming timestamps without timezone are parsed in local time instead of UTC -- I'd call this a bug in node-postgres. Anyway, you can override it to do the right thing by adding the following code:
pg.types.setTypeParser(1114, function(stringValue) {
console.log(stringValue);
return new Date(Date.parse(stringValue + "+0000"));
})
I am using Google Apps Script with a Google form. When the user submits the Google Form I get a value from a question. I then take that value and make it a date object, from what I saw on this post about daylight savings I use that to determine the timezone. I run the date object through Utilities.formatDate and want to get the correctly formatted date.
example: 9:00 AM
But instead I am getting a completely different time than expected.
My question is: Can someone help me understand why the below code is outputting a time that is 3 hours different?
function onSubmit(e) {
var values = e.values;
Logger.log(values);
try {
var start1 = new Date(values[3]);
var startN = new Date(start1).toString().substr(25,6)+"00";
var startT = Utilities.formatDate(start1, startN, "h:mm a");
Logger.log(startT);
} catch(error) {
Logger.log(error);
}
}
The assumption that Utilities formatDate does not support GMT... parameter is not true.
The post you mentioned in reference is used to get calendar events and is a useful way to get the right value when you get events from another daylight saving period (getting the TZ info from the calendar event itself), for example events for next month will be in "summer time" while we are still in "winter time"...
Your issue might come from different sources depending on time zone settings of your script vs timezone of the source. Could you describe the exact configuration in which you use this script ?
In the mean time, here is a small code that demonstrates how the code is working + the logger results :
function testOnSubmit() {
var eventInfo = {};
var values = {};
values['3'] = new Date();
eventInfo['values'] = values;
Logger.log('eventInfo = '+JSON.stringify(eventInfo)+'\n\n');
onSubmit(eventInfo);
}
function onSubmit(e) {
var values = e.values;
try {
var start1 = new Date(values[3]);
Logger.log('onSubmit log results : \n');
Logger.log('start1 = '+start1)
var startN = new Date(start1).toString().substr(25,6)+"00";
Logger.log('startN = '+startN);
var startT = Utilities.formatDate(start1, startN, "h:mm a");
Logger.log('result in timeZone = '+startT);
} catch(error) {
Logger.log(error);
}
}
EDIT : additionally, about the 30 and 45' offset, this can easily be solved by changing the substring length like this :
var startN = new Date(start1).toString().substr(25,8);
the result is the same, I had to use the other version a couple of years ago because Google changed the Utilities.formatDate method at some moment (issue 2204) but this has been fixed.
EDIT 2 : on the same subject, both methods actually return the same result, the GMT string has only the advantage that you don't have to know the exact timezone name... there is also the Session.getScriptTimeZone() method. Below is a demo script that shows the resulst for 2 dates in January and July along with the log results :
function testOnSubmit() {
var eventInfo = {};
var values = {};
values['3'] = new Date(2014,0,1,8,0,0,0);
eventInfo['values'] = values;
Logger.log('eventInfo = '+JSON.stringify(eventInfo)+'\n\n');
onSubmit(eventInfo);
values['3'] = new Date(2014,6,1,8,0,0,0);
eventInfo['values'] = values;
Logger.log('eventInfo = '+JSON.stringify(eventInfo)+'\n');
onSubmit(eventInfo);
}
function onSubmit(e) {
var values = e.values;
var start1 = new Date(values[3]);
Logger.log('onSubmit log results : ');
Logger.log('start1 = '+start1)
var startN = new Date(start1).toString().substr(25,8);
Logger.log('startN = '+startN);
Logger.log('result in timeZone using GMT string = '+Utilities.formatDate(start1, startN, "MMM,d h:mm a"));
Logger.log('result in timeZone using Joda.org string = '+Utilities.formatDate(start1, 'Europe/Brussels', "MMM,d h:mm a"));
Logger.log('result in timeZone using Session.getScriptTimeZone() = '+Utilities.formatDate(start1, Session.getScriptTimeZone(), "MMM,d h:mm a")+'\n');
}
Note also that the Logger has its own way to show the date object value ! it uses ISO 8601 time format which is UTC value.
Try this instead:
var timeZone = Session.getScriptTimeZone();
var startT = Utilities.formatDate(start1, timeZone, "h:mm a");
The Utilities.formatDate function expects a time zone that is a valid IANA time zone (such as America/Los_Angeles), not a GMT offset like GMT+0700.
I am making the assumption that Session.getScriptTimeZone() returns the appropriate zone. If not, then you might need to hard-code a specific zone, or use a different function to determine it.
Additionally, the +"00" in the script you had was assuming that all time zones use whole-hour offsets. In reality, there are several that have 30-minute or 45-minute offsets.