Parse date in javascript using CET as default timezone - javascript

I have some legacy webservices that sometimes does not localize dates. Sometimes they do, so I have to support both cases.
They shall always use Italy's locale (UTC+1 for standard time, and UTC+2 for daylight saving time), but sometimes they return dates omitting the timezone at the end of date ISO strings.
For example, the italian new year should be 2018-01-01T00:00:00+0100 and they instead return only 2018-01-01T00:00:00
This causes bad behaviour in Javascript, especially when dealing with deadlines and clients that are in other timezones.
I'd like to be able to write a piece of code that parses a date string in ISO format, assuming italian localization if there is no timezone specified.
My code is almost ok (it doesn't parse milliseconds, but I can live with that), unfortunately it miserably fails when executed by browsers in timezones that doesn't have daylight saving time. What should I do? Am I missing something?
Thanks in advance
/**
* Get the local timezone using standard time (no daylight saving time).
*/
Date.prototype.stdTimezoneOffset = function() {
var jan = new Date(this.getFullYear(), 0, 1);
var jul = new Date(this.getFullYear(), 6, 1);
return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
}
/**
* Check whether current time is daylight saving time.
*/
Date.prototype.isDst = function() {
return this.getTimezoneOffset() < this.stdTimezoneOffset();
}
/**
* Check whether daylight saving time is observed in current timezone.
*/
Date.prototype.isDstObserved = function() {
var jan = new Date(this.getFullYear(), 0, 1);
var jul = new Date(this.getFullYear(), 6, 1);
return jan.getTimezoneOffset() != jul.getTimezoneOffset();
}
/**
* Cross-browser parse of a date using CET as default timezone.
*/
Date.parseFromCET = function(str) {
if (str == null) return null;
// split the input string into an array of integers
var a = str.split(/[^0-9]/)
.map(function(s) {
return parseInt(s, 10)
});
var b = new Date(
a[0], // yyyy
a[1] - 1 || 0, // MM
a[2] || 1, // dd
a[3] || 0, // hh
a[4] || 0, // mm
a[5] || 0 // ss
);
// if no timezone is present, force to CET
if (str.lastIndexOf('-') <= 7 && str.indexOf('+') == -1 && str.indexOf('Z') == -1) {
var CET_timezone_offset = b.isDst() ? '+0200' : '+0100'
var isoString = a[0] + '-' + a[1] + '-' + a[2] + 'T' +
a[3] + ':' + a[4] + ':' + a[5] + CET_timezone_offset;
return Date.parseFromCET(isoString);
}
// remove local timezone offset to go from UTC time to local time
b.setMinutes(b.getMinutes() - b.getTimezoneOffset());
// add/remove forced timezone offset to calculate exact local time
if (str.indexOf('+') > -1) {
let hours = Math.floor(a[a.length - 1] / 100);
let minutes = a[a.length - 1] % 100;
b.setMinutes(b.getMinutes() - minutes);
b.setHours(b.getHours() - hours);
}
if (str.lastIndexOf('-') > 7) {
let hours = Math.floor(a[a.length - 1] / 100);
let minutes = a[a.length - 1] % 100;
b.setMinutes(b.getMinutes() + minutes);
b.setHours(b.getHours() + hours);
}
return b;
}

The timestamp isn't consistent with the format in ECMA-262 as it's missing a colon in the offset. So parsing is implementation dependent and you may get an invalid date (e.g. in Safari).
The standard offset for Rome is +01:00. Daylight saving starts at 02:00 on the last Sunday in March (change to +02:00), and ends at 02:00 on the last Sunday in October (back to +01:00).
Note that there have been historical changes. Italy started using daylight saving in 1916, but there have been periods where it wasn't observed. It has been observed continuously since 1965, so as long as your dates are after that, you don't have to worry about past changes, only future ones.
The following is one way to go about it, it needs a lot more testing and should do validation of the input string and resulting Date object. You should probably also handle a timezone of "Z".
If you're going to do this a lot, a library that manages timezones will help greatly as it should also handle historical changes, past and future.
/** Get a Date for the last Sunday of the month
* #param {number|string} year - year for month
* #param {number|string} month - calendar month number, 1 = Jan, 2 = Feb, etc.
* #returns {Date} date for last Sunday for given year and month
*/
function getLastSunday(year, month) {
// Date for last day of month
var d = new Date(Date.UTC(year, month, 0));
// Adjust to previous Sunday
d.setUTCDate(d.getUTCDate() - d.getUTCDay());
return d;
}
/** Return a date set to the UTC start of Italian DST
* Starts at +0300 UTC on the last Sunday in March
*
* #param {number|string} year to get start of DST for
* #returns {Date} set to start date and +0300Z
*/
function getDSTStart(year) {
var d = getLastSunday(year, 3);
d.setUTCHours(3);
return d;
}
/** Return a date set to the UTC end of Italian DST
* Ends at +0400 UTC on the last Sunday in October
*
* #param {number|string} year to get start of DST for
* #returns {Date} set to start date and +0400Z
*/
function getDSTEnd(year) {
var d = getLastSunday(year, 10);
d.setUTCHours(4);
return d;
}
/** Given a year, month, day and hour, return
* true or false depending on whether DST is
* being observed in Italy.
* Use UTC to avoid local influences, assume standard time
*
* #param {number|string} year - subject year
* #param {number|string} month - subject calendar month
* #param {number|string} day - subject day
* #param {number|string} hour - subject hour
* #returns {number} offset for provided date and time
*/
function getItalianOffset(year, month, day, hour) {
var d = new Date(Date.UTC(year, month-1, day, +hour + 1));
return d >= getDSTStart(year) && d < getDSTEnd(year)? '+0200' : '+0100';
}
/** Convert offset in format +0000 to minutes
* EMCAScript offset has opposite sign
*
* #param {string} offset - in format +/-HHmm
* #reaturns {number} offset in minutes, + for west, - for east
*/
function offsetToMins(offset) {
sign = /^\+/.test(offset)? -1 : 1;
tz = 60 * offset.slice(-4, -2) + (1 * offset.slice(-2));
tz *= sign;
return tz;
}
/** Parse timestamp that may or may not have a timezone.
* If no timezone, assume Italian timezone (+0100), adjusting for
* daylight saving.
* DST starts at +0300Z on last Sunday in March
* DST ends at +0400Z on last Sunday in October
* 2018-01-01T00:00:00+0100 or 2018-01-01T00:00:00
*/
function parseItalianDate(s) {
var b = s.split(/\D/);
var hasTz = /[+-]\d{4}$/.test(s);
var d, sign, tz;
// If has offset, get from string
// Otherwise, calculate offset
if (hasTz) {
tz = s.slice(-5);
} else {
tz = getItalianOffset(b[0], b[1], b[2], b[3]);
}
// Create Date using correct offset
d = new Date(Date.UTC(b[0], b[1]-1, b[2], b[3], b[4], b[5]));
d.setUTCMinutes(d.getUTCMinutes() + offsetToMins(tz));
return d;
}
// Tests
['2018-01-01T00:00:00', // New year
'2018-03-25T01:00:00', // One hour before change over
'2018-03-25T03:00:00', // One hour after change over
'2018-03-25T01:00:00+0100',// As above but with timzone offset
'2018-03-25T03:00:00+0200',
'2018-10-27T03:00:00', // Still in DST
'2018-10-28T03:00:00', // After DST ended
'2018-10-28T03:00:00+0100'
].forEach(function(s) {
console.log(`${s} => ${formatDate(parseItalianDate(s))}`);
});
// Helper to format a date in Europe/Rome timezone
function formatDate(d) {
return d.toLocaleString('en-GB',{
year : 'numeric',
month : 'short',
day : '2-digit',
weekday: 'short',
hour : '2-digit',
minute : '2-digit',
second : '2-digit',
hour12 : 'false',
timeZone: 'Europe/Rome',
timeZoneName: 'short'
});
}

As RobG mentioned in his great answer, you'll have an easier time if you use a library for this. There are several to choose from. A good choice for modern applications is Luxon, which will support all of the scenarios you described. Here are some examples:
let dt = DateTime.fromISO('2018-01-01T00:00:00+0100', { setZone: true, zone: 'Europe/Rome'});
console.log(dt.toISO()); //=> "2018-01-01T00:00:00.000+01:00"
let dt = DateTime.fromISO('2018-01-01T00:00:00', { setZone: true, zone: 'Europe/Rome'});
console.log(dt.toISO()); //=> "2018-01-01T00:00:00.000+01:00"
let dt = DateTime.fromISO('2018-07-01T00:00:00', { setZone: true, zone: 'Europe/Rome'});
console.log(dt.toISO()); //=> "2018-07-01T00:00:00.000+02:00"
A few notes about the above:
fromISO will accept the offset missing, or with Z, or with an offset with or without a colon.
setZone: true tells it to keep the offset from the string if one is provided.
zone: 'Europe/Rome' tells it to use the time zone for Italy when no offset is provided.
You can, of course, use the Luxon DateTime object (dt here) in a variety of ways other than just emitting a string with toISO().
There are other libraries that can do such things, such as moment-timezone, and js-joda, but I prefer Luxon lately.

Related

Why is there an extra 1/4 of a day in this time calculation?

I was trying to do a basic date calculation as seen below. I want to find the remaining days in the month given the arguments below.
I thought I determined a solution, but there appears to be an extra 1/4 in the answer.
// returns last day of month
function lastDayOfMonth(date) {
return new Date(date.getFullYear(), date.getMonth() + 1, 0)
}
let day_start = new Date('2018-11-04');
let day_end = new Date('2019-01-10');
let day_last = lastDayOfMonth(day_start);
let total = 0;
if (day_end <= day_last) {
total = day_end - day_start;
} else {
total = day_last - day_start;
}
console.log(total / (1000 * 60 * 60 * 24)); // returns 26.25
The difference comes from the UTC offset, since as you have it written, day_start and day_end are midnight UTC, but day_last is midnight in your local timezone.
To fix, just do all calculations in UTC. Since day_last is the only one that is local, this is the only one you need to adjust. In order to produce this in UTC, you could use the setUTCFullYear and related methods (sadly I don't believe there is a way to do it with the Date constructor):
// returns last day of month
function lastDayOfMonth(date) {
// Set correct date in UTC:
let last_date = new Date();
last_date.setUTCFullYear(date.getFullYear());
last_date.setUTCMonth(date.getMonth() + 1);
last_date.setUTCDate(0);
// Zero out the time so that this will end up as midnight:
last_date.setUTCHours(0);
last_date.setUTCMinutes(0);
last_date.setUTCSeconds(0);
last_date.setUTCMilliseconds(0);
return last_date;
}
let day_start = new Date('2018-11-04');
let day_end = new Date('2019-01-10');
let day_last = lastDayOfMonth(day_start);
let total = 0;
if (day_end <= day_last) {
total = day_end - day_start;
} else {
total = day_last - day_start;
}
console.log(total / (1000 * 60 * 60 * 24)); // returns 26 flat
The Date constructor using the format, new Date(year, month, day) is evaluated using your local time zone. The Date constructor for the ISO 8601 string, YYYY-MM-DD is evaluated in UTC.
You can resolve this by printing your local date in ISO 8601 format and passing that into a newly constructed Date object.
function lastDayOfMonth(date) {
let localDate=new Date(date.getFullYear(), date.getMonth()+1, 0);
let m=localDate.getMonth();
let d=localDate.getDate();
return new Date(localDate.getFullYear()+'-'+(m<10?'0':'')+m+'-'+ (d<10?'0':'')+d);
}

Calculating BST time from Date object?

I've reviewed a few questions on similar topics already, but none of them address calculating a destination timezone, taking its DST (daylight savings time) into account. I'm trying to write a simple widget that displays a live local time of a specific timezone to anyone visiting the page. The timezone of interest is BST (what is BST?), but if possible I'd love to see a generic implementation for any locale. Here's my attempt, using vanilla JavaScript:
function getBST() {
var date = new Date(),
utc = date.getTime() + date.getTimezoneOffset() * 60000,
local = new Date(utc), // this is GMT, not BST
day = local.getDate(),
mon = local.getMonth() + 1,
year = local.getFullYear(),
hour = local.getHours(),
minute = ('0' + local.getMinutes()).slice(-2),
second = ('0' + local.getSeconds()).slice(-2),
suffix = hour < 12 ? 'AM' : 'PM';
hour = (hour - 24) % 12 + 12;
return mon + '/' + day + '/' + year + ', ' + hour + ':' + minute + ':' + second + ' ' + suffix;
}
setInterval(function () {
document.body.textContent = getBST();
}, 1000);
Based on #RobG's answer, I've written this code:
function resetDay(date) {
date.setUTCMilliseconds(0);
date.setUTCSeconds(0);
date.setUTCMinutes(0);
date.setUTCHours(1);
return date;
}
function lastDay(date, day) {
while (date.getUTCDay() !== day) {
date.setUTCDate(date.getUTCDate() - 1);
}
return date;
}
function adjustDST(date, begin, end) {
if (date >= begin && date < end) {
date.setUTCHours(date.getUTCHours() + 1);
}
return date;
}
function updateBST() {
var date = new Date();
var begin = resetDay(new Date());
begin.setUTCMonth(3, -1);
begin = lastDay(begin, 0);
var end = resetDay(new Date());
end.setUTCMonth(10, -1);
end = lastDay(end, 0);
date = adjustDST(date, begin, end);
var day = date.getUTCDate(),
mon = date.getUTCMonth() + 1,
year = date.getUTCFullYear(),
hour = date.getUTCHours(),
minute = ('0' + date.getUTCMinutes()).slice(-2),
second = ('0' + date.getUTCSeconds()).slice(-2),
suffix = hour < 12 ? 'AM' : 'PM';
hour = (hour - 24) % 12 + 12;
return mon + '/' + day + '/' + year + ', ' + hour + ':' + minute + ':' + second + ' ' + suffix;
}
setInterval(function () {
document.body.textContent = updateBST();
}, 1000);
I tested the BST by pausing in the debugger and changing the month of the current date, and the output was an hour later so it seems to work properly. Thanks for all your help everyone!
It's not possible in pure JS, just using the Date methods, but there's (of course) a lib for that: http://momentjs.com/timezone/
Example:
moment.tz("Europe/London").format(); // 2016-01-15T09:21:08-07:00
It's not difficult to support one timezone provided you know when it transitions in and out of daylight saving.
A javascript Date consists of a time value in UTC and a timezone offset based on the host system settings. So all you need to do is apply the timezone offset that you want to the UTC time and presto, there's your time in any timezone.
There is no standardised system for abbreviating time zones, though there are some defacto standards (e.g. IATA timezone codes and IANA timezones). I guess by BST you mean British Summer Time, also known as British Daylight Time (BDT) and British Daylight Saving Time (BDST). It might also be Bangladesh Standard Time or Bougainville Standard Time which are also known as "BST".
There are various libraries (such as Moment Timezone) that use the IANA codes and can provide the time for any supported time zone for (more or less) any time.
BST starts at 01:00 UTC on the last Sunday in March and ends at 01:00 UTC on the last Sunday in October each year, so the algorithm is:
Create a new date based on the system's local settings (e.g. new Date())
Create dates for the start and end of BST based on UTC values (for this year, 2016-03-27T01:00:00Z and 2016-03-30T02:00:00Z)
See if the current UTC time falls in that range
If so, add 1 hour to the UTC time of the date created in #1
Output a formatted string based on the date's UTC values
That's it, the only hard part is finding the appropriate Sunday dates. You don't need to consider the local timezone offset at all, since everything is based on UTC and Date objects are too.
Right now I don't have time to provide code, so have a go and I can get back to you in about 10 hrs.
Edit
So here's the code. The first two function are helpers, one gets the last Sunday in a month, the other formats an ISO 8601 string with offset. Most of the work is in those two functions. Hopefully the comments are sufficient, if more explanation is required, just ask.
I haven't included milliseconds in the string, feel free to add them if you want, add + '.' + ('00' + d.getUTCMilliseconds()).slice(-3) before the offset part of the formatted string.
Note that the function will need to be modified if the dates for starting or stopping daylight saving are changed, but that is infrequent. Historic dates of course will need a small database of when daylight saving starts and stops for particular years and periods.
/* Return a Date for the last Sunday in a month
** #param {number} year - full year number (e.g. 2015)
** #param {number} month - calendar month number (jan=1)
** #returns {Date} date for last Sunday in given month
*/
function getLastSunday(year, month) {
// Create date for last day in month
var d = new Date(year, month, 0);
// Adjust to previous Sunday
d.setDate(d.getDate() - d.getDay());
return d;
}
/* Format a date string as ISO 8601 with supplied offset
** #param {Date} date - date to format
** #param {number} offset - offset in minutes (+east, -west), will be
** converted to +/-00:00
** #returns {string} formatted date and time
**
** Note that javascript Date offsets are opposite: -east, +west but
** this function doesn't use the Date's offset.
*/
function formatDate(d, offset) {
function z(n){return ('0'+n).slice(-2)}
// Default offset to 0
offset = offset || 0;
// Generate offset string
var offSign = offset < 0? '-' : '+';
offset = Math.abs(offset);
var offString = offSign + ('0'+(offset/60|0)).slice(-2) + ':' + ('0'+(offset%60)).slice(-2);
// Generate date string
return d.getUTCFullYear() + '-' + z(d.getUTCMonth()+1) + '-' + z(d.getUTCDate()) +
'T' + z(d.getUTCHours()) + ':' + z(d.getUTCMinutes()) + ':' + z(d.getUTCSeconds()) +
offString;
}
/* Return Date object for current time in London. Assumes
** daylight saving starts at 01:00 UTC on last Sunday in March
** and ends at 01:00 UTC on the last Sunday in October.
** #param {Date} d - date to test. Default to current
** system date and time
** #param {boolean, optional} obj - if true, return a Date object. Otherwise, return
** an ISO 8601 formatted string
*/
function getLondonTime(d, obj) {
// Use provided date or default to current date and time
d = d || new Date();
// Get start and end dates for daylight saving for supplied date's year
// Set UTC date values and time to 01:00
var dstS = getLastSunday(d.getFullYear(), 3);
var dstE = getLastSunday(d.getFullYear(), 10);
dstS = new Date(Date.UTC(dstS.getFullYear(), dstS.getMonth(), dstS.getDate(),1));
dstE = new Date(Date.UTC(dstE.getFullYear(), dstE.getMonth(), dstE.getDate(),1));
// If date is between dstStart and dstEnd, add 1 hour to UTC time
// and format using +60 offset
if (d > dstS && d < dstE) {
d.setUTCHours(d.getUTCHours() +1);
return formatDate(d, 60);
}
// Otherwise, don't adjust and format with 00 offset
return obj? d : formatDate(d);
}
document.write('Current London time: ' + getLondonTime(new Date()));
London Time / BST:
const now = new Date();
console.log(now.toLocaleString('en-GB', { timeZone: 'Europe/London' }));

Get time (hour, minute) from a non-local ISO8601 string with moment

I would like to get the time of an ISO8601 string in the specified timezone.
However, whenever I grab the moment object of the ISO8601 string, it converts everything to my machine's local time.
E.g. when I do moment("2015-09-15T07:55+02:00").hours(), it returns 1 since it converted it to 2015-09-15T01:55:00-04:00".
How do I make it so that it returns 7 for hours?
I think this will help:
http://momentjs.com/docs/#/parsing/special-formats/
moment("2010-01-01T05:06:07", moment.ISO_8601);
not sure about moment, but here's my converter for the eastern time zone, if it helps...
getServerDate: function(dateValue) {
var dateJan;
var dateJul;
var timezoneOffset;
// Get dates for January and July
dateJan = new Date(dateValue.getFullYear(), 0, 1);
dateJul = new Date(dateValue.getFullYear(), 6, 1);
// Get timezone offset
timezoneOffset = Math.max(dateJan.getTimezoneOffset(), dateJul.getTimezoneOffset());
// Check if daylight savings
if (dateValue.getTimezoneOffset() < timezoneOffset) {
// Adjust date by 4 hours
dateValue = new Date(dateValue.getTime() + ((1 * 60 * 60 * 1000) * 4));
} else {
// Adjust date by 5 hours
dateValue = new Date(dateValue.getTime() + ((1 * 60 * 60 * 1000) * 5));
}
return dateValue;
},

Why is moment.js diff method returning NaN?

Terminal output:
Now: { _d: Sat Jan 13 2018 02:39:25 GMT-0400 (AST),
_isUTC: false,
_a: null,
_lang: false }
Expiration Date: { _d: Wed Feb 13 2013 02:00:15 GMT-0400 (AST),
_isUTC: false,
_a: null,
_lang: false }
Difference between Now and Expiration Date: NaN
Code:
console.log('Difference between Now and Expiration Date:', now.diff(expDate, 'months', true));
moment.js source:
diff : function (input, val, asFloat) {
var inputMoment = this._isUTC ? moment(input).utc() : moment(input).local(),
zoneDiff = (this.zone() - inputMoment.zone()) * 6e4,
diff = this._d - inputMoment._d - zoneDiff,
year = this.year() - inputMoment.year(),
month = this.month() - inputMoment.month(),
date = this.date() - inputMoment.date(),
output;
if (val === 'months') {
output = year * 12 + month + date / 30;
} else if (val === 'years') {
output = year + (month + date / 30) / 12;
} else {
output = val === 'seconds' ? diff / 1e3 : // 1000
val === 'minutes' ? diff / 6e4 : // 1000 * 60
val === 'hours' ? diff / 36e5 : // 1000 * 60 * 60
val === 'days' ? diff / 864e5 : // 1000 * 60 * 60 * 24
val === 'weeks' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7
diff;
}
return asFloat ? output : round(output);
}
I had the same issue when comparing two moment objects. The problem I had was that the first moment was constructed specifying a UK date format while the second moment was created without specifying the UK date format and the date happened to have a day greater than 12 e.g 27th of the month. On closer inspection while debugging, the second date's moment object noted that its _d field was an invalid date.
var startDate = moment("23/09/2019", "DD/MM/YYYY");
var endDate = moment("27/09/2019");
var dateDiff = startDate.diff(endDate, "days"); // returns NaN
The fix was to construct the second moment object and specify the date format.
var endDate = moment("27/09/2019", "DD/MM/YYYY");
From comments of the question, I gather that you are attempting to store a moment instance directly into MongoDB and then retrieve it later.
A moment isn't directly serializable, so this will always cause issues. You should instead obtain an ISO string from the moment:
var m = moment();
var s = m.toISOString(); // "2013-08-02T20:13:45.123Z"
Store that string in MongoDB. Later, when you retrieve it, you can construct a new moment instance from that value.
var m = moment("2013-08-02T20:13:45.123Z");
If you prefer something more compact, you could use the number obtained from m.valueOf() instead. But that's not as easy to read or manipulate.
Don't use the _d field suggested in comments. That is internal to moment, and shouldn't be used directly. It might not be what you are expecting.

how can I convert day of year to date in javascript?

I want to take a day of the year and convert to an actual date using the Date object. Example: day 257 of 1929, how can I go about doing this?
"I want to take a day of the year and convert to an actual date using the Date object."
After re-reading your question, it sounds like you have a year number, and an arbitrary day number (e.g. a number within 0..365 (or 366 for a leap year)), and you want to get a date from that.
For example:
dateFromDay(2010, 301); // "Thu Oct 28 2010", today ;)
dateFromDay(2010, 365); // "Fri Dec 31 2010"
If it's that, can be done easily:
function dateFromDay(year, day){
var date = new Date(year, 0); // initialize a date in `year-01-01`
return new Date(date.setDate(day)); // add the number of days
}
You could add also some validation, to ensure that the day number is withing the range of days in the year supplied.
The shortest possible way is to create a new date object with the given year, January as month and your day of the year as date:
const date = new Date(2017, 0, 365);
console.log(date.toLocaleDateString());
As for setDate the correct month gets calculated if the given date is larger than the month's length.
// You might need both parts of it-
Date.fromDayofYear= function(n, y){
if(!y) y= new Date().getFullYear();
var d= new Date(y, 0, 1);
return new Date(d.setMonth(0, n));
}
Date.prototype.dayofYear= function(){
var d= new Date(this.getFullYear(), 0, 0);
return Math.floor((this-d)/8.64e+7);
}
var d=new Date().dayofYear();
//
alert('day#'+d+' is '+Date.fromDayofYear(d).toLocaleDateString())
/* returned value: (String)
day#301 is Thursday, October 28, 2010
*/
Here is a function that takes a day number, and returns the date object
optionally, it takes a year in YYYY format for parameter 2. If you leave it off, it will default to current year.
var getDateFromDayNum = function(dayNum, year){
var date = new Date();
if(year){
date.setFullYear(year);
}
date.setMonth(0);
date.setDate(0);
var timeOfFirst = date.getTime(); // this is the time in milliseconds of 1/1/YYYY
var dayMilli = 1000 * 60 * 60 * 24;
var dayNumMilli = dayNum * dayMilli;
date.setTime(timeOfFirst + dayNumMilli);
return date;
}
OUTPUT
// OUTPUT OF DAY 232 of year 1995
var pastDate = getDateFromDayNum(232,1995)
console.log("PAST DATE: " , pastDate);
PAST DATE: Sun Aug 20 1995 09:47:18 GMT-0400 (EDT)
Here's my implementation, which supports fractional days. The concept is simple: get the unix timestamp of midnight on the first day of the year, then multiply the desired day by the number of milliseconds in a day.
/**
* Converts day of the year to a unix timestamp
* #param {Number} dayOfYear 1-365, with support for floats
* #param {Number} year (optional) 2 or 4 digit year representation. Defaults to
* current year.
* #return {Number} Unix timestamp (ms precision)
*/
function dayOfYearToTimestamp(dayOfYear, year) {
year = year || (new Date()).getFullYear();
var dayMS = 1000 * 60 * 60 * 24;
// Note the Z, forcing this to UTC time. Without this it would be a local time, which would have to be further adjusted to account for timezone.
var yearStart = new Date('1/1/' + year + ' 0:0:0 Z');
return yearStart + ((dayOfYear - 1) * dayMS);
}
// usage
// 2015-01-01T00:00:00.000Z
console.log(new Date(dayOfYearToTimestamp(1, 2015)));
// support for fractional day (for satellite TLE propagation, etc)
// 2015-06-29T12:19:03.437Z
console.log(new Date(dayOfYearToTimestamp(180.51323423, 2015)).toISOString);
If I understand your question correctly, you can do that from the Date constructor like this
new Date(year, month, day, hours, minutes, seconds, milliseconds)
All arguments as integers
You have a few options;
If you're using a standard format, you can do something like:
new Date(dateStr);
If you'd rather be safe about it, you could do:
var date, timestamp;
try {
timestamp = Date.parse(dateStr);
} catch(e) {}
if(timestamp)
date = new Date(timestamp);
or simply,
new Date(Date.parse(dateStr));
Or, if you have an arbitrary format, split the string/parse it into units, and do:
new Date(year, month - 1, day)
Example of the last:
var dateStr = '28/10/2010'; // uncommon US short date
var dateArr = dateStr.split('/');
var dateObj = new Date(dateArr[2], parseInt(dateArr[1]) - 1, dateArr[0]);
this also works ..
function to2(x) { return ("0"+x).slice(-2); }
function formatDate(d){
return d.getFullYear()+"-"+to2(d.getMonth()+1)+"-"+to2(d.getDate());
}
document.write(formatDate(new Date(2016,0,257)));
prints "2016-09-13"
which is correct as 2016 is a leaap year. (see calendars here: http://disc.sci.gsfc.nasa.gov/julian_calendar.html )
If you always want a UTC date:
function getDateFromDayOfYear (year, day) {
return new Date(Date.UTC(year, 0, day))
}
console.log(getDateFromDayOfYear(2020, 1)) // 2020-01-01T00:00:00.000Z
console.log(getDateFromDayOfYear(2020, 305)) // 2020-10-31T00:00:00.000Z
console.log(getDateFromDayOfYear(2020, 366)) // 2020-12-31T00:00:00.000Z

Categories