I'm currently working with a project where I need to find if a time in HH:mm is before another time.
I am using moment time zone and setting the time zone globally to UTC.
The date which I am comparing is: 2020-09-02T00:00:00.0000+00:00
I am running a check where I am doing the following:
const example = '2020-09-02T00:00:00.0000+00:00'
const time = moment(example)
const timeStart = moment('08:00', 'HH:mm')
console.log(time.isBefore(timeStart))
The console log returns a result of false.
The reason I believe is that the timeStart is evaluating to the current day, so it's failing due to the date being in the future. I need to stop moment from comparing the date, is there a way to do this?
What I am trying to achieve is something of the following
'00:00:00'.isBefore('08:00')
const time = moment('2020-09-02T00:00:00.0000+00:00')
const timeStart = moment('08:00', 'HH:mm')
console.log(time)
console.log(timeStart)
console.log(time.isBefore(timeStart))
<script src="https://cdn.jsdelivr.net/momentjs/2.13.0/moment.min.js"></script>
You can do this with moment.js by converting the timestamp to a moment object, then cloning the object and setting its time to the required comparison time. That way you're always comparing times on the same date.
To keep everything as UTC, use utc. E.g.
let ts = '2020-09-02T00:00:00.0000+00:00';
let d = moment.utc(ts); // Invoke UTC mode
let time = '08:30';
let [h, m] = time.split(':');
let e = d.clone().hours(h).minutes(m).seconds(0).milliseconds(0);
console.log(d.format() + ' is before\n' +
e.format() + '? ' + d.isBefore(e));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js"></script>
You can compare just the time with plain JS fairly easily:
/**
* Compare a time to just the time part of a
* Date, all using UTC.
*
* #param {Date} date - date to compare
* #param {string} time - time in HH:mm format
* #returns {boolean} true if time in date is before time passed to function
*/
function isTimeBefore(date = new Date(), time) {
// Get time value for start of UTC day
// Copy date so don't affect original
let dayStart = new Date(+date).setUTCHours(0,0,0,0);
// Convert the time to milliseconds since midnight
let [h, m] = time.split(':');
let ms = h*3.6e6 + m*6e4;
// Compare to the date's milliseconds elapsed since midnight
return ms > (date - dayStart);
}
// UTC timestamp
let ts = '2020-09-02T00:00:00.0000+00:00';
// Convert to Date
let d = new Date(ts);
// local time
let time = '08:30';
console.log('UTC: ' + d.toISOString() + '\nis before ' + time + ' UTC? : ' + isTimeBefore(d, time));
The built–in parser should generally be avoided, however the above uses it to convert the timestamp to a Date because it's about the only supported format that is reliably parsed by browsers in use.
const time = moment('2020-09-02T00:00:00.0000+00:00')
const timeStart = moment('09:00', 'HH:mm')
time.isBefore(timeStart) //returns true
returns true to me.
Related
I'm trying to create an all-day calendar event to 14 days from today using Google App Script but I keep getting the error
Exception: The parameters (String,String) don't match the method signature for CalendarApp.Calendar.createAllDayEvent.
My script is:
var SheetApp = SpreadsheetApp.getActiveSheet();
var SelectedRow = SheetApp.getActiveRange().getRowIndex();
var value = SheetApp.getRange(SelectedRow, 8).getValue(); //Name of the event
var eventCal = CalendarApp.getCalendarById("sampleemail#gmail.com");
eventCal.createAllDayEvent(value.toString(), new Date() + 14);
When you add a number to a Date object, the Date is automatically converted to its underlying value, i.e., the number of milliseconds since the epoch. The result of the addition will be that number of milliseconds plus 14, which is just a big integer. The createAllDayEvent() method expects a Date object rather than an integer.
To get a date that is 14 days from now, use Date.setDate(). This method has the benefit that it automatically takes care of rolling into the next month or next year as necessary, but be careful. The setDate() method modifies a Date object in place, but does not return the Date object. Instead, it returns the underlying value, i.e., the number of milliseconds since the epoch.
This means that you cannot just assign the result you get from setDate() to a variable in the hope of getting a valid Date. Something like const twoWeeksFromNow = new Date().setDate(new Date().getDate() + 14) will not work if you expect twoWeeksFromNow to contain a Date.
An easy way to get it right is to use a short utility function, like this:
function testCreateAllDayEvent() {
const twoWeeksFromNow = dateOffset_(new Date(), +14);
const eventCal = CalendarApp.getCalendarById("sample_email#gmail.com");
const sheet = SpreadsheetApp.getActiveSheet();
const eventTitle = sheet
.getRange('H' + sheet.getActiveRange().getRow())
.getDisplayValue();
eventCal.createAllDayEvent(eventTitle, twoWeeksFromNow);
}
/**
* Gets a new Date object that is offset by numDays from date.
*
* #param {Date} date The date from which to count.
* #param {Number} numDays The number of days to add or subtract from date.
* #return {Date} A new Date object that is offset by numDays from date.
*/
function dateOffset_(date, numDays) {
const newDate = new Date(date);
newDate.setDate(date.getDate() + numDays);
return newDate;
}
Change second parameter
new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate() + 14)
I have always worked with dates in ISO format that ends with a 'Z'. But now I have to replace that 'Z' with timezone info like +08:00.
In other words, currently I have this format 2020-01-17T00:30:00.000Z, but now I need it in this format 2020-01-17T08:30:00+08:00.
Looks like popular date library like moment and dayjs convert date to ISO format without 'Z' too by default. Is that still considered an 'ISO' date format? And I can't find out how to do it with vanilla Javascript and doing the .toISOString() always gives me the 'Z'..
If you get the date string in the ISO format, but you want to get the string in a certain timezone, with the timezone.
Then here's a simple function that does just that.
function getISODateStampWithTZ (date, tzHours)
{
let dateTz = new Date(date);
dateTz.setUTCHours(tzHours);
return dateTz.toISOString().replace(/Z$/,
(tzHours<0 ? '-' : '+') +
(Math.abs(tzHours)<10 ? '0'+Math.abs(tzHours) : Math.abs(tzHours)) +
':00');
}
const date = new Date('2020-01-17T00:30:00.000Z');
console.log(date.toISOString());
console.log(getISODateStampWithTZ(date, 8));
console.log(getISODateStampWithTZ(date, -1));
Such function could also be added to the Date prototype.
The example below prefixes the function with 'custom' to make it distinct from standard methods.
Date.prototype.customToISOStringTZ = function (tzHours)
{
let dateTz = new Date(this);
dateTz.setUTCHours(tzHours);
return dateTz.toISOString().replace(/Z$/,
(tzHours<0 ? '-' : '+') +
(Math.abs(tzHours)<10 ? '0'+Math.abs(tzHours) : Math.abs(tzHours)) +
':00');
}
const date = new Date('2020-01-17T00:30:00.000Z');
console.log(date.toISOString());
console.log(date.customToISOStringTZ(8));
console.log(date.customToISOStringTZ(-1));
parser.isoparse('2019-08-28T14:34:25.518993Z')
use this to get correct format
The Z ("Zulu") on the end means UTC, ie. an offset from UTC of zero. I'm assuming you want to convert from UTC to local time, in which case you need to calculate the offset from UTC:
function convertUTCDateToLocalDate(date) {
const newDate = new Date(date.getTime() + date.getTimezoneOffset() * 60 * 1000);
const offset = date.getTimezoneOffset() / 60;
const hours = date.getHours();
newDate.setHours(hours - offset);
return newDate;
}
Usage:
const date = new Date("2020-01-17T00:30:00.000Z")
const newDate = convertUTCDateToLocalDate(date)
newDate.toISOString() // "2020-01-17T01:30:00.000+01:00"
Beware! This solution won't work for timezones where the offset isn't a full hour.
If you're running this in a browser I'd strongly recommend using a tool like moment.
I observed some strange Date behaviour in Chrome (Version 74.0.3729.131 (Official Build) (64-bit)).
Following javascript was executed in the Chrome Dev Console:
new Date('1894-01-01T00:00:00+01:00')
// result: Mon Jan 01 1894 00:00:00 GMT+0100 (Central European Standard Time)
new Date('1893-01-01T00:00:00+01:00')
// result: Sat Dec 31 1892 23:53:28 GMT+0053 (Central European Standard Time)
I have already read about non standard date parsing via the Date ctor in different browsers, although providing valid ISO8601 values.
But this is more than strange o_o
In Firefox (Quantum 66.0.3 (64-Bit)) the same calls result in expected Date objects:
new Date('1894-01-01T00:00:00+01:00')
// result: > Date 1892-12-31T23:00:00.000Z
new Date('1893-01-01T00:00:00+01:00')
// result: > Date 1893-12-31T23:00:00.000Z
Is this a bug in Chrome?
My input is valid ISO8601 i guess?
The most important question is, how do I fix this? (hopefully without parsing the input string myself)
Okay, seems like this behaviour cannot be avoided, so you should parse dates manually. But the way to parse it is pretty simple.
If we are parsing date in ISO 8601 format, the mask of date string looks like this:
<yyyy>-<mm>-<dd>T<hh>:<mm>:<ss>(.<ms>)?(Z|(+|-)<hh>:<mm>)?
1. Getting date and time separately
The T in string separates date from time. So, we can just split ISO string by T
var isoString = `2019-05-09T13:26:10.979Z`
var [dateString, timeString] = isoString.split("T")
2. Extracting date parameters from date string
So, we have dateString == "2019-05-09". This is pretty simple now to get this parameters separately
var [year, month, date] = dateString.split("-").map(Number)
3. Handling time string
With time string we should make more complex actions due to its variability.
We have timeString == "13:26:10Z"
Also it's possible timeString == "13:26:10" and timeString == "13:26:10+01:00
var clearTimeString = timeString.split(/[Z+-]/)[0]
var [hours, minutes, seconds] = clearTimeString.split(":").map(Number)
var offset = 0 // we will store offset in minutes, but in negation of native JS Date getTimezoneOffset
if (timeString.includes("Z")) {
// then clearTimeString references the UTC time
offset = new Date().getTimezoneOffset() * -1
} else {
var clearOffset = timeString.split(/[+-]/)[1]
if (clearOffset) {
// then we have offset tail
var negation = timeString.includes("+") ? 1 : -1 // detecting is offset positive or negative
var [offsetHours, offsetMinutes] = clearOffset.split(":").map(Number)
offset = (offsetMinutes + offsetHours * 60) * negation
} // otherwise we do nothing because there is no offset marker
}
At this point we have our data representation in numeric format:
year, month, date, hours, minutes, seconds and offset in minutes.
4. Using ...native JS Date constructor
Yes, we cannot avoid it, because it is too cool. JS Date automatically match date for all negative and too big values. So we can just pass all parameters in raw format, and the JS Date constructor will create the right date for us automatically!
new Date(year, month - 1, date, hours, minutes + offset, seconds)
Voila! Here is fully working example.
function convertHistoricalDate(isoString) {
var [dateString, timeString] = isoString.split("T")
var [year, month, date] = dateString.split("-").map(Number)
var clearTimeString = timeString.split(/[Z+-]/)[0]
var [hours, minutes, seconds] = clearTimeString.split(":").map(Number)
var offset = 0 // we will store offset in minutes, but in negation of native JS Date getTimezoneOffset
if (timeString.includes("Z")) {
// then clearTimeString references the UTC time
offset = new Date().getTimezoneOffset() * -1
} else {
var clearOffset = timeString.split(/[+-]/)[1]
if (clearOffset) {
// then we have offset tail
var negation = timeString.includes("+") ? 1 : -1 // detecting is offset positive or negative
var [offsetHours, offsetMinutes] = clearOffset.split(":").map(Number)
offset = (offsetMinutes + offsetHours * 60) * negation
} // otherwise we do nothing because there is no offset marker
}
return new Date(year, month - 1, date, hours, minutes + offset, seconds)
}
var testDate1 = convertHistoricalDate("1894-01-01T00:00:00+01:00")
var testDate2 = convertHistoricalDate("1893-01-01T00:00:00+01:00")
var testDate3 = convertHistoricalDate("1894-01-01T00:00:00-01:00")
var testDate4 = convertHistoricalDate("1893-01-01T00:00:00-01:00")
console.log(testDate1.toLocaleDateString(), testDate1.toLocaleTimeString())
console.log(testDate2.toLocaleDateString(), testDate2.toLocaleTimeString())
console.log(testDate3.toLocaleDateString(), testDate3.toLocaleTimeString())
console.log(testDate4.toLocaleDateString(), testDate4.toLocaleTimeString())
Note
In this case we are getting Date instance with all its own values (like .getHours()) being normalized, including timezone offset. The testDate1.toISOString will still return weird result. But if you are working with this date, it will probably 100% fit your needings.
Hope that helped :)
This might be the case when all browsers follow their own standards for encoding date formats (but I am not sure on this part). Anyways a simple fix for this is to apply the toISOString method.
const today = new Date();
console.log(today.toISOString());
I know that
var currentTime = new Date();
var currentOffset = currentTime.toISOString();
will give current date & time in IST format. Can anyone help me how to get past 2 hours date & time in IST format
To calculate a time difference, you can use a combination of the relevant get and set methods. After you get the value, you perform the desired calculation and use the result as the argument for the set.
Note that the default timezone is based on system settings. So performing such a change has no bearing on the timezone (i.e. for me the code outputs in PDT).
var time = new Date();
var currentOffset = time.getTimezoneOffset();
console.log('Current time: ' + time.toISOString());
console.log('Current offset: ' + currentOffset);
time.setHours(time.getHours() - 2);
var pastOffset = time.getTimezoneOffset();
console.log('Past time: ' + time.toISOString());
console.log('Past offset: ' + currentOffset);
let's say that I have two variables: one that contains a date string (without time) and another one that contains the same date string but with time 00:00:00 like this:
var date1 = '2013-10-23';
var date2 = '2013-10-23 00:00:00';
var date1_time = new Date(date1).getTime();
var date2_time = new Date(date2).getTime()
console.debug('Date 1 time: ' + date1_time + "\n" + 'Date 2 time: ' + date2_time);
The result in the console is this:
Date 1 time: 1382486400000
Date 2 time: 1382500800000
Why aren't these two equal to the same thing? Shouldn't date1's time portion default to 00:00:00 since I did not put a time?
Thank you
I just tested it on chrome and found out that
The first one gives you the timestamp for 00:00:00 UTC
The second gives you timestamp with your current timezone.
So if you substract them you will see +-your timezone:
var date1_time = new Date(date1).getTime();
var date2_time = new Date(date2).getTime();
console.log((date2_time-date1_time)/1000/60/60); //will print the timezone difference.
You can get exact what time the first variable is considering.
var timestamp = 1382486400000;
var date = new Date();
date = new date(timestamp + date.getTimezoneOffset() * 60000)
alert(date);
It all happens because of default UTC time zone and your current
timezone.