正規表現を使うの何年振りだろ

前回の続き。
選択されたテキストの文字列が日付かどうかを判定する部分を作成中。
もし年がなくても使えるように「年月日」の形式「月日」の形式にあっているかどうかを調べたい。いきなりDateオブジェクトを生成すると、とんでもないことになりそうなので、とりあえずその文字列のチェックを行う。
ここで正規表現の登場。いやあ、何年振りだろ。しかも、正規表現は sed でしか使ったことがないので、Adobeアプリケーションでは初めてである。このために前回の「DTPの勉強部屋」に参加したんだよ(ウソウソ
せうぞーさんのテキストとにらめっこしながら、こんなんでいいのかな。
まず「月日」の形式

 (1[012]|[1-9])[^\d]+(0?[1-9]|[12][0-9]|3[01])[^\d]*

\d と [0-9] が混在してるけど気にしないように(^^; 慣れてないんだからっ。多分これで「11月21日」でも「11.21」でも「11/21」でもマッチするはず。おっと「Nov. 21」とかにはマッチしない。ってこれは私の能力では無理。あきらめてください。
「11月31日」にもマッチするけど? って、それはあとで実際に存在する日付かどうかをチェックするルーチンを書くから!
次に「年月日」の形式。って何でこの国には年の表現方法が2つあるんだ。

 (20|[^\d]+)?(\d?\d|元)[^\d]+(1[012]|0?[1-9])[^\d]+(0?[1-9]|[12][0-9]|3[01])[^\d]*

これで「2008年11月21日」にも「’08/11/21」にも「平成20年11月21日」にも「H20.11.21」にも対応できるのかな?
「1998年5月25日(娘の誕生日」にはマッチしない。うーん、ここが悩みどころ。曜日だけならある程度過去の場合でも取得できるんだけど、祝日がねえ。例えば海の日が7月20日から7月の第3月曜日に変わったのって何年だっけ。そこまで精査しようとするとちょっと労力が掛かり過ぎて。
過去は曜日だけにしようかね。それでもどこまでの範囲にしよう。とりあえず1900年からにしておくか。未来も2099年までで。

 (20|19|[^\d]+)?(\d?\d|元)[^\d]+(1[012]|0?[1-9])[^\d]+(0?[1-9]|[12][0-9]|3[01])[^\d]*

ということで。元号を絞り込むかどうかも迷いどころ。M・T・S・Hの全半角を含めて絞り込もうとすると頭の中が混乱します。
201年や196年にもマッチするよーと思われたあなた。鋭い!(って私が抜けてるだけなのかな) 元号の1桁にも対応しようとすると、この形しか思いつかなくて。そのあたりの年はあとではじくということで。
本当にこんなんでいいのかな。まだ抜けてそうな気がするんだけど。とりあえず今日はここまで!


11月26日 追記
せうぞーさんがいっぱいコメントを付けてくれました。続きは「名もないテクノ手」へ。

『正規表現を使うの何年振りだろ』へのコメント

  1. 名前:せうぞー 投稿日:2008/11/22(土) 13:10:14 ID:101dc1343 返信

    Date.parseよりも広い範囲で文字列を日付型にしようってことですね。
    いくつかの種類に分類して、計算しないといけないですね。
    おもしろそう。あとで少しやってみます。

  2. 名前:せうぞー 投稿日:2008/11/22(土) 14:39:48 ID:101dc1343 返信

    普通、日付文字列をパースさせる時って、解釈しやすいように考えてあげていたので、いまさらながら日付フォーマットについて考えるとクラクラしますね。
    http://www.kanzaki.com/docs/html/dtf.html
    InDesignのJSだと
    new Date(mm/dd/yyyy);
    を解釈するみたい。ありえない日付もこの形式で解釈してしまうので、融通がきかない。
    誰かがすでに書いているような気が少しします。もうすこし探してみます。

  3. 名前:せうぞー 投稿日:2008/11/22(土) 14:53:56 ID:101dc1343 返信

    連投すいません。
    W3C-DTFのフォーマットが使えるライブラリ
    http://www.kawa.net/works/js/date/w3cdtf.html
    http://www.kawa.net/works/js/date/w3cdtf.html
    テストちう。

  4. 名前:せうぞー 投稿日:2008/11/22(土) 15:26:49 ID:101dc1343 返信

    http://www.hesperus.net/javascript/w3cdtf.shtml
    これがなんとなくアバウトな感じで動いてくれます。これを改造してみるのがいいかもしれない。
    夕ごはんのお買い物行かなくちゃ。。。

  5. 名前:せうぞー 投稿日:2008/11/23(日) 01:12:34 ID:465ad0003 返信

    こんな感じで書き換えてみました。
    function zen2han(str) {
    var tmp_data = '';
    for (var i =0; i< str.length; i++) {
    var c = str.charCodeAt(i);
    if (c >= 65296 && c <= 65305) {
    tmp_data += String.fromCharCode(c – 65248);
    }else {
    tmp_data += str.charAt(i);
    }
    }
    return tmp_data;
    }
    Date.str2date = function(str) {
    var str = zen2han(str);
    var returnValue = new Date(1900, 0, 1);
    var granularity = 0;
    var tzd = 0;
    var offset = 0;
    var matched = null;
    var yyyy = '(19[0-9]{2}|20[0-9]{2})[-年/\\. ]+';
    var mm = '(1[012]|0?[1-9])[-月/\\. ]+';
    var dd = '(0?[1-9]|[12][0-9]|3[01])[日 ]?';
    var hh = 'T?([01]?[0-9]|2[0-3])[-:時/\\. ]+';
    var min = '([0-5]?[0-9])[-:分/\\. ]+';
    var sec = '([0-5]?[0-9])(?:\\.[0-9]+)?[-:秒/\\. ]?';
    var timezone = '(?:Z|TZD|GMT)? ?([+-][0-2][0-9]):?([0-5][0-9])';
    if (matched = str.match(RegExp (yyyy + mm + dd + hh + min + sec + timezone))) {
    granularity = 6;
    tzd = 1;
    } else if (matched = str.match(RegExp (yyyy + mm + dd + hh + min + sec + 'Z'))) {
    granularity = 6;
    tzd = 2;
    } else if (matched = str.match(RegExp (yyyy + mm + dd + hh + min + timezone))) {
    granularity = 4;
    tzd = 1;
    } else if (matched = str.match(RegExp (yyyy + mm + dd + hh + min + 'Z'))) {
    granularity = 4;
    tzd = 2;
    } else if (matched = str.match(RegExp (yyyy + mm + dd))) {
    granularity = 3;
    } else if (matched = str.match(RegExp (yyyy + mm))) {
    granularity = 2;
    } else if (matched = str.match(RegExp (yyyy))) {
    granularity = 1;
    }
    $.writeln (matched);
    switch (granularity) {
    case 6:
    case 5:
    returnValue.setSeconds(parseInt(matched[6], 10));
    case 4:
    returnValue.setMinutes(parseInt(matched[5], 10));
    returnValue.setHours(parseInt(matched[4], 10));
    case 3:
    returnValue.setDate(parseInt(matched[3], 10));
    case 2:
    returnValue.setMonth(parseInt(matched[2], 10) – 1);
    case 1:
    returnValue.setFullYear(parseInt(matched[1], 10));
    }
    switch (tzd) {
    case 1:
    offset = parseInt(matched[matched.length – 2], 10);
    offset = 60 * offset + ((offset < 0) ? -1 : 1) * parseInt(matched[matched.length – 1], 10);
    case 2:
    returnValue.setMinutes(returnValue.getMinutes() – offset – returnValue.getTimezoneOffset());
    }
    return returnValue;
    }
    var str = '';
    str = "1967年3月1日";
    str = "2008-11-23";
    str = "2005-04-23T17:20:00+09:00";
    str = "1964年";
    str = "1998年5月25日(娘の誕生日)";
    var dateValue = Date.str2date(str);
    dateValue;

  6. 名前:せうぞー 投稿日:2008/11/23(日) 02:08:34 ID:465ad0003 返信

    とりあえず、明治〜平成まで考えてみました。
    function zen2han(str) {
    var tmp_data = '';
    for (var i =0; i< str.length; i++) {
    var c = str.charCodeAt(i);
    if (c >= 65296 && c <= 65305) {
    tmp_data += String.fromCharCode(c – 65248);
    }else {
    tmp_data += str.charAt(i);
    }
    }
    return tmp_data;
    }
    function wareki2seireki(str) {
    var wareki = 0;
    var seireki = 0;
    var matched = null;
    if (matched = str.match(/(明治?|[MMmm][\.・]?)[  ]?([0-9元]+)/)) {
    wareki = parseInt(matched[2].replace('元', '1'));
    seireki = '' + (wareki + 1867);
    return str.replace(matched[0], seireki);
    } else if (matched = str.match(/(大正?|[TTtt][\.・]?)[  ]?([0-9元]+)/)) {
    wareki = parseInt(matched[2].replace('元', '1'));
    seireki = '' + (wareki + 1911);
    return str.replace(matched[0], seireki);
    } else if (matched = str.match(/(昭和?|[SSss][\.・]?)[  ]?([0-9元]+)/)) {
    wareki = parseInt(matched[2].replace('元', '1'));
    seireki = '' + (wareki + 1925);
    return str.replace(matched[0], seireki);
    } else if (matched = str.match(/(平成?|[HHhh][\.・]?)[  ]?([0-9元]+)/)) {
    wareki = parseInt(matched[2].replace('元', '1'));
    seireki = '' + (wareki + 1988);
    return str.replace(matched[0], seireki);
    } else {
    return str;
    }
    }
    Date.str2date = function(str) {
    var str = zen2han(str);
    var str = wareki2seireki(str);
    var returnValue = new Date(1900, 0, 1);
    var granularity = 0;
    var tzd = 0;
    var offset = 0;
    var matched = null;
    var yyyy = '(19[0-9]{2}|20[0-9]{2})[-年/\\. ]+';
    var mm = '(1[012]|0?[1-9])[-月/\\. ]+';
    var dd = '(0?[1-9]|[12][0-9]|3[01])[日 ]?';
    var hh = 'T?([01]?[0-9]|2[0-3])[-:時/\\. ]+';
    var min = '([0-5]?[0-9])[-:分/\\. ]+';
    var sec = '([0-5]?[0-9])(?:\\.[0-9]+)?[-:秒/\\. ]?';
    var timezone = '(?:Z|TZD|GMT)? ?([+-][0-2][0-9]):?([0-5][0-9])';
    if (matched = str.match(RegExp (yyyy + mm + dd + hh + min + sec + timezone))) {
    granularity = 6;
    tzd = 1;
    } else if (matched = str.match(RegExp (yyyy + mm + dd + hh + min + sec + 'Z'))) {
    granularity = 6;
    tzd = 2;
    } else if (matched = str.match(RegExp (yyyy + mm + dd + hh + min + timezone))) {
    granularity = 4;
    tzd = 1;
    } else if (matched = str.match(RegExp (yyyy + mm + dd + hh + min + 'Z'))) {
    granularity = 4;
    tzd = 2;
    } else if (matched = str.match(RegExp (yyyy + mm + dd))) {
    granularity = 3;
    } else if (matched = str.match(RegExp (yyyy + mm))) {
    granularity = 2;
    } else if (matched = str.match(RegExp (yyyy))) {
    granularity = 1;
    }
    switch (granularity) {
    case 6:
    case 5:
    returnValue.setSeconds(parseInt(matched[6], 10));
    case 4:
    returnValue.setMinutes(parseInt(matched[5], 10));
    returnValue.setHours(parseInt(matched[4], 10));
    case 3:
    returnValue.setDate(parseInt(matched[3], 10));
    case 2:
    returnValue.setMonth(parseInt(matched[2], 10) – 1);
    case 1:
    returnValue.setFullYear(parseInt(matched[1], 10));
    }
    switch (tzd) {
    case 1:
    offset = parseInt(matched[matched.length – 2], 10);
    offset = 60 * offset + ((offset < 0) ? -1 : 1) * parseInt(matched[matched.length – 1], 10);
    case 2:
    returnValue.setMinutes(returnValue.getMinutes() – offset – returnValue.getTimezoneOffset());
    }
    return returnValue;
    }
    var str = '';
    str = "1967年3月1日";
    str = "2008-11-23";
    str = "2005-04-23T17:20:00+09:00";
    str = "1964年";
    str = "1998年5月25日(娘の誕生日)";
    str = "昭和20年8月15日は終戦記念日";
    var dateValue = Date.str2date(str);
    dateValue;

  7. 名前:お~まち 投稿日:2008/11/23(日) 11:47:20 ID:d0d831d07 返信

    パソコン立ち上げてびっくり。
    せうぞーさんすごいなー。
    私が買ったばかりのiPod nanoが自宅のパソコン(Windows 2000)に繋げられないという事実を知ってかなり落ち込んでいる間にこんなに書き込まれていたなんて。
    しかもかなり長い。頭がくらくらします。こんなに大変なことだったとは。