15

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:

two Google Spread Sheet

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 ?

1
  • A quick note: you are not really reading time values, but Duration values. Time values will go only up to 23:59:59, or 11:59 PM. I suggest changing the title of the question, as it is currently misleading (I myself am looking for that, and got here instead).CommentedAug 19, 2019 at 18:21

5 Answers 5

21

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.

5
  • Thanks for this nice little function, far more convenient than my code ;-) btw I did made a confusion between JS and SS reference in my attempt to explain, I corrected my explanation now hoping it's understandable to anybody...CommentedJul 18, 2013 at 15:41
  • one detail : I just noticed that for "special" time zones like 03:30 for example it doesn't return an integer... adding a Math.round() on the final returned values (minutes and hours) might be useful in this special case. After all it doesn't bother the other cases ;-)CommentedJul 18, 2013 at 22:21
  • Thanks Serge, I've adjusted the logic in such a way that it correctly handles timezone offsets that aren't whole hours.CommentedJul 22, 2013 at 14:21
  • 2
    According to code.google.com/p/google-apps-script-issues/issues/… the Issue 402 is fixed :)
    – Wicket
    CommentedDec 9, 2015 at 6:23
  • Maybe fixed within sheets, but certainly not for taking dates from sheets to docs.
    – Ralf
    CommentedDec 18, 2017 at 18:44
14

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

3
  • Using this for a Google Docs text document, I get "not defined"
    – Ralf
    CommentedDec 18, 2017 at 17:15
  • Question and answer is about sheets specifically.
    – Howdy
    CommentedDec 18, 2017 at 21:58
  • 3
    Thank you, yes, I need to specifically fetch times and dates from sheets into docs. Display values, not messed up by timezones or anthing else. I now worked around it by setting time zones in all related files and scripts to "GMT (no daylight saving)".
    – Ralf
    CommentedDec 19, 2017 at 23:03
11

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) } 

enter image description here

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.

4
  • Form where is this value coming: 2.2091616E12?
    – patrick
    CommentedJul 18, 2013 at 7:25
  • one small problem, I'm getting 19999.0 could it be that the value 2.2091616E12 depend on the timezone of the spreadsheet?
    – patrick
    CommentedJul 18, 2013 at 7:37
  • 1
    I added a line that you can use with a hour value = 0 to get the constant value that suits your TZ, it is indeed depending on your spreadsheet Time ZoneCommentedJul 18, 2013 at 7:45
  • Eric's answer is far more "elegant", I'd suggest you mark his answer as the best one.CommentedJul 18, 2013 at 15:38
5

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.

1
  • "GMT without daylight saving" - genius. Thank you so much, dude.
    – Ralf
    CommentedDec 18, 2017 at 18:52
0

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:

enter image description here

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.

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.