Reputation: 2223
I'm trying to get the millisecond value of the nearest absolute year in JavaScript, replying only on the valueOf()
method of the JavaScript Date object.
For example: today is Monday April 4th 2016 at 12:50am. So I am looking for the nearest absolute year (in the past). January 1st, 2016, 00:00am.
Here is the code I have:
var ms_per_year = 31536000000;
var now = new Date().valueOf();
var mod_year = now % ms_per_year;
var nearest_absolute_year = now - mod_year;
console.log(new Date(nearest_absolute_year));
// Sun Dec 20 2015 19:00:00 GMT-0500 (EST)
console.log(new Date(Date.parse('2016 1 1 00:00:00')));
// Fri Jan 01 2016 00:00:00 GMT-0500 (EST)
I would expect the two printed dates to be the same, as they are with minutes:
var ms_per_minute = 60 * 1000;
var now = new Date().valueOf();
var mod_minute = now % ms_per_minute;
var nearest_absolute_minute = now - mod_minute;
console.log(new Date(nearest_absolute_minute));
// Mon Apr 04 2016 00:57:00 GMT-0400 (EDT)
console.log(new Date(Date.parse('2016 4 4 00:57:00')));
// Mon Apr 04 2016 00:57:00 GMT-0400 (EDT)
How can I calculate the milliseconds passed since 1970 and the beginning of the current year with without using Date.parse(), relying solely on math?
Upvotes: 1
Views: 150
Reputation: 9084
This should do the trick:
var d = new Date(new Date().getFullYear(), 0);
d.valueOf(); // > 1451634391371 (ms)
Upvotes: 1
Reputation: 147343
You need to deal with leap years, Himanshu is on the track for an elegant solution, a simple loop will do the trick but is not so efficient:
/* @returns {number} time value for start of current year
** Don't use Date methods
** Assumes current time is after epoch (1970-01-01T00:00:00Z)
*/
function getStartOfYear(timeValue) {
var timeValue = timeValue || Date.now();
var accumulatedTime = 0;
var day = 8.64e7;
var year = 1970;
var msForYear = 365*day; // ms for 1970
function isLeap(n) {
return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
}
// Continue while adding ms for current year won't go past limit
while ((accumulatedTime + msForYear) < timeValue) {
// Add time for current year
accumulatedTime += msForYear;
// Calculate time for next year
msForYear = (isLeap(++year)? 366:365) * day;
}
// Return accumulated time
return accumulatedTime;
}
// Tests
['2010','2011','2012','2013','2014','2015','2016','2017'].forEach(function(y){
//Generate time value away from start of year
var startOfYear = new Date(getStartOfYear(new Date(y,3).getTime() + 23000));
document.write('<br>UTC start of year: ' + startOfYear.toISOString());
startOfYear.setMinutes(startOfYear.getMinutes() + startOfYear.getTimezoneOffset());
document.write('<br>Local start of year: ' + startOfYear);
});
body {
font-family: courier, mono-space;
font-size: 90%
}
Here is a non–looping solution. It works in UTC as above, but can be adjusted to local as suggested there too.
function getStartOfYear(timeValue) {
timeValue = +timeValue || Date.now();
// ms for one day
var day = 8.64e7;
// ms for standard year
var year = 365 * day
// ms for leap block
var leapBlock = year * 4 + day;
// Use 1969-01-01T00:00:00Z as epoch
timeValue += year;
// Accumulate time
var accumulatedTime = 0;
accumulatedTime += Math.floor(timeValue / leapBlock) * leapBlock;
accumulatedTime += Math.floor((timeValue % leapBlock) / year) * year;
// Reset epoch to 1970-01-01T00:00:00Z and return time value
return accumulatedTime - year;
}
And if you want obfuscated, but concise, code, try:
function getStartOfYear(timeValue) {
timeValue = +timeValue || Date.now();
var day = 8.64e7, year = 365 * day, leapBlock = year * 4 + day, accumulatedTime = 0;
return ((timeValue + year) / leapBlock | 0) * leapBlock + ((timeValue + year) % leapBlock / year | 0) * year - year;
}
Upvotes: 1
Reputation: 906
You're logic is not working due to the following assumption
var ms_per_year = 31536000000
For a year with 365 days this is the correct value of milliseconds, but you forgot to take leaps years into account.
The following code will help
var ms_per_year = 31536000000;
var ms_per_day = 86400000;
var now = new Date().valueOf();
var mod_year = now % ms_per_year;
var year = Math.floor(now/ms_per_year);
var leap_years = Math.floor(year/4);
var nearest_absolute_year = now - mod_year;
var actual_value = nearest_absolute_year + (leap_years*ms_per_day);
console.log(new Date(actual_value));
correct me if you find any mistakes.
Upvotes: 0