ANSI date (COBOL Integer date) to current date - javascript

I need to translate an integer representing the number of days since 01.01.1601 (as of 6th November 2012: 150422) to a javascript Date object.
Each year has approximately 365.242199 days, so the calculation should be as follows:
var daysPerYear = 365.242199;
var daysSince = 150422;
var year = 1601 + Math.floor(daysSince / daysPerYear); // correct, gives me 2012
var days = Math.floor(daysSince % daysPerYear); // wrong, gives me 307
Now I create the Date object:
var date = new Date(year, 0);
date.setDate(days);
The date now points to 'Fri Nov 02 2012 00:00:00 GMT+0100 (CET)' which is off by about 4 days.
What is wrong with my calculation? Is there an easier way to obtain the Date object?

Clone out a copy of OpenCOBOL 1.1, and look through libcob/intrinsic.c for computations.
See cob_intr_date_of_integer in particular.
For an SVN readonly checkout
svn checkout svn://svn.code.sf.net/p/open-cobol/code/trunk open-cobol-code
or browse to
http://sourceforge.net/projects/open-cobol/files/open-cobol/1.1/open-cobol-1.1.tar.gz/download

JavaScript dates revolve from midnight on 1st January, 1970. If you do new Data().getTime(), for example, you'll be returned the number of milliseconds from that point. Therefore, to convert your dates from 1st January, 1601, you need to calculate the exact number of milliseconds between 1/1/1601 and 1/1/1970 and take the difference from your date (also converted into milliseconds).
This way, all you are doing is adding numbers together and won't suffer from any floating point inaccuracies or error from your approximations.

Related

Javascript Date, setFullYear changes timezone

Javascript Dates work with timezones. If I create a date, it sets the timezone. When I update the year of that date, I don't expect the timezone to change. It does, however. The worst thing is that it changes the timezone but not the time, causing the actual time to shift by one hour!
This causes the issue that if I have the person's birth date, and want to know his birthday this year, I cannot simply set the year to the current year using birthdate.setFullYear(2018), because it will return the birthday minus one hour. That means that it occurs one day before the actual birthday, at eleven 'o clock.
let test = new Date('1990-10-20');
console.log(test);
console.log(test.toISOString().substring(0, 10));
// 1990-10-20 ( Sat Oct 20 1990 01:00:00 GMT+0100 (Central European Standard Time) )
test.setFullYear(2000);
console.log(test);
console.log(test.toISOString().substring(0, 10));
// 2000-10-19 ( Fri Oct 20 2000 01:00:00 GMT+0200 (Central European Summer Time) === one hour too soon!! )
It might be that your timezone does not reproduce, here is my output:
"1990-10-20T00:00:00.000Z"
1990-10-20
"2000-10-19T23:00:00.000Z"
2000-10-19
The only workaround I found is substring the date and replace it as string values. How can I do it better using the Date object?
Hm, maybe it would be better to use setUTCFullYear(...) (MDN Docs) instead? At least in that case, it won't mess up the time.
let test = new Date('1990-10-20');
console.log(test);
console.log(test.toISOString().substring(0, 10));
// "1990-10-20T00:00:00.000Z")
test.setUTCFullYear(2000);
console.log(test);
console.log(test.toISOString().substring(0, 10));
// "2000-10-20T00:00:00.000Z"
BEWARE THE TIMEZONE If you want to work with just-a-date using the date object (and you more or less have to) your date objects should represent UTC midnight at the start of the date in question. In your case, to indicate a year, use UTC midnight at the start of the 1st January. This is a common and necessary convention, but it requires a lot of attention to make sure that the timezone doesn't creep back in, as you've discovered.
When you say "javascript dates work with timezones", a javascript date is a moment in time (ticks since the epoch) with handy static functions for converting that moment into a meaningful string in the local timezone (or a specified timezone). The date object itself does not have a timezone property.
So, you can create your UTC midnight year with something like...
var myDate = new Date(Date.UTC(2018,0,1)); // months are zero-indexed !
Then serialize it specifying UTC...
myDate.toISOString();
myDate.toLocaleDateString("en",{timezone:"UTC"});

How can I compare two different date formats and different time zones in JavaScript?

I want to compare two different time objects given in different formats and time zones.
Input as structured english:
IF "Mon Sep 15 2014 18:20:52 GMT+0200 (CEST)" is greater than "2014-09-04T13:57:33.338Z"
Expected output:
True
How to achieve this in JavaScript?
Native JavaScript
You can use the Date.parse(dateObject) to convert your date to a Unix timestamp and do your test on it. You can look at the documentation about it.
Using an additionnal library
You can also use an external library, such as Moment.js. This library allows you to compare your date without transforming them into timestamps or other exoctic formats.
You just have to get your date and compare it to another one:
moment("Mon Sep 15 2014 18:20:52 GMT+0200 (CEST)").isAfter('2014-09-04T13:57:33.338');
I let you explore this wonderful library by yourself if you want.
Moment website: http://momentjs.com/
Hope this two solutions will help you :)
In JavaScript use Date.parse(myDateObject) to convert different date formats to a Unix timestamp (although the resulting last 3 digits are not part of the unix timestamp, but are milliseconds).
e.g.:
if (Date.parse("Mon Sep 15 2014 18:20:52 GMT+0200 (CEST)") > Date.parse("2014-09-04T13:57:33.338Z")) {}; // evaluates to true
Do not expect the Date object to correctly or consistently parse date and time strings. There are a number of libraries to help if you have many different formats to support, but if you only have one or two then supporting them is not hard.
To parse an UTC ISO 8601 format string like '2014-09-04T13:57:33.338Z':
function parseISOUTC(s) {
// Get the parts
var b = s.split(/\D+/);
// Return a new Date
return new Date(Date.UTC(b[0], --b[1], b[2], b[3], b[4], b[5], b[6]));
}
To parse a string like 'Mon Sep 15 2014 18:20:52 GMT+0200 (CEST)' takes a little more work:
function parseMDYHMSO(s) {
// Get the parts
var b = s.split(/[ :]/);
// Convert the month name to a number
var months = {jan:0,feb:1,mar:2,apr:3,may:4,jun:5,
jul:6,aug:7,sep:8,oct:9,nov:10,dec:11};
var month = months[b[1].toLowerCase()];
// Get the offset
var sign = s.indexOf('+') == -1? 1 : -1;
var offset = b[7].substr(4,2) * 60;
offset += +b[7].substr(6,2);
// Create a new Date
return new Date(Date.UTC(b[3], month, b[2], b[4], +b[5] + sign*offset, b[6]));
}
So now you have two date objects. If you want to compare just the time part, then get the hours, minutes and seconds of each and compare them:
var d0 = parseISOUTC('2014-09-04T13:57:33.338Z');
// 2014-09-15T16:20:52.000Z
var d1 = parseMDYHMSO('Mon Sep 15 2014 18:20:52 GMT+0200 (CEST)');
d0.getUTCHour(); // 13
d1.getUTCHour(); // 16
d0.getUTCMin(); // 57
d1.getUTCMin(); // 20
d0.getUTCMin(); // 33
d1.getUTCMin(); // 52
You can also just use the plain getHour, getMinute, etc. methods but the UTC ones make it clear that you're using the same offset.
You can also do the above just using the time part with offset and ignore the date. Convert the hours, minutes and seconds to say seconds, apply the offset, allow for day boundaries and compare the result.

Unable to validate double digit month using Javascript

I have an HTML form where I am allowing users to enter a date to print out a report. However, if that date is within 3 days of the current date, I have it set to tell the user that they must wait 3 days. For some reason the code works when I enter something like "09/30/2012" but when I enter "10/01/2012", the error check skips. It seems at though, if it's a double digit month (10, 11, and 12), it complete skips the error check. Please let me know if you have any ideas. Thanks
JS Code:
var date = myForm.SC_date.value;
var d = new Date(date);
var varBegin = (d.getMonth()+1) + "-" + (d.getDate()-3) + "-" + d.getFullYear()
re = /^\d{1,2}\/\d{1,2}\/\d{4}$/;
if (myForm.SC_date.value == "")
window.alert("Please enter the requested date of variance. NOTE: Date must be 3 days prior to today's date.")
//Here is where I am having issues
/*else if(new Date(date) > new Date(varBegin))
window.alert("Invalid date. You must wait at least 3 days before you can request a report.")*/
else if(!myForm.SC_date.value.match(re))
window.alert("Invalid date. Please enter the date as follows: mm/dd/yyyy.")
HTML Code:
<td>Date of Variance </td>
<td colspan="2"><input name="SC_date:*" id="SC_date" type="text" tabindex="06">
</textarea><b><span class="style3">*</span> </b><span class="style2">(mm/dd/yyyy)</span>
</td>
I don't think you want to construct your "3 days ago" date by manipulating a string. I.e., this snippet here:
var varBegin = (d.getMonth()+1) + "-" + (d.getDate()-3) + "-" + d.getFullYear()
First, I'm not sure why you're using hyphens as delimiters here, when you are using forward-slashes as delimiters in your input field?
In any case, that's not a reliable way to construct the date. When you feed a string into the constructor of a Date object, you are effectively calling Date.parse(). That behaves differently on different browsers.
Check this out:
> new Date('1-1-2012');
Sun Jan 01 2012 00:00:00 GMT-0800 (PST)
> new Date('01-01-2012');
Sun Jan 01 2012 00:00:00 GMT-0800 (PST)
> new Date('2012-1-1');
Sun Jan 01 2012 00:00:00 GMT-0800 (PST)
Looks pretty good, right? But that's on Chrome.
Now check out what happens in an up-to-date version of Firefox, with the exact same calls:
> new Date('1-1-2012');
Date {Invalid Date}
> new Date('01-01-2012');
Date {Invalid Date}
> new Date('2012-1-1');
Date {Invalid Date}
> new Date('2012-01-01');
Date {Sat Dec 31 2011 16:00:00 GMT-0800 (PST)}
Furthermore, look at this behavior, in both browsers:
> new Date('2012-01-01');
Sat Dec 31 2011 16:00:00 GMT-0800 (PST)
Simply prepending zeroes to the month and date digits causes a time warp! You have to set the time and a timezone (for me, PST) to make that go away:
> new Date('2012-01-01T00:00:00-08:00')
Sun Jan 01 2012 00:00:00 GMT-0800 (PST)
Basically, dealing with date string parsing is a headache. You don't want to have to digest and account for specs like this, this, and this.
So, here's a better alternative -- pass the year, month, and date values (in that order) to the constructor of the Date object. That will reliably create the date for you, so your comparisons are valid.
Like this, for your specific example:
var WARNING_PERIOD_IN_DAYS = 3;
// Extract month, day, year from form input, 'trimming' whitespace.
var re = /^\s*(\d{1,2})\/(\d{1,2})\/(\d{4})\s*$/;
var match = re.exec(inputVal); // from "myForm.SC_date.value".
if (match) {
var month = parseInt(match[1]) - 1; // Zero-indexed months.
var date = parseInt(match[2]);
var year = parseInt(match[3]);
var inputDate = new Date(year, month, date);
var currentDate = new Date();
var threeDaysAgo = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDay() - WARNING_PERIOD_IN_DAYS);
console.log((inputDate > threeDaysAgo) ? 'Within warning period' : 'No warning needed');
}
Speaking of specs, there's one cool thing to note here, which is that in JavaScript, you can "wrap" the date value (it can be too large, or negative), and the resulting Date will still be valid and correct. Here's why:
From the ECMAScript 262 spec, here's what happens when you call setDate():
**15.9.5.36 Date.prototype.setDate (date)**
1. Let t be the result of LocalTime(this time value).
2. Let dt be ToNumber(date).
3. Let newDate be MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)).
4. Let u be TimeClip(UTC(newDate)).
5. Set the [[PrimitiveValue]] internal property of this Date object to u.
6. Return u.
This is the key bit: MakeDay(YearFromTime(t), MonthFromTime(t), dt)
MakeDay gets the year and the month from the current time value of the Date object (in milliseconds of epoch time), and does this:
**15.9.1.12 MakeDay (year, month, date)**
The operator MakeDay calculates a number of days from its three arguments, which must be ECMAScript Number values. This operator functions as follows:
1. If year is not finite or month is not finite or date is not finite, return NaN.
2. Let y be ToInteger(year).
3. Let m be ToInteger(month).
4. Let dt be ToInteger(date).
5. Let ym be y + floor(m /12).
6. Let mn be m modulo 12.
7. Find a value t such that YearFromTime(t) == ym and MonthFromTime(t) == mn and DateFromTime(t) == 1;
but if this is not possible (because some argument is out of range), return NaN.
8. Return Day(t) + dt - 1.
This looks rather involved, but basically it's:
The floor, modulo, and date==1 bits handle month rollovers (months that are negative or greater than 12).
The resulting instant in epoch time is converted to a number of days.
Your date value is added to that number of days. If your date value is negative, that's fine, it will just be subtracted.
The result is passed back to setDate().
setDate calls MakeDate(), which converts the number of days plus the intra-day time into milliseconds in epoch time.
The Date object's internal time is set to this new epoch time.
That's why you can do stuff like this (comments taken from the MakeDay() function in the V8 JS engine project):
// MakeDay(2007, -4, 20) --> MakeDay(2006, 8, 20)
// MakeDay(2007, -33, 1) --> MakeDay(2004, 3, 1)
// MakeDay(2007, 14, -50) --> MakeDay(2007, 8, 11)
Ok, so that was almost certainly too much detail for this particular problem... but I just wanted to make clear what's really going on behind the scenes. Thanks for your patience.
And... just one last thing...
You have a random </textarea> hanging out in that HTML snippet. If there is an opening <textarea> somewhere before it, then it's incorrectly enclosing some of your other elements. If there is no opening <textarea>, then delete it.
If you don't care about the time of the day, I recommend you do the following:
var dUser = new Date(date); //just like you did before
var dVarBegin = new Date("10/05/2012"); //here you do whatever is the date you are setting.
var diff = dVarBegin.getTime() - dUser.getTime();
//now diff is the difference in milliseconds!
I don't fully understand your requirements in relation to the 3 days. However, if what you needed was to compare dates, now you can! I hope this works for you. If you need something more, please ellaborate a little bit on the 3 days thing.
Use a library that lets you control the date formats accepted, e.g. Globalize.js or Date.js. Then define the exact test you wish to carry out especially whether time of the day is significant and whether the test should be relative to current time in user’s system (which is what you get with new Date() without arguments). You can then e.g. calculate a time difference as outlined by #Mamsaac and convert milliseconds to days with simple arithmetic.
It is illogical to use Date() and then, without checking the result, start doing pattern matching on the input. Moreover, Date() is by definition system-dependent and should seldom be used. There is no guarantee that it will accept a format like mm/dd/yyyy at all.

What range of dates are permitted in Javascript?

What is the maximum and the minimum date that I can use with the Date object in Javascript?
Is it possible to represent ancient historical dates (like January 1, 2,500 B.C.) or dates that are far into the future (like October 7, 10,000)?
If these far-from-present dates can't be represented with the Date object, how should I represent them?
According to §15.9.1.1 of the ECMA-262 specification,
Time is measured in ECMAScript in milliseconds since 01 January, 1970 UTC.
...
The actual range of times supported by ECMAScript Date objects is ... exactly –100,000,000 days to 100,000,000 days measured relative to midnight at the beginning of 01 January, 1970 UTC. This gives a range of 8,640,000,000,000,000 milliseconds to either side of 01 January, 1970 UTC.
So the earliest date representable with the Date object is fairly far beyond known human history:
new Date(-8640000000000000).toUTCString()
// Tue, 20 Apr 271,822 B.C. 00:00:00 UTC
The latest date is sufficient to last beyond Y10K and even beyond Y100K, but will need to be reworked a few hundred years before Y276K.
new Date(8640000000000000).toUTCString()
// Sat, 13 Sep 275,760 00:00:00 UTC
Any date outside of this range will return Invalid Date.
new Date(8640000000000001) // Invalid Date
new Date(-8640000000000001) // Invalid Date
In short, the JavaScript Date type will be sufficient for measuring time to millisecond precision within approximately 285,616 years before or after January 1, 1970. The dates posted in the question are very comfortably inside of this range.

Minimum and maximum date

I was wondering which is the minimum and the maximum date allowed for a Javascript Date object. I found that the minimum date is something like 200000 B.C., but I couldn't get any reference about it.
Does anyone know the answer? I just hope that it doesn't depend on the browser.
An answer in "epoch time" (= milliseconds from 1970-01-01 00:00:00 UTC+00) would be the best.
From the spec, §15.9.1.1:
A Date object contains a Number indicating a particular instant in time to within a millisecond. Such a Number is called a time value. A time value may also be NaN, indicating that the Date object does not represent a specific instant of time.
Time is measured in ECMAScript in milliseconds since 01 January, 1970 UTC. In time values leap seconds are ignored. It is assumed that there are exactly 86,400,000 milliseconds per day. ECMAScript Number values can represent all integers from –9,007,199,254,740,992 to 9,007,199,254,740,992; this range suffices to measure times to millisecond precision for any instant that is within approximately 285,616 years, either forward or backward, from 01 January, 1970 UTC.
The actual range of times supported by ECMAScript Date objects is slightly smaller: exactly –100,000,000 days to 100,000,000 days measured relative to midnight at the beginning of 01 January, 1970 UTC. This gives a range of 8,640,000,000,000,000 milliseconds to either side of 01 January, 1970 UTC.
The exact moment of midnight at the beginning of 01 January, 1970 UTC is represented by the value +0.
The third paragraph being the most relevant. Based on that paragraph, we can get the precise earliest date per spec from new Date(-8640000000000000), which is Tuesday, April 20th, 271,821 BCE (BCE = Before Common Era, e.g., the year -271,821).
To augment T.J.'s answer, exceeding the min/max values generates an Invalid Date.
let maxDate = new Date(8640000000000000);
let minDate = new Date(-8640000000000000);
console.log(new Date(maxDate.getTime()).toString());
console.log(new Date(maxDate.getTime() - 1).toString());
console.log(new Date(maxDate.getTime() + 1).toString()); // Invalid Date
console.log(new Date(minDate.getTime()).toString());
console.log(new Date(minDate.getTime() + 1).toString());
console.log(new Date(minDate.getTime() - 1).toString()); // Invalid Date
A small correction of the accepted answer; the year of the minimum date is actually 271,822 BCE, as you can see when running the following snippet:
console.log(new Date(-8640000000000000).toLocaleString("en", {"year": "numeric", "era": "short"}))
Indeed, year -271,821 is actually 271,822 BCE because JavaScript's Date (along with ISO 8601) uses astronomical year numbering, which uses a year zero.
Thus, year 1 is 1 CE, year 0 is 1 BCE, year -1 is 2 BCE, etc.
var startingDate = new Date().toISOString().split('T')[0] + "T00:00:00.001Z"; // for 2022-07-25 00 00 00
var endingDate = new Date().toISOString().split('T')[0] + "T23:59:59.999Z" ;
As you can see, 01/01/1970 returns 0, which means it is the lowest possible date.
new Date('1970-01-01Z00:00:00:000') //returns Thu Jan 01 1970 01:00:00 GMT+0100 (Central European Standard Time)
new Date('1970-01-01Z00:00:00:000').getTime() //returns 0
new Date('1970-01-01Z00:00:00:001').getTime() //returns 1

Categories