setting the time delta format in google sheet - google-apps-script

I have a google sheet form response data where column B is a time duration value. But it shows time in AM or PM. I tried different app script but still unable to get the value a time Delta( difference) format
The form response data itself gives time in AM or PM. Help me in this matter.
This is my latest code.
function timeFormat() {
var cell = SpreadsheetApp.getActive().getSheetByName('Sheet4').getRange('B1:B');
cell.setNumberFormat('hh:mm');
}

Is your issue basically with the AM / PM part popping up when you click into the cells formatted as 'hh:mm'? You can fix that by applying the duration format for hours to the entire range (e.g. B2:B)
GAS:
range.setNumberFormat('[hh]:mm');
Or manually under Format -> Number -> More Formats -> Custom number format (a lot easier)
And here's how the end result looks like. Note that you get the 'AM/PM' part popping up when you click into cells formatted as 'hh:mm' but not with the ones formatted as [hh]:mm:

You could use this function as a cell function and then format the cells as a duration and you will get hours:minutes:seconds.
function days(Start,End) {
if(Start && End)
{
var second=1000;
var minute=60*second;
var hour=minute*60;
var day=hour*24;
var t1=new Date(Start).valueOf();
var t2=new Date(End).valueOf();
var d=t2-t1;
return d/day;
}
else
{
return 'Invalid Inputs';
}
}
This essentially returns the date number in the form that the spreadsheet understands as days and fraction of days.

Related

Convert a string to a date format using a script or a Google Sheets built-in formula

DESCRIPTION:
I want to convert a DD/MM/YYYY HH:mm or 25/01/2022 11:00 string, in an accepted date format.
Doesn't matter which one, it just has to be recognized by Apps Script and Google Sheets and be able to work with it.
If you can provide an Apps Script's code (not a formula in Google Sheets like I attempted to do) that converts the string into a date and then set the values in another range, to work with them as dates, I would be grateful, thanks.
If it's a Google Sheet formula no problem, as long as it works.
TRIED:
After many attempts, I tried to build a custom formula putting pieces together around the web but it doesn't function
//formula is translated from italian
=ARRAYFORMULA(IF(F10:F="",,TEXT(DATE(
IF.ERROR(REGEXEXTRACT(F10:F, "/(\d+) "), YEAR(F10:F))*1,
IF.ERROR(REGEXEXTRACT(F10:F, "/(\d+)"), MONTH(F10:F))*1,
IF.ERROR(REGEXEXTRACT(F10:F, "\d+"), DAY(F10:F))*1)+
IF.ERROR(TIME.VALUE(F10:F), REGEXEXTRACT(F10:F, "\d+:\d+")+
IF(REGEXMATCH(F10:F, "PM"), 0.5, 0)), "yyyy-mm-dd hh:mm")))
It gives a #VALUE error, which says "'11:00' is a string and can't be recognized as a date" (11:00 is an example).
I've also got the Regular Expression, but I don't know if it's correct and how to use it in code:
/([\d])\w+\/([\d])\w+\/([\d])\w+\s([\d])\w+\:([\d])\w+/g
I also tried changing the time zone but it didn't work.
Keep in mind I'm using the Italian time zone, if it's possible I'd rather keep it as it is.
Table example (like I said, what's important is that dates are accepted as dates):
F: Column source strings
Q: Column desired dates recognizable as dates by Sheets
(Q because it's the real column where I want to put the formula)
F
..
Q
16/02/2023 16:00
16/02/2023 16:00:00
25/11/2022 15:00
25/11/2022 15:00:00
For #Cooper and the solution based on the script.
I've customized the script, but it doesn't recognize the split function anymore (copy and paste of your function logs what it expects in Apps Script), and doesn't get any results in overwriting the existing string dates.
let dateStringed; //source wrong dates
var i = 0;
var flatArray;
function expired() {
//bLast is the range Last Row
dateStringed = gen.getRange(10, 6, bLast, 1).getValues();
flatArray = [].concat.apply([], dateStringed);
while (i <= bLast) {
i++;
convert();
};
Logger.log(flatArray);
gen.getRange(10, 6, bLast, 1).setValues(flatArray);
};
function convert(s=flatArray[i]) { //instead of "25/01/2022 11:00"
let [d,m,y,hr,mn] = s.split(/[\/ :]/)
Logger.log('y: %s m: %s d: %s hr: %s mn: %s',y,m,d,hr,mn);
Logger.log(new Date(y,m - 1,d,hr,mn).toLocaleString());
//don't know if it's correct, but it logs the dates
//in an easier syntax
};
For #doubleunary solution:
Demo SHEET ITA
In the sheet I copied and pasted the first column of my private original sheet, the F column with the text dates, and the Q10 cell I've pasted the formula as it is
I made sure to set local to Italy but to display english name formulas.
I don't know why, here it colors green and it doesn't give me a result.
But I did a test, and set the sheet tu US time and it functions. Any idea on how to make it function in Italian version?
Demo SHEET US
Solved: I used this script
function dateCorrected(){
gen.getRange('N10:N').clearContent();
//get the formula from another code sheet:
//'=arrayformula( SE.ERRORE( 1 / VALORE(
//regexreplace( to_text(F10:F);
//"(\d+)/(\d+)/(\d+) (\d+):(\d+)"; "$3-$2-$1 $4.$5" ) ) ^ -1 ) )'
var dateCorr = codeSheet.getRange('T1').getFormula();
Logger.log(dateCorr);
gen.getRange('N10').setFormula(dateCorr);
gen.getFilter().sort(14, false);
gen.getRange('N10:N').clearContent();
gen.getRange('N10').setFormula(dateCorr);
}
And this gives me the possibility to delete rows that meet a certain date condition. Thank you all for the support.
It is usually easiest to do the text string to datetime conversion using a spreadsheet formula. You can convert text strings like 25/01/2022 11:00 to dates with this formula in cell G10:
=arrayformula( iferror( 1 / value( regexreplace( to_text(F10:F); "(\d+)/(\d+)/(\d+) (\d+):(\d+)"; "$3-$2-$1 $4.$5" ) ) ^ -1 ) )
Format the result column as Format > Number > Date time.
In the event you need to "fix" those datetime values in place, you can replace the formula results with static values with Control+C to copy and Control+Shift+V to paste values only, or do the same with a simple range.setValues(range.getValues()) script bit.
In the event you need to pass those datetime values to Apps Script, it is usually easiest to get them as Date objects rather than text strings. The Date objects will refer to the same moment in time (in UTC) as the date times in the spreadsheet (in the spreadsheet's time zone).
You should note that Apps Script is JavaScript which means that Date objects are always in the UTC timezone. If you log them or output them in some other way, they will not be shown in the Italian timezone as you expect.
There are two easy ways to present such dates in a human-readable format in the spreadsheet's timezone. The first is to directly get the data as a text string in the format that it is shown in the spreadsheet:
function test1() {
const ss = SpreadsheetApp.getActive();
const dateStrings = ss.getRange('Sheet1!G10:G')
.getDisplayValues()
.flat()
.filter(String);
console.log(dateStrings);
}
The second is to get the data as Date objects and convert them to text strings using the spreadsheet's timezone, like this:
function test2() {
const ss = SpreadsheetApp.getActive();
const timezone = ss.getSpreadsheetTimeZone();
const dates = ss.getRange('Sheet1!G10:G')
.getValues()
.flat()
.filter(String)
.map(date =>
Object.prototype.toString.call(date) === '[object Date]'
? Utilities.formatDate(date, timezone, 'dd/MM/yyyy HH:mm')
: date
);
console.log(dates);
}
Convert String to Date:
function convert(s="25/01/2022 11:00") {
let [d,m,y,hr,mn] = s.split(/[\/ :]/)
Logger.log('y: %s m: %s d: %s hr: %s mn: %s',y,m,d,hr,mn);
Logger.log(new Date(y,m - 1,d,hr,mn));
}
Execution log
10:58:11 AM Notice Execution started
10:58:12 AM Info y: 2022 m: 01 d: 25 hr: 11 mn: 00
10:58:12 AM Info Tue Jan 25 11:00:00 GMT-07:00 2022
10:58:13 AM Notice Execution completed
To convert a string to a Date object in Google Apps Script use Utilities.parseDate.
Example:
function myFunction(){
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const timeZone = spreadsheet.getSpreadsheetTimeZone();
const date = Utilities.parseDate('25/01/2022 11:00',timeZone, 'dd/MM/yyyy HH:mm');
return date;
}
Using the above as a custom function might not make sense for some use cases since the same result might be achieved by using built-in functions which are more efficient and less prone to have problems.
The options for using built-in functions depends on the spreadsheet settings, i.e. DATEVALUE might return different results for ambiguos dates like 25/01/2022 as for certain regions the month goes first and for others the day of the month goes first.
=DATEVALUE("25/01/2022") works correctly when the spreasheet region is set to Italy. You might have to manually set the cell formatting to date in order to make it show a date instead of the time serialized value (a number).
To convert 25/01/2022 11:00 using formulas in the above spreadsheet, use
=INDEX(SPLIT("25/01/2022 11:00";" ");1) + SUBSTITUTE(INDEX(SPLIT("25/01/2022 11:00";" ");2);":";".")
The above formula has two main parts joined by using +. The first part returns the time serialized value corresponding to the date, the second part returns the time serialized value corresponding to the time.
Array formula
=ArrayFormula(DATEVALUE(REGEXEXTRACT(F10:F;"^([ˆ\d/]+) "))+TIMEVALUE(SUBSTITUTE(REGEXEXTRACT(F10:F;" ([ˆ\d/:]+)$");":";".")))
The same concept as the previous formula, but instead of INDEX it uses REGEXEXTRACT.
Google has a Utility to do just that!
let dateTime = '2022-12-16 13:00:00';
let timeZone = 'GMT';
let convertedDateTime = Utilities.formatDate(dateTime, timeZone, 'dd/MM/yyyy HH:ss')
check out Class Utilities for more info.

Google Script Forcing Date Format [duplicate]

I'm trying to get from a time formatted Cell (hh:mm:ss) the hour value, the values can be bigger 24:00:00 for example 20000:00:00 should give 20000:
Table:
if your read the Value of E1:
var total = sheet.getRange("E1").getValue();
Logger.log(total);
The result is:
Sat Apr 12 07:09:21 GMT+00:09 1902
Now I've tried to convert it to a Date object and get the Unix time stamp of it:
var date = new Date(total);
var milsec = date.getTime();
Logger.log(Utilities.formatString("%11.6f",milsec));
var hours = milsec / 1000 / 60 / 60;
Logger.log(hours)
1374127872020.000000
381702.1866722222
The question is how to get the correct value of 20000 ?
Expanding on what Serge did, I wrote some functions that should be a bit easier to read and take into account timezone differences between the spreadsheet and the script.
function getValueAsSeconds(range) {
var value = range.getValue();
// Get the date value in the spreadsheet's timezone.
var spreadsheetTimezone = range.getSheet().getParent().getSpreadsheetTimeZone();
var dateString = Utilities.formatDate(value, spreadsheetTimezone,
'EEE, d MMM yyyy HH:mm:ss');
var date = new Date(dateString);
// Initialize the date of the epoch.
var epoch = new Date('Dec 30, 1899 00:00:00');
// Calculate the number of milliseconds between the epoch and the value.
var diff = date.getTime() - epoch.getTime();
// Convert the milliseconds to seconds and return.
return Math.round(diff / 1000);
}
function getValueAsMinutes(range) {
return getValueAsSeconds(range) / 60;
}
function getValueAsHours(range) {
return getValueAsMinutes(range) / 60;
}
You can use these functions like so:
var range = SpreadsheetApp.getActiveSheet().getRange('A1');
Logger.log(getValueAsHours(range));
Needless to say, this is a lot of work to get the number of hours from a range. Please star Issue 402 which is a feature request to have the ability to get the literal string value from a cell.
There are two new functions getDisplayValue() and getDisplayValues() that returns the datetime or anything exactly the way it looks to you on a Spreadsheet. Check out the documentation here
The value you see (Sat Apr 12 07:09:21 GMT+00:09 1902) is the equivalent date in Javascript standard time that is 20000 hours later than ref date.
you should simply remove the spreadsheet reference value from your result to get what you want.
This code does the trick :
function getHours(){
var sh = SpreadsheetApp.getActiveSpreadsheet();
var cellValue = sh.getRange('E1').getValue();
var eqDate = new Date(cellValue);// this is the date object corresponding to your cell value in JS standard
Logger.log('Cell Date in JS format '+eqDate)
Logger.log('ref date in JS '+new Date(0,0,0,0,0,0));
var testOnZero = eqDate.getTime();Logger.log('Use this with a cell value = 0 to check the value to use in the next line of code '+testOnZero);
var hours = (eqDate.getTime()+ 2.2091616E12 )/3600000 ; // getTime retrieves the value in milliseconds, 2.2091616E12 is the difference between javascript ref and spreadsheet ref.
Logger.log('Value in hours with offset correction : '+hours); // show result in hours (obtained by dividing by 3600000)
}
note : this code gets only hours , if your going to have minutes and/or seconds then it should be developped to handle that too... let us know if you need it.
EDIT : a word of explanation...
Spreadsheets use a reference date of 12/30/1899 while Javascript is using 01/01/1970, that means there is a difference of 25568 days between both references. All this assuming we use the same time zone in both systems. When we convert a date value in a spreadsheet to a javascript date object the GAS engine automatically adds the difference to keep consistency between dates.
In this case we don't want to know the real date of something but rather an absolute hours value, ie a "duration", so we need to remove the 25568 day offset. This is done using the getTime() method that returns milliseconds counted from the JS reference date, the only thing we have to know is the value in milliseconds of the spreadsheet reference date and substract this value from the actual date object. Then a bit of maths to get hours instead of milliseconds and we're done.
I know this seems a bit complicated and I'm not sure my attempt to explain will really clarify the question but it's always worth trying isn't it ?
Anyway the result is what we needed as long as (as stated in the comments) one adjust the offset value according to the time zone settings of the spreadsheet. It would of course be possible to let the script handle that automatically but it would have make the script more complex, not sure it's really necessary.
For simple spreadsheets you may be able to change your spreadsheet timezone to GMT without daylight saving and use this short conversion function:
function durationToSeconds(value) {
var timezoneName = SpreadsheetApp.getActive().getSpreadsheetTimeZone();
if (timezoneName != "Etc/GMT") {
throw new Error("Timezone must be GMT to handle time durations, found " + timezoneName);
}
return (Number(value) + 2209161600000) / 1000;
}
Eric Koleda's answer is in many ways more general. I wrote this while trying to understand how it handles the corner cases with the spreadsheet timezone, browser timezone and the timezone changes in 1900 in Alaska and Stockholm.
Make a cell somewhere with a duration value of "00:00:00". This cell will be used as a reference. Could be a hidden cell, or a cell in a different sheet with config values. E.g. as below:
then write a function with two parameters - 1) value you want to process, and 2) reference value of "00:00:00". E.g.:
function gethours(val, ref) {
let dv = new Date(val)
let dr = new Date(ref)
return (dv.getTime() - dr.getTime())/(1000*60*60)
}
Since whatever Sheets are doing with the Duration type is exactly the same for both, we can now convert them to Dates and subtract, which gives correct value. In the code example above I used .getTime() which gives number of milliseconds since Jan 1, 1970, ... .
If we tried to compute what is exactly happening to the value, and make corrections, code gets too complicated.
One caveat: if the number of hours is very large say 200,000:00:00 there is substantial fractional value showing up since days/years are not exactly 24hrs/365days (? speculating here). Specifically, 200000:00:00 gives 200,000.16 as a result.

Why do I get Wrong timeZone conversion in google script using this code?

It's hours I'm struggling to know why I don't get correct timeZone using this script !
I've used moment.js and moment-timezone and followed instruction explained here:
Timezone conversion in a Google spreadsheet
google-spreadsheet/40324587
I am trying to convert timezones usign following script:
var DT_FORMAT = 'YYYY-MM-DD HH:mm:ss';
function fromUtc(dateTime, timeZone) {
var from = m.moment.utc(dateTime,
DT_FORMAT);//https://momentjs.com/timezone/docs/#/using-
timezones/parsing-in-zone/
return from.tz(timeZone).format(DT_FORMAT);
}
function toUtc(dateTime, timeZone) {
var from = m.moment.tz(dateTime, DT_FORMAT,
timeZone);//https://momentjs.com/timezone/docs/#/using-timezones/parsing-
in-zone/
return from.utc().format(DT_FORMAT);
}
function myFunction(datetimeString,timeZone,format) {
var moment = new Date(datetimeString);
return Utilities.formatDate(moment, timeZone, format)
}
In my spreadsheet, the first column is date and time in standard GMT and I'm going to change date and time to different TimeZones which I wrote in next columns. Even-though I'm getting the date and time, but it is incorrect and I don't know how to fix it.
Here is the link to Google sheet:
https://docs.google.com/spreadsheets/d/1Tc0G2daX9FYIL354iyOffd06BtNPx5v-40KE5RtwDB4/edit?usp=sharing
and here is the link to my script app:
https://script.google.com/d/1u54McW1HBm-A2alno1DWWTFwk-vdok8ljwKfI_5htAHK-XrMt554YGLn/edit?usp=sharing
Update (replying to comment) :
I use the function like this, I write in in a cell in spreadsheet:
=fromUtc(A2, D1)

Time Arithmetic Calculation Error

I have the following code on a gsheet -
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var task = ss.getRange(1,2).getValue(); //ss.getRange("A2").getValue();
var date = new Date();
function onEdit(e) {
if (e.range.getA1Notation() == 'B2') {
if (/^\w+$/.test(e.value)) {
// console.log(e.value);
eval(e.value)();
e.range.clear();
}
}
}
function Start() {
var last = ss.getLastRow();
ss.getRange(last+1, 1).setValue(task);
ss.getRange(last+1, 2).setValue(date);
}
function End() {
var last = ss.getLastRow();
ss.getRange(last, 3).setValue(date);
var endTime = ss.getRange(last, 3).getValue();
var startTime = ss.getRange(last, 2).getValue();
ss.getRange(last, 4).setValue(endTime-startTime);
}
Whenever the cell in B2 is edited, it runs one of the validated names of the functions - either Start or End.
If 'start', it puts the value of cell b1 (validated list of task names) in first column, and current time in MM/DD/YYYY HH:MM:SS format.
If 'end', its the current time in MM/DD/YYYY HH:MM:SS format in the third column, and attempts to put the difference between the "start" time and "end" time in the fourth column, or a calculation.
Here's the error (or my lack of understanding how gsheets works)
The code produces the following:
Task| Start | End | Duration
DOS | 8/2/2017 16:44:28 | 8/2/2017 16:44:31 | 2,418.00
Question - what is the 2,418? The total duration should be 2 seconds, or 00:00:02. Given the above code, is it a code issue or a format of the cell issue?
when I put in the cell d2 = c2-b2, it works fine as long as column is formatted as a duration. But I'd rather not do it, as the inserting or start/end times is dependent upon the last row - so if I copy/paste the formula down to the bottom on the gsheet the data will not be continuous.
JavaScript timestamps are in milliseconds. Divide by 1000 to get seconds.
Also, using eval like you do seems a pretty bad idea. I would use a switch statement there, and display an error for the user if the entered function is not one of options. As is, your code silently fails if they enter "stat" instead of "start".

Google Scripts: Time formatted cells cause Error: Overflow

I am trying to write a custom function that takes two cell ranges (Start time and Stop time) as input and then outputs the total hours. If the cells are formatted as normal numbers (i.e. 9:00pm is represented as 0.875) the function works fine. If I format the cells as time the cell value the formula is in says "#NUM!" and the tooltip is "error: Overflow". Is there a way to read the raw cell data without the formatting so that my formula will work?
Formula code
function getHoursTest(startRange, stopRange) {
var hours = 0; // define "hours" with start value of 0
var i = startRange.length - 1; // define "i" and assign the position of the last array element
while(i>=0){
hours = hours + ((stopRange[i] - startRange[i])*24);
i--
}
return hours;
}
When a cell is formatted as a date or time value then it's value is passed to Apps Script as a JavaScript Date object. You can determine if the value is a date by using code like:
if (value instanceof Date) {
...
} else {
...
}