Convert UTC ISO value to `String` (Text) - javascript

Objective: I need to convert a UTC value (col A) to a String before passing it to a function for local time zone conversion.
Errors: no errors, script runs successfully
Issue: Time is not converted to specified time zone
Further info:
If I use the following in my LOCALTIME function, the date is converted correctly.
var originalDate = new Date("2020-05-12T23:22:46.120Z");
Function to get the time in UTC (col A):
function getAllEmailAndDiningPreferences(changeType){
var orders = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Orders');
var ordersData = orders.getDataRange().getValues();
var lr = getLastRow(orders, "A1:E");
for (var i=0; i<lr; i++){
if(ordersData[i][1].length == 0){
var orderId = ordersData[i][0]; //note: need to convert 'orderId' to a String
var email = getEmailFromOrderedId(orderId.toString()); //toString() doesn't convert it to a expected String
convertOrderDateToLocalTimeZone(i+1, orderId);
i+1;
}
}
}
Function to convert to locatime zone:
function LOCALTIME(datetimeString) {
if (datetimeString.map) {
return datetimeString.map(LOCALTIME);
} else {
var originalDate = new Date(datetimeString);
return Utilities.formatDate(originalDate, "Australia/Adelaide", "yyyy-MM-dd'T'HH:mm:ss'Z'")
}
}

Answer:
Due to Sheet locale, you need to get the display value of the cell before making a date object so that the Script project reads it correctly as UTC.
More Information:
getDisplayValues() will literally get the values of the cells as you see them in the UI. As a result, as the strings are inputted in UTC, you can bypass the conversion to your local time zone by calling for the displayed datetime rather than the stored one:
Code Example:
function dateConvert() {
var d = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange("A2").getDisplayValue();
var dateObj = new Date(d);
var newDate = Utilities.formatDate(dateObj, "Australia/Adelaide", "yyyy-MM-dd'T'HH:mm:ss'Z'")
Logger.log(newDate);
}
In this case, newDate will be converted to the Australia/Adelaide timezone.
There is also the .getDisplayValues() method, which works in the same way only over a range of values rather than just a single cell.
References:
Class Range - getDisplayValue() | Apps Script | Google Developers

Related

Adding "HH:MM" time format to Date Object Javascript properly

I have the requirement to set particular time of the day to Date Object. The Time is in String and is CET, so "16:00" means "15:00" in UTC in Winter time. The following code does the job in node.js on my local machine which is in CET Timezone:
addTimetoDate(new Date(),"16:00");
function addTimetoDate(theDate,theTime){
var dtDate = new Date(theDate)
try{
var strTime = theTime.replace(/ /g,'');
var hourArray = strTime.split(":");
dtDate.setHours(parseInt(hourArray[0]), parseInt(hourArray[1]), 0)
if (dtDate == "Invalid Date"){
dtDate = theDate;
}
} catch (e){
dtDate = theDate;
}
return dtDate
}
However when deployed to remote server it produces Date Object which is offset by one hour the other direction when displayed with toLocaleString it shows "17:00".
How to do it elegant way (as simple deduction of one hour will work only in Winter Time.
---EDIT---
To clarify the question is - Is there anything I can do prior to using .setHours to make it right. Or I should not use setHours but rather manipulate the string for Date Constructor, so 16.00 CET gets properly converted to UTC representation?
toLocaleString will convert the given time into the timezone defined by the locale,... and that's fine if the time is UTC to start with. But if it's already been offset, then it's going to offset it again, which is what you're seeing.
Time is a fiddly creature; when I'm working with time I always store it (eg in a database) as UTC and let the client descide how it gets displayed. That way I can guarantee no server-side silliness.
Ok Found the solution:
function addTimetoDate(theDate,theTime){
var d = new Date();
var hourLocal = d.toLocaleString({timezone:'Europe/Berlin'}).split(" ")[1].split(":")[0]
var hourISO = d.toISOString().split("T")[1].split(":")[0]
var diff = parseInt(hourLocal) - parseInt(hourISO);
var dtDate = new Date(theDate)
try{
var strTime = theTime.replace(/ /g,'');
var hourArray = strTime.split(":");
dtDate.setHours(parseInt(hourArray[0])-diff, parseInt(hourArray[1]), 0)
if (dtDate == "Invalid Date"){
dtDate = theDate;
}
} catch (e){
dtDate = theDate;
}
return dtDate
}
Diff in whatever situation - being on the server, or on the browser captures current status (including daylight saving change) of hourly difference for the given Timezone. Then you can adjust your given hour to UTC to use setHours.

Getting substrings of each Array-Element

I am trying to convert data stored in "dd.mm.yy" format to "w" format in Google Sheets/Appscript. Unfortunately I cannot use the ".formatDate()" function since sheets does not recognice the input values as date values, so I tried the following:
function CheckWeekNuber() {
var dateStr =SpreadsheetApp.getActiveSpreadsheet().getSheetByName("INSERT RAW DATA HERE").getRange(3,5,300,4).getValues();
var day = dateStr.substring(0,2);
var month = dateStr.substring(3,5);
var year = "20"+dateStr.substring(6,8);
var weekNumb = Utilities.formatDate(new Date(year, month-1, day), "GMT", "w");
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("WEEKNUMBERS").getRange(3,5,300,4).setValues(weekNumb);
}
It worked perfectly with a foor loop, converting each single value at a time. But it was super slow. My problem lays in using the ".substring()" formula for arrays, it does not seem to work..
Do you have any idea how to fix it?
The use of substring() is not possible when applying directly to an array. Looping the only way in getting the substring() of each element on an array.
Suggestion
If you are open to changing the data in your sheet, you can try using createTextFinder(".").replaceAllWithText("/") on your data range to convert your data to date format. Once you have this, you will still loop into your new data to get the week number of each date. Code is as follows and this is working on my end:
function CheckWeekNuber() {
var range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("INSERT RAW DATA HERE").getRange(3,5,300,4);
range.createTextFinder(".").replaceAllWith("/") // converts str date to date format
var dateFormat = range.getValues();
var lastRow = range.getLastRow();
var lastCol = range.getLastColumn();
var weekNumb = [];
for(var row in dateFormat){
for(var column in dateFormat[row]){
dateFormat[row][column] = Utilities.formatDate(new Date(dateFormat[row][column]), "GMT", "w");
}
}
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("WEEKNUMBERS").getRange(3,5,300,4).setValues(dateFormat);
}

Google sheets dates, stored as strings by toString(), are not recognized as dates when written back into a cell?

I have a sheet that adds several thousand values per day. To keep it small and nimble, it regularly stores rows of data as comma separated values on a separate sheet. I loop through each row using toString() on all the cells.
When retrieving and reprinting the needed data to a new sheet, the sheets do not recognize the strings as dates.
e.g. Fri Mar 29 2019 13:45:06 GMT-0700 (PDT)
My workaround is slicing the strings when retrieving them, I am just wondering why they don't recognize the standard date strings as dates.
function compressData() {
var sheet = SpreadsheetApp.getActiveSheet();
var arr = sheet.getRange(1,1,2,2).getValues();
var arrNew = [];
var arrRow =[];
var arrExpanded;
for (var i in arr) {
for (var x in arr[i]) {
arrRow.push(arr[x][i]);
}
arrNew.push([arrRow[i].toString()]);
}
// stores each row of the original array as a
// comma separated string in a new array
sheet.getRange(1, 3, arrNew.length, arrNew[0].length).setValues(arrNew);
// writes the new array to a new range
}
function expandData(){
// prints the arrays onto a new range and the new date
// cell isn't recognized as a date
var arrExpanded = [];
var sheet = SpreadsheetApp.getActiveSheet();
var arrCompressed = sheet.getRange(1,3,2,1).getValues();
for (var i in arrCompressed) {
var arrTemp = arrCompressed.toString().split(',');
arrExpanded.push(arrTemp);
}
// rebuilds the 2d array
sheet.getRange(1, 4, arrExpanded.length, arrExpanded[0].length).setValues(arrExpanded);
}
You extract date values from a string, and are wondering why Google Sheets doesn't recognize the standard date strings as dates.
The reason is that the date that you see on-screen is actually a number, milliseconds from the Unix epoch. It is merely formatted to display so that it "looks like" a date consisting of text.
To convert a date string to a value that Google Sheets will recognise as a date, you need to convert it. The following code provides a simple example:
In the screenshot below, the value in Cell B1 is a string; the value is cell C1 is a date value. Note the actual data value (number) displayed in cell C5 (this cell is formatted as 'Number');
function so5842277201() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "Sheet1";
var sheet = ss.getSheetByName(sheetname);
var datestring = sheet.getRange("A1").getValue();
Logger.log(datestring);
var date01 = new Date(datestring);
Logger.log(date01);
var date02 = Utilities.formatDate(date01,"GMT-7", "EEE MMM d yyyy HH:mm:ss");
var newdaterange = sheet.getRange("C1");
newdaterange.setValue(date02);
}
Screenshot

DateTime Saved loses time portion

I have a Datetime String Saved to a cookie as below
DateTime date = DateTime.Now;
LoginDate = date.ToString("u", DateTimeFormatInfo.InvariantInfo).Replace("Z", "");
int sessionTimeout = 1;
DateTime dateExpress = date.AddMinutes(sessionTimeout);
ExpressDate = dateExpress.ToString("u", DateTimeFormatInfo.InvariantInfo).Replace("Z", "");
HttpCookie cookie = HttpContext.Current.Request.Cookies["express"];
if (cookie == null)
{
cookie = new HttpCookie("express");
}
cookie.HttpOnly = false;
cookie.Path = "/";
cookie.Values["express"] = ExpressDate;
Response.Cookies.Add(cookie);
This work as expected as I can see in the Application tab that cookie has saved correctly
Please refer the image below
But when I access it from the client side it only returns the date bit losing the time portion.
var current = getCookie("express");
var date = current.split(" ")[0];
alert(date);
What am i doing wrong here?
Since your express contains express=2017-01-16 09:07:49 and when we split it apart by space you will get two string in which first will be date and another next would be time. Thus you have to do something like this to get the date and time separated.
var current = getCookie("express");
var date = current.split(' ')[0];
var intime = current.split(' ')[1];
alert(date);
alert(intime);
Whereas the current.split(" ")[0]; will give you expression like express=2017-01-16 and thus I believe you should break this apart using = again to get the date only.
or else if you are looking for date and time in one variable you can do something like this
var current = getCookie("express");
var smDateTime = current.split('=')[1];
alert(smDateTime);
your cookie is in the format "yyyy-mm-dd hh:mm:ss"
split that string on a space ... .split(" ") ... you get an array
["yyyy-mm-dd", "hh:mm:ss"]
element [0] is "yyyy-mm-dd"
solution
var date = getCookie("express");
alert(date);

How to subtract two different dates from a date/time stamp?

I need to subtract a date like 1/26/2015 from a date-time like 2016-01-27T01:10:57.569000+00:00. From what I've read converting both to distance in milliseconds from Epoch and then subtracting is the easiest way. I've tried using various methods, but all the methods seem to say 2016-01-27T01:10:57.569000+00:00 is invalid data. The method .getTime() works great for the 1/26/2015 format, but it can't read the 2016-01-27T01:10:57.569000+00:00.
How does one go about getting the date/time UTC time into milliseconds?
On a complicated way you can use a regex to extract each part of the date as string and then use them in a new Date with all parameters:
function getTimeDifference(){
var regEx = /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):([\d.]+)/;
var dateString = '2016-01-27T01:10:57.569000+00:00';
var r = regEx.exec( dateString );
var date1 = new Date(r[1], r[2]-1, r[3], r[4], r[5], r[6]); // Notice the -1 in the month
var date2 = new Date('1/26/2015');
var difference = date1 - date2;
Logger.log(difference);
}
I ended up using this. When I call parseDate(), I used getTime() to get the date in milliseconds then subtracted them and converted them to days. For my use case the time didn't have to be down to the second, but if it did, it wouldn't be hard to parse more info from the string. I ran into trouble initially because as a beginner Javascript writer I didn't know why apps script wouldn't accept this format into the date constructor.
function parseDate(str) {
//This should accept 'YYYY-MM-DD' OR '2016-01-27T01:10:57.569000+00:00'
if(str.length == 10){
var mdy = str.split('-');
return new Date(mdy[0], mdy[1]-1, mdy[2]);
}
else
{
var mdy = str.split('-');
var time = mdy[2].split('T');
var hms = time[1].split(':');
return new Date(mdy[0], mdy[1]-1, time[0], hms[0], hms [1]);
}
}
If you are confident that the values in the date strings will always be valid and that the ISO8601 string will always have offset 00:00 (i.e. UTC), then simple parse functions are:
// Parse ISO 8601 format 2016-01-27T01:10:57.569000+00:00
function parseISOUTC(s) {
var b = s.split(/\D/);
return new Date(Date.UTC(b[0],b[1]-1,b[2],b[3],b[4],b[5],b[6]));
}
document.write(parseISOUTC('2016-02-04T00:00:00.000+00:00'));
// Parse US format m/d/y
function parseMDY(s) {
var b = s.split(/\D/);
return new Date(b[2],b[0]-1,b[1]);
}
document.write('<br>'+ parseMDY('2/4/2016'))
document.write('<br>'+ (parseISOUTC('2016-02-04T00:00:00.000+00:00') - parseMDY('2/4/2016')))
Note that the first string is UTC and the second will be treated as local (per ECMAScript 2015), so the difference between 2016-02-04T00:00:00.000+00:00 and 2/4/2016 will be the time zone offset of the host system.

Categories