Date_parseISO8601String その3

眠れないのに、頭は寝ぼけてる。あぁ〜。

<!DOCTYPE html>
<title>Date_parseISO8601String</title>

<style type="text/css">
table , td {
  border :1px #ccc inset;
}
th {
  border :1px #ccc outset;
  background : #eee;
}
</style>

<body>
<h1>Date_parseISO8601String() </h1>
<table>
<tr>
<th rowspan="1">DateTime<th>YYYY<th>MM<th>DD<th>HH<th>MN<th>SS<th>MS<th>Date ISO 8601


<script>
// Date_parseISO8601String

if ('undefined' === typeof Array.prototype.map)
  Array.prototype.map = function (a) {
    if ('function' !== typeof a)
      throw TypeError (a + ' is not a function.');
    for (var b = 0, c = this.length, d = new Array(c), e = arguments[1]; b < c; b++)
      if (b in this) d[b] = a.call (e, this[b], b, this);
    return d
  };

(function () {
  var YY = '[0-9]{6}|[0-9]{4}';
  var MM = '[0-1][0-9]';
  var DD = '[0-3][0-9]';
  var MMDD = '-('+ MM +')?(?:-('+ DD +'))?';
  var WwwD = '-W([0-5][0-9])(?:-([1-7]))?';
  var DDD  = '-([0-3][0-9][0-9])';
  var DATE = '(([+-])?('+ YY +')?(?:'+ MMDD + '|' + WwwD + '|' + DDD +')?)';
  var HH  = '[0-2][0-9]';
  var HHd = '[0-2][0-9](?:\\.[0-9]*)?';
  var MN  = '[0-5][0-9]';
  var MNd = '[0-5][0-9](?:\\.[0-9]*)?';
  var SSd  = '(?:[0-5][0-9])?(?:\\.[0-9]*)?';
  var TIME = '(('+ HHd +')|('+ HH +')?:('+ MNd +')?|('+ HH + ')?:('+ MN +')?:('+ SSd +'))?';
  var TZ = '([+-])([0-2][0-9]):(00|30|45)';
  var UTC = 'Z';
  var HTML5 = '^(?:' + DATE + ')?(?:T' + TIME + ')?('+ TZ +')?('+ UTC +')?$';
  var date_time_tz_reg = new RegExp (HTML5);

  var maxDay = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  var tzOffset = (new Date).getTimezoneOffset ();


  var num = function (str) {
    return str ? Number (str): 0;
  };
  

  var parse = function (dateString) {
    var dt = date_time_tz_reg.exec (dateString);
    var result;
    var y, m, d, h, n, s, ms, offset;
    var w, ws, wOffset;
    var len;
    var leap_year;
    
    while (dt) {
      if (dt[1]) { // DATE
        // YY, [+-]Y6, Yn
        result = new Date;
        if (dt[3]) {
          len = dt[3].length;
          if ((dt[2]) && (6 !== len)) break;
          y = num ((dt[2] || '') + dt[3]);
          if (0 === y) break;

          result.setFullYear (y);
          leap_year = !(y % 4) ^ !(y % 100) ^ !(y % 400); //閏年
        }
        
        // MM
        if (dt[4]) {
          m = num (dt[4]);
          if ((m < 1) || (12 < m)) break;
          m -= 1;
          result.setMonth (m);
        }
        
        // DD
        if (dt[5]) {
          d = num (dt[5]);
          if (d < 1) break;
          if ((maxDay[m] + leap_year) < d) break;
          result.setDate (d);
        }
        
        // WwwD
        if (dt[6]) {
          w = num (dt[6]);
          if ((w < 1) || (53 < w)) break;
          result.setMonth (0);
          result.setDate (1);
          ws = result.getDay ();
          d = num (dt[7]) || 1;
          wOffset = (ws <= 4 ? - ws: 7 - ws);
          d = w * 7 + d + wOffset - 7;
          if (365 + leap_year + wOffset < d) break;
          result.setDate (d);
        }
        
        // DDD
        if (dt[8]) {
          d = num (dt[8]);
          if (d < 1) break;
          if ((365 + leap_year) < d) break;
          result.setMonth (0);
          result.setDate (1);
          result.setDate (d);
        }
        
      }      
      else {
        result = new Date (0, 0);
      }
      
      if (dt[9]) { // TIME
        h = n = s = 0;
        
        if (dt[10]) h = num (dt[10]);
        if (dt[11]) h = num (dt[11]);
        if (dt[12]) n = num (dt[12]);
        if (dt[13]) h = num (dt[13]);
        if (dt[14]) n = num (dt[14]);
        if (dt[15]) s = num (dt[15]);
        if (24 < h) break;
        ms = ((h * 60 + n) * 60 + s) * 1000;
        if (86400000 < ms) break;
      }
      
      else {
        ms = 0;
      }
      
      offset = (dt[20]) ? tzOffset: 0;
        
      if (dt[16]) {
        if ((dt[20]) && ('-' === dt[17])) break;
        h = num (dt[17] + dt[18]);
        if (h < -12 || 14 < h) break;
        n = num (dt[17] + dt[19]);
        offset += h * 60 + n;
      }
      ms += offset * 60000;
        result.setHours (0);
        result.setMinutes (0);
        result.setSeconds (0);
      result.setMilliseconds (ms);
      
 //     return dt;
      return result;
    }

    return null;
  };
  

  //________
  
  this.Date_parseISO8601String = parse;
})();

[
  '2010-11',
  '2010-11Z',
  '2010-11-25',
  '+002010-11-25',
  '0005-06-07',
  'T::01',
  'T12.5',
  'T23:59.5',
  'T12:34:56.',
  'T12:34:56.789',
  '2010-11-25T12:34:56.789',
  '2010-11-25T12:34:56.789Z',
  '2010-11-25T12:34:56.789+09:00',
  '2010-11-25T12:34:56.789+09:00Z',
  '2010-11-14T::.123',
  '2010-11-14T00:00:00.000Z',
  '2010-11-14T00:00:00.000+01:00',
  '2010-11-14T00:00:00.000+01:30Z',
  '+102010-11-14T12:34:56.789+01:45Z',
  '2010-11T12:34:56.789+01:00Z',
  '+102010-11T12:34:56.789+01:30Z',
  '2010T12:34:56.789+01:45Z',
  '+102010T12:34:56.789+01:30Z',
  '2010-W01',
  '2010-W01-1',
  '+102010-W10-1T12:34:56.789+01:45Z',
  '2010-110',
  '+102010-110T12:34:56.789+01:00Z',
  '2010-11-25T12:34:56Z',
  '--30',
  '-12-01'

].map (function (t) {
  var d = Date_parseISO8601String (t);
  var a = (d instanceof Date)
    ? [
        t,
        d.getFullYear (),
        d.getMonth () + 1,
        d.getDate (),
        d.getHours (),
        d.getMinutes (),
        d.getSeconds (),
        d.getMilliseconds ()
      ]
    : d;
  document.write ('<tr><td>' + (a||[t]).join ('<td>'));
});
</script>
</table>


http://www.virtual-tech.net/blog/2009/01/javascript-iso8601java.html
途中で、こういうやりかたを考えていたのだけれど。

ECMA-262 Page.168 (178 of 252) 抜粋

15.9.1.15 Date Time String Format

ECMAScript defines a string interchange format for date-times based upon a simplification of the ISO 8601 Extended Format. The format is as follows: YYYY-MM-DDTHH:mm:ss.sssZ

Where the fields are as follows:

YYYY is the decimal digits of the year in the Gregorian calendar.
- “:” (hyphon) appears literally twice in the string.
MM is the month of the year from 01 (January) to 12 (December).
DD is the day of the month from 01 to 31.
T “T” appears literally in the string, to indicate the beginning of the time element.
HH is the number of complete hours that have passed since midnight as two decimal digits.
: “:” (colon) appears literally twice in the string.
mm is the number of complete minutes since the start of the hour as two decimal digits.
ss is the number of complete seconds since the start of the minute as two decimal digits.
. “.” (dot) appears literally in the string.
sss is the number of complete milliseconds since the start of the second as three decimal digits.
Both the “.” and the milliseconds field may be omitted.
Z is the time zone offset specified as “Z” (for UTC) or either “+” or “-” followed by a time expression hh:mm

This format includes date-only forms:

YYYY
YYYY-MM
YYYY-MM-DD

It also includes time-only forms with an optional time zone offset appended:

THH:mm
THH:mm:ss
THH:mm:ss.sss

Also included are “date-times” which may be any combination of the above.
All numbers must be base 10.
Illegal values (out-of-bounds as well as syntax errors) in a format string means that the format string is not a valid instance of this format.

NOTE 1 As every day both starts and ends with midnight, the two notations 00:00 and 24:00 are available to distinguish the two midnights that can be associated with one date. This means that the following two notations refer to exactly the same point in time: 1995-02-04T24:00 and 1995-02-05T00:00

NOTE 2 There exists no international standard that specifies abbreviations for civil time zones like CET, EST, etc. and sometimes the same abbreviation is even used for two very different time zones. For this reason, ISO 8601 and this format specifies numeric representations of date and time.


15.9.1.15.1 Extended years

ECMAScript requires the ability to specify 6 digit years (extended years); approximately 285,616 years, either forward or backward, from 01 January, 1970 UTC. To represent years before 0 or after 9999, ISO 8601 permits the expansion of the year representation, but only by prior agreement between the sender and the receiver. In the simplified ECMAScript format such an expanded year representation shall have 2 extra year digits and is always prefixed with a + or &#8211; sign. The year 0 is considered positive and hence prefixed with a + sign.

「THH」 の書式はなかった? ^^;
ループするわけではないけれど、while () にして、範囲の判定で break すのは、どこかで使えるかな。