Javascript Dates working weird (when converting milliseconds to seconds ) - javascript

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.

Related

Can I apply a offset with a unix timestamp value?

I have a unix timestamp like:
1647386880
I want to apply -0400 offset to this value, is this possible in javascript w/o any external libraries?
No library needed. If you know a datetime in the unix timestamp format and you just want to adjust it by 4 hours, you can add/subtract as needed.
(presuming the datestamp is in GMT, and you are not worried about DST (without an actual timezone location any DST can't be determined)
-0400 would be minus 4 hours, thus 4x60x60= 14,400.
let origDatestamp = 1647386880;
let offsetDatestamp = origDatestamp - 14400;
If your offsets will vary, but always be in the standard format you supplied "[-]HHMM", you could create a function to simplify this.
function applyOffset(gmtTimestamp, offset){
return gmtTimestamp + (parseInt(offset) / 100) * 60 * 60;
}
Note there was another answer posted that seems to have been removed (it had potential too!) - just note that offsets are typically to the hour, but there are some that are not e.g. https://www.timeanddate.com/time/time-zones-interesting.html e.g. Newfoundland Canada at -0230

How to convert a JS Date (programmatically) into a Google Sheet Serial Number and vice-versa?

So, I have a JS date
> new Date()
Mon Aug 05 2019 06:55:46 GMT-0700 (Pacific Daylight Time
In Google Sheets API, the default dateTime render option is DateTimeRenderOption.SERIAL_NUMBER, which as per the documentation says
Instructs date, time, datetime, and duration fields to be output as doubles in "serial number" format, as popularized by Lotus 1-2-3. The whole number portion of the value (left of the decimal) counts the days since December 30th 1899. The fractional portion (right of the decimal) counts the time as a fraction of the day. For example, January 1st 1900 at noon would be 2.5, 2 because it's 2 days after December 30st 1899, and .5 because noon is half a day. February 1st 1900 at 3pm would be 33.625. This correctly treats the year 1900 as not a leap year.
I want to know how to convert the JS Date to SERIAL_NUMBER using API/library and back from SERIAL_NUMBER to JS Date object?
This will get you the serial number. You may or may not want to trim it
function createSerialNum() {
var oneDay = 24 * 60 * 60 * 1000;
var firstDate = new Date(1899, 11, 30);
var secondDate = new Date();
console.log(secondDate);
var secondDateMidnight = new Date(secondDate.getFullYear(), secondDate.getMonth(), secondDate.getDate());
var diff = secondDate.getTime() - secondDateMidnight.getTime();
var left = Math.round(Math.abs((firstDate.getTime() - secondDate.getTime()) / (oneDay))) - 1;
var right = diff / oneDay;
var result = left + right;
console.log(result);
return result;
}
This will turn back it into a date
function createDateFromSerial(serialNum){
serialNum = String(serialNum).split(".");
var ogDate;
var oneDay = 24 * 60 * 60 * 1000;
var firstDate = new Date(1899, 11, 30);
var days = serialNum[0];
var ms = serialNum[1] * oneDay;
ms = String(ms).substring(0, 8);
firstDate.setDate(days);
ogDate = new Date(firstDate.getFullYear(), firstDate.getMonth(), firstDate.getDate(), 0, 0, 0, ms);
console.log(ogDate);
return ogDate;
}
Before we get to conversions first some notes on the serial number format.
Serial Format
As described in the documentation, the serial number has two parts. The integer before the decimal representing the days from December 30th 1899, and the fraction representing the time of day, without a timezone. That is, 0.5 always represents the time 12:00 (Noon), regardless of the user machine or sheet's set timezone, daylight savings, or anything else.
Time zone:
The Serial number is without time zone. This means that a cell value of 0 will always be rendered as "12/30/1899 0:00:00", regardless of the sheet's timezone.
The sheet's timezone only affects functions such as NOW() or TODAY(), and changing the timezone will change their numeric output (and not change the numeric output of TIME() functions).
For example:
Set a cell to the formula =NOW(), then format the cell to render as a number.
Change the sheet's timezone to a different timezone +6 hours from what it previously was.
Make a change to the sheet so the NOW() value recalculates.
Notice that the numeric value will change by about +0.25 to represent 6 hours difference.
Also note that no other date-time cells change their display from the time zone change.
When getting data via spreadsheets.values.get, the default valueRenderOption is
ValueRenderOption.FORMATTED_VALUE, which means that the default dateTimeRenderOption of DateTimeRenderOption.SERIAL_NUMBER is ignored.
Formatting
A DATE_TIME formatted cell will be in a locale format according to the spreadsheet's locale, not the user machine's locale, e.g. "9/12/2020 8:15:40" or "12.9.2020 8:15:40" if the spreadsheet is set to German. If all you're doing with the date is displaying it, and you don't care that there may be a locale mismatch, this is ok. Note that it's not ok to use Date.parse() on this string, as the value will be inconsistent if the spreadsheet locale is something other than English.
If you change valueRenderOption to ValueRenderOption.UNFORMATTED_VALUE, we get the SERIAL_NUMBER value of dates, which I'll give formulas to below.
Writing
To write to the sheet using a JS Date, we don't necessarily need to convert it to lotus serial format. We can write to the cell using a formula:
`=DATE(${date.getFullYear()}, ${date.getMonth() + 1}, ${date.getDate()}) + TIME(${date.getHours()}, ${date.getMinutes()}, ${date.getSeconds()})`
This will have a formula output of something like: =DATE(2020, 9, 12) + TIME(9, 15, 41)
That cell will render as 9/12/2020 9:15:41, regardless of timezone.
In that example the cell was written with the date object's local time. We can write it as the date object's UTC time via:
`=DATE(${date.getUTCFullYear()}, ${date.getUTCMonth() + 1}, ${date.getUTCDate()}) + TIME(${date.getUTCHours()}, ${date.getUTCMinutes()}, ${date.getUTCSeconds()})`
Which you use depends on your use case. Using the local time means that if the local time is 3:15, the cell renders within sheets how you'd expect as "3:15".
Using UTC time means that if the local time is 3:15, and your timezone is -600GMT, the cell will display as "9:15", possibly adding confusion.
Using local time can be a problem however if you are finding the difference between two dates. Take the case of daylight savings. If for example you are in America/Chicago timezone with daylight savings time ending 2am Nov 1, 2020, then the local time difference between 2am and 1am is 2 hours.
(new Date(2020, 10, 1, 2)) - (new Date(2020, 10, 1, 1)) // 7200000 (2 hours) (note JS month is 0-indexed)
But in Sheets, there is no daylight savings time, so subtracting those two dates will be 1 hour, regardless of the spreadsheet's set timezone.
So we have a few options:
Consider the sheet date_time values as UTC times.
Pros: Times are unambiguous. Apps can render the time stamp in the user's timezone and save as UTC.
Cons: Editing the sheet directly can be confusing, as you'd need to convert local times to UTC by hand. Would not work with NOW() / TODAY() functions.
Consider the sheet values to have no time zone.
Pros: Times work with NOW() and TODAY() methods. When editing the sheet directly, times are always entered as the user's local time.
Cons: Timezone information is lost. When calculating the difference between times, daylight savings time changes can cause calculation to be off by an hour.
Consider the sheet values to be relative to the time zone in which the sheet is set.
Pros: Times are unambiguous. Times work with NOW() and TODAY() methods.
Cons: Sheet time zones are always standard time, so editing the sheet directly has the same problems as #1. Converting between sheet's standard time and user's local time is difficult and requires external libraries.
No matter which method we choose, we'll start with the same formulae.
Finally, the Code
(Note that this is very similar to Jeremy Ring's answer, except this will handle DST changes)
/**
* Converts a Lotus serial format to a JS UTC date.
* #param serial {number}
* #return {Date}
*/
function serialToDateUTC(serial) {
const intPart = Math.floor(serial);
const fracPart = serial - intPart;
const time = Math.round(fracPart * DAY_TO_MS);
const date = new Date(Date.UTC(1899, 11, 30 + intPart) + time);
return date;
}
/**
* Converts a JS Date to Lotus serial format.
* #param date {Date}
* #return {number}
*/
function utcDateToSerial(date) {
const dateSansTime = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
const intPart = Math.round((dateSansTime.getTime() - Date.UTC(1899, 11, 30)) / DAY_TO_MS);
const fracPart = (date.getTime() - dateSansTime.getTime()) / DAY_TO_MS;
return intPart + fracPart;
}
To display the UTC date, use Date.toUTCString (Or Intl.DateTimeFormat with the timeZone set). Example:
console.log(serialToDateUTC(44086.344212963).toUTCString()); // 9/12/2020 8:15:40
(Option 1)
If we wish to store in sheets as UTC, and display to the user in local time, we read/write to sheets with these UTC conversions, but then display locally via Date.toLocaleString or Intl.DateTimeFormat
Note that this option it may be confusing when editing the time directly within the Google document; the times will need to be entered in the UTC timezone.
(Option 2)
If we're ignoring time zone, this means we're considering the date/time value from sheets to always be local to the user, regardless of their timezone.
For this, if we have a Date with local time 4:24, we want to store 4:24 in sheets (using a formula this is simply =TIME(4, 24, 0).
To do this, we can use the same methods posted earlier, but coerce/pretend that the local time is a UTC time. E.g.
const timestamp = new Date(); new Date(Date.UTC(timestamp.getFullHours(), timestamp.getMonth(), timestamp.getDate(), timestamp.getHours(), timestamp.getMinutes(), timestamp.getSeconds()));
This will make time differences unstable -- Consider a timesheet application where you took a start time and an end time via Date.now() or new Date(), and had a duration cell with the formula end time minus start time. Coercing this to standard time may then lose or gain an hour in the difference if we straddle the time change.
(Option 3)
Treating all times local to the Sheet's timezone.
This requires us to get two pieces of information. The cell data, and the sheet's timezone. JavaScript natively has no way to create a Date in another timezone, nor does it have a way to get a timezone offset from an arbitrary timezone. To do this we'll need an external library such as countries-and-timezones.
Get the timezone with the Google files.get api, then get the dstOffset (not the utc offset) from the countries-and-timezones lookup. Use that offset to adjust the sheet's timezone from the local timezone's Date.getTimezoneOffset(). Yep, kinda gnarly.
I've been using this for Google Sheet API. Works fine for me.
// Hour format: HH:MM
// Date format: YYYY-MM-DD
function serialDate(hour, date) {
let leftDate = date;
leftDate = new Date(leftDate);
leftDate = leftDate.getTime();
let serialNumberReference = (new Date('1899-12-30')).getTime();
leftDate = (leftDate - serialNumberReference) / (3600 * 24 * 1000);
let rightDate = date;
rightDate = (new Date(rightDate + ' ' + hour)).getTime() - (new Date(rightDate + ' 00:00').getTime());
rightDate = rightDate / (3600 * 24 * 1000);
return leftDate + rightDate;
}
(The algorithm for this is very similar to the one for converting between Celsius and Fahrenheit, which you may be vaguely familiar with.)
There are three main differences between the scale used for JavaScript dates and the one you describe for Google Sheets:
1) JS dates are millisecond-based, whereas Google Sheets dates are day-based,
2) JS dates take 1-Jan-1970 (at 12:00:00 AM UTC) to equal zero, while Google Sheets dates use 30-Dec-1900 for this purpose, and
3) The Date (and Time) constructor in Google Sheets expects its input's timezone to match UTC, whereas JavaScript's Date constructor uses the timezone of whatever location the script runs in.
To convert between the two formats -- as well adjusting for timezone handling -- we need to "stretch" the scale (using multiplication/division) to get from days to milliseconds, and we also need to "shift" the scale (using addition / subtraction) to account for the different interpretations of 'zero'.
This shows in detail how it can be done:
(Note that this script has not been tested using Google Sheets, with the exception of the one example value shown here.)
// Defines constants in milliseconds (JavaScript's unit for dates)
const MS_PER_MINUTE = 60000; // Assumes no leap year adjustment in this minute
const MS_PER_DAY = 86400000; // Assumes no leap year adjustment in this day
const MS_PER_70YEARS_2DAYS = 2209161599801; // Diff between gSheets' & unix's "zero"
// Defines functions to build JS UTC Date objects
/* (The first function makes a preliminary Date object, calculates the difference
between the user's timezone and UTC, and use this to make the final Date object) */
function newUTCDateFromComponents(YYYY, MM, DD = 1, hh = 0, mm = 0, ss = 0){
// Takes 2 to 6 arguments (the components of a Date), returns a UTC Date object
const date = new Date(YYYY, MM, DD, hh, mm, ss);
const offsetMillisecs = date.getTimezoneOffset() * MS_PER_MINUTE;
return new Date(date.getTime() - offsetMillisecs);
}
function newUTCDateFromTimestamp(timestamp){
// Takes a timestamp, returns a UTC Date object
const date = new Date(timestamp);
return date;
}
// Defines functions to convert between JavaScript timestamps and gSheets dates
function gsheetsDateToJsTimestamp(days){
const jsTimestamp = (days * MS_PER_DAY) - MS_PER_70YEARS_2DAYS;
return jsTimestamp;
}
function jsUTCTimestampToGsheetsDate(millisecs){
let days = (millisecs + MS_PER_70YEARS_2DAYS) / MS_PER_DAY;
days = Math.ceil(days * 100000) / 100000; // Rounds the result up to 5 digits
return days;
}
//
// Examples
//
// JS to gSheets
const jsUTCDateSource = newUTCDateFromComponents(2019, 0, 1, 16, 20); // 2019-JAN-01, 4:20PM
const jsUTCDateStringSource = jsUTCDateSource.toUTCString();
const jsUTCMillisecSource = jsUTCDateSource.getTime();
const gsheetsDateResult = jsUTCTimestampToGsheetsDate(jsUTCMillisecSource);
console.log(`${jsUTCDateStringSource}
from JS (milliseconds since Jan 1, 1970 began): ${jsUTCMillisecSource}
to Google Sheets (days since Dec 30, 1899 began): ${gsheetsDateResult}`);
console.log(`-----------`);
// gSheets to JS
const gsheetsDateSource = 43466.68056; // 2019-JAN-01, 4:20PM
const jsMillisecsResult = gsheetsDateToJsTimestamp(gsheetsDateSource);
const jsUTCDateResult = newUTCDateFromTimestamp(jsMillisecsResult);
const jsUTCMillisecsResult = jsUTCDateResult.getTime();
const jsUTCDateStringResult = jsUTCDateResult.toUTCString();
console.log(`from Google Sheets: ${gsheetsDateSource}
to JS: ${jsUTCMillisecsResult} // off by a fraction of a second (see below)
human-readable string: ${jsUTCDateStringResult}`);
console.log(`NOTE: The result is off by a fraction of a second due to rounding
but evaluates to the same human-readable string.`);
Date to Serial Number: (date.getTime() - new Date(1899, 11, 30).getTime()) / (24 * 60 * 60 * 1000)
Serial Number to Date: new Date(serialNumber * 24 * 60 * 60 * 1000 + new Date(1899, 11, 30).getTime())

Get current timestamp from a specific timezone in javascript

I'm trying to get a timestamp from a specific timezone that is independent of the local time.
I want my clients from all over the world to see the exact same timestamp. Is this even possible? I don't want a solution in node.js but if there is a working library, please include it.
You can either generate a timezone independent timestamp by means of JavaScript, using Date object, or using specialized libraries such as moment.js:
const timestampMilliseconds = (new Date()).getTime();
console.log(timestampMilliseconds);
const timestampSeconds = Math.round((new Date()).getTime() / 1000);
console.log(timestampSeconds);
const timestampSecondsMoment = moment().unix();
console.log(timestampSecondsMoment)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.23.0/moment.min.js"></script>
You say that you want to get a timestamp for a "specific time zone". If you know what the time zone offset is for that specific time zone then you should be able to get a UTC date, subtract the time zone offset from it and output a date string that should be the same on all clients. This statement should work:
var timeZoneOffset = 300; // Eastern Standard Time
var sameDate = (new Date(((new Date()) - (timeZoneOffset * 60 * 1000)))).toISOString()
new Date() should return the same time in milliseconds on all clients if the date and time of the local machines are accurate. Time zone offsets are in minutes so you need to multiply them be 60 * 1000 (60 seconds/minute times 1000 milliseconds/second) and then subtract that many milliseconds from the UTC date to get it to equal the current time in the time zone that has that offset. Then convert it to an ISO string. You can manipulate the resulting string if you want. Perhaps get rid of the Z on the end.
var dateWithoutZ = sameDate.slice(0,-1);

How do I get difference between two dates of unknown format in javascript?

I get a date as String from server like this: 2017-01-23T16:08:45.742Z. I want to find the difference in days, between this and the current date (or precisely, current time). I could just extract date alone (without time) and check, but I'd need a precise answer based on provided time & current time.
How do I achieve this?
Should be easy....
var dateFromServer = '2017-01-23T16:08:45.742Z'
var msInDay = 1000 * 60 * 60 * 24
var difference = (new Date(dateFromServer) - Date.now()) / msInDay
document.write('difference = ' + difference + ' days')
That date format looks like ISO_8061. https://en.wikipedia.org/wiki/ISO_8601
Use the Date object to get the difference between today and the other date in milliseconds, then divide by the number of milliseconds in a day.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
The code below can be condensed into a single line but I wanted to be explicit.
let date = "2017-01-23T16:08:45.742Z";
let d1 = new Date(date); // given date
let today = new Date(); // today's date
let diff = (d1 - today); // difference in milliseconds
let days = diff / 8.64e+7; // divide difference by 1 day in milliseconds
console.log(days)
Point of clarification: if I understand you correctly, you're actually trying to get the difference between two dates of different formats, not two dates of unknown formats. That's way easier.
Further, it looks like your server string is already stored in ISO format, which again makes this way easier.
I'd recommend looking at the JavaScript Date object. I believe in this case your best bet would be something like this:
// Your string from the server
var data_from_server = '2017-01-23T16:08:45.742Z';
// Create a new Date() object using the ISO string provided by your server
var olddate = new Date(data_from_server);
// Create a new empty Date() object, which should default to the current time
var currentdate = new Date();
// Subtract the two
var dif = currentdate.getTime() - olddate.getTime();
// Alert the result
alert(dif);

change the time of the start of the day in javascript

Is there a way to set the day to start from 4am not from 12am as we all know, i'm searching for a javascript library or a method to make this available, i'm now using moment.js but I couldn't figure out how to do it.
Thanx for your advice,
Get their local time, and change the timezone to be the timezone of your server plus 4 hours. The timezone of your server is the difference in hours between your local time and GMT.
var GMTdate = new Date();
var timeZoneFromDB = 4.00;
// add or subtract from the timeZone to set it to server time zone plus 4.
// get the timezone offset from local time in minutes
var tzDifference = timeZoneFromDB * 60 + GMTdate.getTimezoneOffset();
//convert the offset to milliseconds, add to targetTime, and make a new Date
var adjustedTime = new Date(GMTdate.getTime() + tzDifference * 60 * 1000);
Javascript date functions using adjustTime will not reflect the date 4 hours later than the date of your server.
Note: the hour will be incorrect of course.
Update ... fixed local time, "their time", to GMT/UTC time.
Updated again ... it was correct the first time. I thought for a moment it needed to start with GMT time (late night due-diligence).
note: if DST is used at server location, change timeZoneFromDb to
var timeZoneFromDB = ((new Date()).dst()) ? '-04:00' : '-05:00';
with correct numbers to adjust to that time zone. Also note DST does not start on the same date universally, to handle it properly the server time zone must be known.

Categories