I asked this question last month, and had no response. Isn't Google supposed to be monitoring this forum for Gmail API questions? Is there another place that I could get support (even paid?) I'm kind of desperate. Anyway...
I am using the "insert" API to add a message to Gmail, and using the query parameter internalDateSource=dateHeader. However Gmail seems to ignore the "Date" header that is in my RFC822 data, just substituting the current date. My POST url looks like this:
https://www.googleapis.com/gmail/v1/users/user#gmail.com/messages?uploadType=multipart&internalDateSource=dateHeader&access_token=XYZ
My uploaded data looks like this:
{ "labelIds": [ "Label_96" ],"raw": "RnJvbTo...tDQr_" }
There is no doubt that the correct "Date" header is in the encoded raw data. (Here is an example):
Date: Wed, 1 Oct 2011 10:47:00 -08:00
Google picks up all the other headers and message data correctly. What am I doing wrong? BTW I did try adding "payload" and "headers" to my json in an attempt to specify a Date header. No change.
TL;DR
I think you formatted your Date wrong. It should be
Wed, 1 Oct 2011 10:47:00 -0800
Not
Wed, 1 Oct 2011 10:47:00 -08:00
This question really intrigued me, and I got it to work!
First, I created an example mail, encoded it and made it URL safe:
btoa(
"Date: Thu, 1 Jan 1970 01:00:00 -0400\n"
"Content-Type: text/plain;\n" +
"to: example#gmail.com\n" +
"from: example#gmail.com\n" +
"subject: Example Subject Text\n\n" +
"The actual message text goes here"
).replace(/\+/g, '-').replace(/\//g, '_');
I used my own email address instead of example#gmail.com, of course.
Which resulted in the following base64-encoded data:
RGF0ZTogIFRodSwgMSBKYW4gMTk3MCAwMTowMDowMCAtMDQwMApDb250ZW50LVR5cGU6IHRleHQvcGxhaW47CnRvOiBleGFtcGxlQGdtYWlsLmNvbQpmcm9tOiBleGFtcGxlQGdtYWlsLmNvbQpzdWJqZWN0OiBFeGFtcGxlIFN1YmplY3QgVGV4dAoKVGhlIGFjdHVhbCBtZXNzYWdlIHRleHQgZ29lcyBoZXJl
Then, I just mimicked your request:
POST https://www.googleapis.com/gmail/v1/users/me/messages?internalDateSource=dateHeader&key={YOUR_API_KEY}
{
"raw": "RGF0ZTogIFRodSwgMSBKYW4gMTk3MCAwMTowMDowMCAtMDQwMApDb250ZW50LVR5cGU6IHRleHQvcGxhaW47CnRvOiBlbXRob2xpbkBnbWFpbC5jb20KZnJvbTogZW10aG9saW5AZ21haWwuY29tCnN1YmplY3Q6IEV4YW1wbGUgU3ViamVjdCBUZXh0CgpUaGUgYWN0dWFsIG1lc3NhZ2UgdGV4dCBnb2VzIGhlcmU="
}
Which resulted in a mail on the beginning of the epoch under my "All mail"-label.
Related
When I test this code in the browser, it processes dates in a logical way (invalid date), but in NodeJS new Date("12345") creates +012345-01-01T06:00:00.000Z
Here is my test for NodeJS
$ node -e 'console.log(new Date("12345"))'
// outputs: +012355-01-01T06:00:00.000Z
Here you can run it in browser
console.log(new Date("12345"))
// outputs: null
Any idea why this is happening? And how can I avoid this?
It depends on the JavaScript engine. In Firefox, dates after January 1, 10000 (year ten thousand) are apparently considered invalid:
> new Date("9999")
Date 9999-01-01T00:00:00.000Z
> new Date("10000")
Invalid Date
In Chrome (that uses V8, just like Node.js) it works perfectly:
> new Date("10000")
Sat Jan 01 10000 00:00:00 GMT-0800 (Pacific Standard Time)
> new Date("12345")
Mon Jan 01 12345 00:00:00 GMT-0800 (Pacific Standard Time)
So, to answer your question: different JavaScript engines have different limits related to which date is valid and which is not, but in general, having just a sequence of digits in a string makes it think it's January 1st of this year (corrected for a timezone used):
> new Date("2019")
Mon Dec 31 2018 16:00:00 GMT-0800 (Pacific Standard Time)
Also note this. Chrome / Node.js:
> new Date("31")
Invalid Date
> new Date("32")
Thu Jan 01 2032 00:00:00 GMT-0800 (Pacific Standard Time)
Firefox:
> new Date("1000")
Date 1000-01-01T00:00:00.000Z
> new Date("999")
Invalid Date
Apparently, Firefox wants the year to have 4 digits exactly, while Chrome/V8 is more flexible.
Also, seems like Chrome/V8 has different logic parsing dates from "1" to "12" (using them as months) but this is totally different story.
As an answer to how to avoid this issue, you can sort of standardize your value to date conversion like so:
// all of these will produce the same date
const stringDate = "1970-01-01T00:00:12.345Z";
const numberString = " 12345 ";
const number = 12345;
function toDate(value) {
if (typeof value === "string" && /^\d+$/.test(value.trim())) {
value = parseInt(value);
}
return new Date(value)
}
console.log("string date", toDate(stringDate));
console.log("number string", toDate(numberString));
console.log("number", toDate(number));
I am using 2.22.1 to format dates.
When i put in a date that comes from a date selector, moment will put the date back one day. For example, when I try to format a date in the following way:
Example 1:
const day = new Date(value).getDate();
const month = new Date(value).getMonth();
const fullYear = new Date(value).getFullYear();
console.log('day', day); // 7
console.log('month', month); // 5
console.log('fullYear', fullYear); //2018
Formatting function:
moment.utc(new Date(month + ' ' + day + ' ' + fullYear), 'MM-DD-YYYY').format('DD-MM-YY')
Output: 06-05-18
Expected: 07-05-18
Example 2:
Input: Thu May 31 2018 00:00:00 GMT+0100 (GMT Summer Time)
Formatting function:
moment.utc(new Date(value)).format('DD-MM-YY')
Output: 30-05-18
Expected: 31-05-18
moment.utc will convert the input to universal time.
You are running new Date(...) with an input that's based on GMT +1.
If you think about this, it makes total sense.
Your output is 30-05-18, because it's 11 PM / 23:00 o'clock on the previous day.
This (how ever) would in fact work:
moment('05-06-2018', 'MM-DD-YYYY').format('DD-MM-YY')
// alternatively (and preferrably) this:
moment.utc('05-06-2018', 'MM-DD-YYYY').format('DD-MM-YY')
and output: "06-05-18"
Because the non utc version does not take a time input in this example.
One of the reasons moment.js exists, is to get rid of Date in your code all together.
(Keep in mind tho, that Date has drastically improved now. DateTimeFormat is a game changer)
Please just read the momentjs documentation on how to properly use moment.
edit:
If you want to process 400000 dates with this, I'd advise using RegExp, .split, .exec, .slice or Date instead.
(I can relate since I wrote a client sided Log parser with javascript generators and Service Workers for a statistical anti-cheat analysis)
I truly recommend playing around with such things to raise your knowledge.
I just ran into this issue and a quick fix I found for the time being processed in "Zulu time" (due to the Z at the end of the string) is to add .LocaleString() after the date variable.
I find that for data consistency, it's easier to store data in UTC time and then convert it to the locale string when displaying the data to the user.
For example, I'm using:
moment.utc(dateVariable.toLocaleString()).format("MM/DD/YYYY")
I'm using gmail api to get messages from my mailbox.
But when using format='raw' in get method, I can't parse the returned data in 'raw' filed.
service.users().messages().get(userId='me', id=message_id, format='raw').execute()
This is in the document,
"raw": Returns the full email message data with body content in the raw field as a base64url encoded string; the payload field is not used.
and I tried
base64.urlsafe_b64decode(raw.encode('utf-8'))
base64.urlsafe_b64decode(raw.encode('ascii'))
base64.urlsafe_b64decode(raw.encode('cp932')) the message is in Japanese
base64.urlsafe_b64decode(raw)
None of the returned value is readable.
I got all ASCII words and numbers, but none of the Japanese words are readable:
Delivered-To
Received
X-Received
...
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: base64
And when it comes to Japanese (or message body?), the message turns to something like: '''6Ie855Sw5qeYDQoNCuOBhOOBpOOCguOBiuS4luipseOBq+OBquOBo+OBpuOBiuOCiuOBvuOBmeOA
gg0K44Oq44Ol44Km44Gn44GZ ...... PjwvZGl2PjwvZGl2PjwvZGl2PjwvZGl2PjwvZGl2PjwvZGl2Pg0KPC9kaXY+
PC9kaXY+PC9kaXY+DQo='''
Please help me out!!
I'm reading a file's modification date with fs.statSync() and I'd like to compare it with a String that comes from my database:
This is my file's last modification date:
Fri Mar 24 2017 13:22:01 GMT+0100 (Central Europe Standard Time)
And I'd like to compare with this string:
2016-07-18 12:28:12
How can I do this with Node.JS? Can I somehow create a new date from the String?
You can use Moment parse both of those formats and compare the results.
var date1 = moment(string1, format1, true);
var date2 = moment(string2, format2, true);
if (date1.diff(date2) === 0) {
// equal
} else {
// not equal
}
See this answer for parsing dates in different formats:
Check if a string is a date value
Make sure that you parse it with explicit format and you don't let it guess the format, or otherwise you will never be sure if it guessed correctly.
I have a form written in Pug. I am getting all the correct parameters out of the form's POST, except my checkbox array is a string containing one value - the latest day selected. Each checkbox has a name equaling an array, so the form should be returning all selected checkboxes. However, that is not happening.
Here is the relevant code in the form for the checkbox selection:
form#create-shift-form(action='/shifts/new', method='post')
div.form-group
p Select which days this shift should apply:
div.checkbox-inline
input.checkbox-input#create-shift-mon(type='checkbox', name='days[]', value='Mon')
label(for='create-shift-mon') Mon
div.checkbox-inline
input.checkbox-input#create-shift-tue(type='checkbox', name='days[]', value='Tue')
label(for='create-shift-tue') Tue
div.checkbox-inline
input.checkbox-input#create-shift-wed(type='checkbox', name='days[]', value='Wed')
label(for='create-shift-wed') Wed
div.checkbox-inline
input.checkbox-input#create-shift-thur(type='checkbox', name='days[]', value='Thur')
label(for='create-shift-thur') Thur
div.checkbox-inline
input.checkbox-input#create-shift-fri(type='checkbox', name='days[]', value='Fri')
label(for='create-shift-fri') Fri
div.checkbox-inline
input.checkbox-input#create-shift-sat(type='checkbox', name='days[]', value='Sat')
label(for='create-shift-sat') Sat
div.checkbox-inline
input.checkbox-input#create-shift-sun(type='checkbox', name='days[]', value='Sun')
label(for='create-shift-sun') Sun
Why aren't all the selected checkbox values being captured in the form's POST? Why is it only returning a string of the last selected value instead of all the selected values, which is the expected HTML behavior?
If it helps, my Node/Express server is set up to parse content with the lines:
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
According to this W3C Spec, I should just be able to give each checkbox input the same name and they will be parsed as an array of values upon POST (see example 2 in the spec). I have also tried this, which leads to the same behavior as my issue - only the last selected item is returned, and as a string, not an array.
UPDATE: My original setup of the code above is correct. Simply giving each attribute's name field the same name assembles the responses into an array. I had set up middleware that I had forgotten about to prevent array responses as a security measure. Removing that library, or whitelisting the route this form POSTs to, allows the original code to work without a problem.
After some research here is what i found:
This method of passing an array highly depends on the server, how it will understand the data payload, PHP understands this syntax but JSON objects have it quiet differently.
form#create-shift-form(action='/shifts/new', method='post')
div.form-group
p Select which days this shift should apply:
each val, index in ['Mon', 'Tue', 'Wed', 'Thur', 'Fri', 'Sat', 'Sun']
div.checkbox-inline
input.checkbox-input(type='checkbox', name=`days[${index}]`, value=val, id=`create-shift-${val}`)
label(for=`create-shift-${val}`)=val
This will payload will be equivalent to something like this:
days[0]=Mon
days[1]=Tue
days[2]=Wed
days[3]=Thur
days[4]=Fri
days[5]=Sat
days[6]=Sun
which is equivalent to this payload in json:
{
days: ['Mon', 'Tue', 'Wed', 'Thur', 'Fri', 'Sat', 'Sun']
}
Source: https://darobin.github.io/formic/specs/json/
Update:
For future reference:
OP figure the issue, the solution (days[${index}]) is correct, however express body parser extended:true uses querystring instead of qs.
With extended:false, the JSON works as expected to behave. The payload becomes "days[0]"="Mon", "day[1]"="Tues", etc.
app.use(bodyParser.urlencoded({ extended: false }); //instead of true