In my backend (written in Perl), I generate an end date. In the front end, I want to compute the remaining time before this end date. To avoid time zones problems, I want to use UTC
In perl, the end date is encapsulated in a data object:
data => {endTime => $date->epoch()}
And this is the Java Script function that I've written:
updateCounter: function (element){
var now = new Date().getTime();
var endTime = $(element).data("endtime");
var diff = epochEndDealTime - milliseconds;
console (diff, now, endTime);
...
}
I know for a fact that endTime is in the future, so diff should be positive, but when I test it is smaller, e.g.:
-1530397503497 1531929432906 1531929409
What am I doing wrong?
If we organize the numbers differently
now: 1531929432906
endTime: 1531929409
you might notice now is roughly 1000x larger than endTime. That's because JS's new Date().getTime() returns the number of milliseconds since 1970-01-01T00:00:00Z, while Perl's time returns the number of seconds since 1970-01-01T00:00:00Z.
So, simply multiply endTime by 1000, or divide now by 1000.
(You could also use different means of obtaining the time in Perl to obtain more precision, but that doesn't appear to be needed.)
Related
I'm quite new (and confused) with time in JavaScript..
I currently have time data to work with, and they are in the format of DD-MMM-YYYY, meaning it would be 23-Feb-2021. This time is already in its own timezone, GMT-10. I'm trying to initialize it as GMT-10 so that I could get its appropriate epoch time.
I've done this:
date = new Date("23-Feb-2021") // This results in 2021-02-23T00:00:00.000Z
But what I'm trying to achieve is to get the time to be 2021-02-23T10:00:00.000Z, which I could then do a getTime() to get its epoch in ms. I understand I could probably hard code to +10 to the time I have, but the data I work with might vary so I'd like to figure a way to initialize the date with a specific timezone.
EDIT:
Here's an example of an outcome I'd want:
date = ("23-Feb-2021")
date = moment(date).format(); // 2021-02-23T00:00:00+00:00
date = date.replace("+00","+10");
date = new Date(msg.date); // 2021-02-22T14:00:00.000Z
date = date.getTime(); // 1614002400000 (2021-02-22T14:00:00.000Z)
In the end, 2021-02-22T14:00:00.000Z is what I'm trying to get, without having to iterate it a bunch of times like above and adding +10
You can add the timezone offset to your input string, and use an explicit string format to parse it:
let date = "23-Feb-2021"
date = moment(date + "-10:00", "D-MMM-YYYYZ")
console.log(date.format()) // 2021-02-23T11:00:00+01:00 (if local is GMT+1)
console.log(date.utc().format()) // 2021-02-23T10:00:00+00:00
It seems you're already using moment.js, so add moment–timezone so you can parse timestamps in whatever IANA timezone you want. You can either choose a location with the offset rules you want (e.g. Pacific/Honolulu or Pacific/Tahiti for -10) or just a fixed offset like etc/GMT+10.
You can then format the value in any timezone, as UTC, or as a time value, e.g.
// Timestamp
let d = "23-Feb-2021";
// Parse in specific IANA timezone
let m = moment.tz(d, 'D-MMM-YYYY', 'Pacific/Honolulu');
// Trigger UTC mode
m.utc()
// Show result
console.log(m.format())
// Get time value (ms since epoch)
console.log(m.valueOf());
// Parse using generic timezone/fixed offset
let g = moment.tz(d, 'D-MMM-YYYY', 'etc/GMT+10');
console.log(g.utc().format());
// Display timestamp for another timezone
console.log(moment.tz(g, 'Asia/Riyadh').format());
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.34/moment-timezone-with-data-10-year-range.js"></script>
Note that for fixed offset timezones like etc/GMT+10, the sign is the opposite of the common offset (e.g. etc/GMT+10 is UTC-10) to be consistent with POSIX notation. However, moment.tz only has limited POSIX support in that it only recognises one hour offsets, not the full POSIX timezone notation, so you can't do say "etc/GMT-530" instead of Asia/Kolkata.
I have future date and now date. Both of this dates are always in same day but with just different hours. My goal is to get the difference of seconds between the future date and now date as my countdown value for a timer. The problem is when I calculate I'm getting inaccurate results.
In my research formula of converting milliseconds to seconds is millis / 1000.0 but non of this returns accurate countdown result;
My code
let now = (new Date().getTime() / 1000.0);
let futureDate = (new Date('2022-04-01T17:41:47.000Z').getTime() / 1000.0);
let difference;
difference = (futureDate - now); // not accurate
difference = parseInt(difference, 10); // not accurate
I would like the solution to work normal on all timezones and to inherit local system timezone instead of future date timezone.
Any help will be appreciated so much.
You should add the system timezone, like this:
let date = new Date('2022-04-01T17:41:47.000Z');
let now = new Date().getTime() / 1000.0;
let futureDate = (date.getTime() + date.getTimezoneOffset() * 60000) / 1000.0;
let difference;
difference = (futureDate - now); // not accurate
difference = parseInt(difference, 10); // not accurate
console.log(difference);
I want to check if you know that date formats like "0000-00-00T00:00:00.000Z" are always recognized as universal time (UK time).
Try using +HH:MM instead of the last Z character.
For example, if you are in the US East, it would be "2022-04-01T17:41:47.000-05:00".
The timestamp '2022-04-01T17:41:47.000Z' will be parsed as offset +0 (aka UTC or GMT) due to the trailing "Z", denoting a +0 offset. The difference between now and then in milliseconds is:
let diff = new Date('2022-04-01T17:41:47.000Z') - Date.now();
where a negative value for diff means the timestamp is in the past. To convert the difference to seconds, divide the result by 1,000.
If run at exactly the same time, the value for diff will be the same regardless of system settings for local offset (allowing for clock accuracy of course).
For the timestamp to be parsed as local, remove the Z:
let diff = new Date('2022-04-01T17:41:47.000') - Date.now();
However, that shifts the instant in time represented by the timestamp by the local offset, so will return a different value for diff for each system with a different offset. That doesn't seem like a sensible thing to do given the timestamp has an offset and so is intended to represent a single instant in time, not one of many different instants (as many as there are different offsets, perhaps hundreds if historical offsets are included) depending on the host offset.
I've a list of timespan(object list actually), like 2:00, 15:00, 18:00 etc, it is in utc.
Now i want to convert this time slot back to CST and then sort it, as i want my time sorted in cst.
For timezone conversion i needed temporary date. so i choose current utc date by
moment.utc(mytimespan). and performed the timezone conversion by .tz("CST").
So list is converted to 20:00,9:00, 12:00
Here please note that i got 20:00 in first place instead of last place in the list.
This is due to date part of moment which went in back date.
All here i want is my timespan in sorted form without any effect of date.
please me to find a way to do it without string conversion!
Thanks
Update
my currently working code using string conversion
TimeSpanDetails.sort(function compare(a, b) {
return moment(moment.utc(a.startTime).tz("CST").format("HH:mm"),"HH:mm").isAfter(moment(moment.utc(b.startTime).tz("CST").format("HH:mm"),"HH:mm")) ? 1 : -1;
});
Now i want to do it without string conversion using format
A few things:
A "time span" usually refers to a duration of time, not a time-of-day. These are two very different concepts that are sometimes confused. Consider:
A timespan of 99 hours is perfectly valid, but "99:00" is nonsensical as a time-of-day.
Due to daylight saving time and other time zone transitions, a timespan can't necessarily be thought of as "time since midnight" because midnight may or may not exist, or some other hour of the day may be absent or repeated.
Time spans can be negative in some programing languages, usually representing a period before a given point in time.
The tz function in Moment.js takes IANA time zone names. You should not use CT or CST, but rather America/Chicago, for example. However, time zones are completely unrelated to time spans, so you should not be applying them at all. You do not need moment-timezone.
Moment represents time spans in Duration objects. You can parse them from strings like so:
var d = moment.duration('99:00');
Duration objects convert numerically to milliseconds, so they are comparable like so:
var a = moment.duration('00:00');
var b = moment.duration('01:00');
var c = a < b; //=> true
Moment does not have a strongly typed object for a time-of-day, but you can use Moment in UTC mode so that it does not have DST transitions, and then just let it use the current day. HOWEVER:
This would assume that all time-of-day values you have should be evaluated on the same date.
This may or may not be the case.
Consider that if all you have is time-of-day and don't know what dates they're from, then the values ['23:00', '00:00'] may be sorted already and only one hour apart, or perhaps they're out of sequence and they are 23 hours apart.
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.
I have a data set with most of the data points in H:M:S format, such as 20:59:59, showing the time span used by individual athletes at different laps.
With D3.js, what is the best way to convert it to time spans, i.e. number of seconds?
I tried parser = d3.timeParse("%H:%M:%S"); but it seems to be very wrong..
If you are using D3 3.x, then you can construct a suitable parser with:
var parser=d3.time.format("%H:%M:%S");
or if you were using D3 4.x:
var parser=d3.timeParse("%H:%M:%S");
Either way, you would then be able to convert your data points into JS Date objects like this:
var t1 = parser.parse("20:59:59");
var t2 = parser.parse("21:02:13");
You can find out the elapsed time between these two time instances by subtracting, eg:
var elapsed = t2 - t1; /* Returns 134000 = 134 seconds */
elapsed will be the number of elapsed milliseconds.
The reason that works is that Date objects provide a valueOf() method which returns the number of milliseconds elapsed since Jan 1 1970. Subtracting the date objects automatically calls valueOf() and subtracts the returned values.
Note that if the time strings you are parsing do not include a date portion, then you will have problems if the starting time and ending time are not in the same day.
EDIT:
From the comments it becomes clear that the times you are working with are already lap durations, in H:M:S format, and you want to convert them to seconds. That does not require D3, it is easy enough:
var components = /^(\d+):(\d+):(\d+)$/.exec("20:59:59");
var elapsedSecs = components[1]*3600+components[2]*60+components[3];
You can use d3.time.format("%H:%M:%S") for your conversion.