質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

5回答

6591閲覧

Dateオブジェクトはシリアライザブルですか?

退会済みユーザー

退会済みユーザー

総合スコア0

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

4クリップ

投稿2017/08/21 23:37

文字列や配列、オブジェクト、booleanはシリアライザブルである認識なのですが、Dateオブジェクトのインスタンスはシリアライザブルでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/08/21 23:44

jsonの話ですか? であれば、jsonの仕様を見れば分かると思いますが、できないはず。
guest

回答5

0

JavaScript のタグなので

シリアライザブル = JSON

として回答します。Date オブジェクトはシリアライザブルではありません。一般的に JSON に日付を出力する際は

  • ISO 8601 形式の文字列
  • RFC3339 形式の文字列
  • epoch 秒数

のいずれかを格納する事が多くいです。その中でも見ただけで日付と分かり、多くの JSON パーサがサポートしている RFC3339 を使うのが良いと思います。

2017-08-22T23:59:60Z

投稿2017/08/21 23:51

mattn

総合スコア5030

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

think49

2017/08/22 01:33

Google Chrome 60.0.3112.101 では、ISO 8601 基本形式をパース出来ませんでした。 new Date("20170822T090000+0900"); // Invalid Date new Date("20170822T000000Z"); // Invalid Date new Date("2017-08-22T09:00:00+09:00"); // Tue Aug 22 2017 09:00:00 GMT+0900 (東京 (標準時)) new Date("2017-08-22T00:00:00Z"); // Tue Aug 22 2017 09:00:00 GMT+0900 (東京 (標準時))
mattn

2017/08/22 01:39

はい。ISO 8601 はフォーマットとしての縛りが緩いので。RFC3339 とそれ以外という意味で分けました。RFC3339 は年が4桁固定ですが ISO 8601 は定義が緩かったりなど。
mattn

2017/08/22 01:43

そうですね。回答に書かせて頂いた通りパース可能な RFC3339 を使うのがいいと思います。余談ですが稀に `"date:エポック秒数` というヘンテコなプレフィックスを付けているプロダクトなども見ます。
think49

2017/08/23 16:27 編集

ISO 8601 は一次情報が有料の為、読めなかったのですが、 http://www.ecma-international.org/ecma-262/8.0/#sec-date-time-string-format によれば、「ISO 8601 拡張形式」とある為、「ISO 8601 拡張形式」と表現すれば表記揺れはないものと認識していました。 先程のパース可能な文字列も、「ISO 8601 拡張形式」と表現して差し支えないものと認識しています。 回答の際には仕様のURLも併記している為、該当リンク先を読めば間違いはないと考えていましたが、表記揺れの件はやや気になりますね。 ISOのサイトへ行くと、「ISO 8601:2004」のようにバージョン番号が割り振られていたので、「ISO 8601」だけでは正確に仕様を指し示せてないのかもしれませんが…。 https://www.iso.org/standard/40874.html
think49

2017/08/22 15:28

RFC3339 では "この情報はISO 8601の1988年のバージョンに基づいています。2000年バージョンに若干の変更があるかもしれません。" と書かれているので、最近の「ISO 8601」では表記揺れが改善されている可能性があるようですね…。 http://www5d.biglobe.ne.jp/stssk/rfc/rfc3339j.html#05
think49

2017/08/24 12:59 編集

> https://bugzilla.mozilla.org/show_bug.cgi?id=1265136 私の英語力の問題かも知れませんが、MM/DD/YY (米国形式)の new Date() 結果がFirefoxとGoogle Chromeで異なる問題を話し合っているように読めました。 ECMAScript 2017定義のISO 8601拡張形式とは異なりますが、オリジナルのISO 8601には米国書式も定義されているのでしょうか。 (国際規格なので、特定の区域の書式は定義されていないものと認識していました)
mattn

2017/08/24 11:39

ISO 8601 は基本書式で2桁省略表記を持っているので ISO 8601 では valid になってしまい、結果米国形式の parse も出来た他のブラウザと動作が異なったというバグです。MDN で YYYY-MM-DD 表記を推奨する様になったきっかけのバグの様です。
退会済みユーザー

退会済みユーザー

2017/08/24 12:05

ありがとうございます!コメント欄も含め、大変参考になりました!!!
think49

2017/08/24 13:16

> ISO 8601 は基本書式で2桁省略表記を持っているので ISO 8601 では valid になってしまい なるほど、そういう事でしたか。理解できました。 仕様を遡ると、ECMAScript で "ISO 8601" の単語が出てきたのは ECMAScript 5.1 からでした(ECMAScript 3 には存在しません)。 http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15 http://www.ecma-international.org/ecma-262/6.0/#sec-date-time-string-format http://www.ecma-international.org/ecma-262/7.0/#sec-date-time-string-format http://www.ecma-international.org/ecma-262/8.0/#sec-date-time-string-format ECMAScript 5.1~ECMAScript 8 (ECMAScript 2017) に至るまで、いずれも「2桁省略表記」を定義していませんでした。 ですので、yy/mm/dd(ISO 8601)は「ECMAScript 定義の ISO 8601拡張形式」ではなく、「ISO が定義する ISO 8601 書式」ではある、というややこしい状況になっているようです。 本当にややこしいですね。 説明していて理解してもらえるか不安になるぐらいに…。
guest

0

JSONでは無理です。

###JSONは無理

ES5準拠のほとんどのブラウザであれば、Date.prototype.toJSONやJSON.stringifyによって文字列化してシリアライズし、デシリアライズではその文字列をDate.parseやnew Dateで読み取ることはできるでしょう。(古いブラウザではできません。また、なにやら実装ずれがあるようで、最新のブラウザでも全てのブラウザでできるとは限らないようです。)

JavaScript

1const date = new Date(); 2console.log(date); 3console.log(date.constructor); 4const json = JSON.stringify(date); 5console.log(json); 6const newDate = new Date(JSON.parse(json)); 7console.log(newDate); 8console.log(newDate.constructor)

しかし、この方法には致命的な欠点があります。それは、日時っぽい文字列と日時を表す文字列を区別できないと言うこと、つまりは、それが日時として解釈して良いのか判断付かないと言うことです。

実際に.toJSON()やJSON.stringify()した場合、"2017-08-22T14:58:32.352Z"という文字列になります。JSON上では、これが元々Dateを表していたのか、それとも初めからこういう文字列だったのかを判断することはできません。JSONにはそういった日時のデータの場合の表し方が規定されておらず、単なる文字列としてしか表現しようがないからです。

何が言いたいかというと、JSONだけでは、JSONの中のデータがDateのような日時を表すデータであると言うことを示すことができないと言うことです。それではオブジェクトを正確にシリアライズ化できたのは言えないと思います。

JavaScript

1const date = '2017-08-22T14:58:32.352Z'; 2console.log(date); 3console.log(date.constructor); 4const json = JSON.stringify(date); 5console.log(json); 6const newDate = new Date(JSON.parse(json)); 7console.log(newDate); 8console.log(newDate.constructor)

最初に渡しているものが文字列なのに、同じようにDateになってしまいます。

###YAMLでやってみよう

では、どうするか。答えは簡単です。JSONで無ければ良いのです。日時データを日時データとして扱えるようなフォーマット形式、例えばYAMLやBSONを使えば良いのです。js-yamlを使用したYAMLの場合は次のようになります。(import文を使用していますので、対応していない環境ではBabelを使って変換してください)

JavaScript

1import YAML from 'js-yaml'; 2const date = new Date(); 3console.log(date); 4console.log(date.constructor); 5const yaml = YAML.dump(date); 6console.log(yaml); 7const newDate = YAML.load(yaml); 8console.log(newDate); 9console.log(newDate.constructor)

もし、Dateではなく文字列を渡した場合も見てください。

JavaScript

1import YAML from 'js-yaml'; 2const date = '2017-08-22T14:58:32.352Z'; 3console.log(date); 4console.log(date.constructor); 5const yaml = YAML.dump(date); 6console.log(yaml); 7const newDate = YAML.load(yaml); 8console.log(newDate); 9console.log(newDate.constructor)

文字列が文字列のままであったことが確認できると思います。これは、YAMLでは日付のデータの書き方が規定されており、それと全く同じ文字列とは厳密に区別できるからです。

###それでもJSONを使いたい

それでもやはり、JSONを使いたいと思うかも知れません。その場合は、この部分は日付であるというのが決め打ちされたJSONになることに注意するしかありません。それは、解釈が決め打ちされたJSONであって、汎用的なJSONでは無い、どこに持って行っても同じ解釈をされるJSONではない、と言うことに注意が必要です。

なお、Date.parse等のブラウザ間の実装の違いが気になる場合は、Momemnt.jsを使うことをお勧めします。.NET独自のJSONのDate形式まで対応していますので、どんな形式で作られていようが大抵は対応できます。でも、.NET形式への変換はできないようです。せっかくSurferOnWwwさんが.NETの場合について色々説明してくれているのに、JavaScriptでは実現できる方法が見つけられなくて、ごめんなさい。

ただ、ここの文字列は日時を表すと決め打ちし、読み込ませないと行けないことには変わりありません。

投稿2017/08/22 15:46

編集2017/08/22 15:57
raccy

総合スコア21733

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

think49

2017/08/22 16:39 編集

> それは、日時っぽい文字列と日時を表す文字列を区別できないと言うこと、つまりは、それが日時として解釈して良いのか判断付かないと言うことです。 正直なところ、ご指摘の件は「シリアライズする以上は回避できない問題」だと私は思います。 シリアライズされた文字列をパースする場合、それが定められた文法規則に準拠しているのであれば、パースされるのが通常です。 JSON.parse() もそれがJSONフォーマットに準拠しているのであれば、「文字列」として解釈せず、「JSON」としてパースします。 > では、どうするか。答えは簡単です。JSONで無ければ良いのです。 そうでしょうか。 それが「YAMLのような文字列」か「YAML」かはどうやって判定するのでしょうか。 YAMLの文法に準拠していれば、それは「文字列」ではなく、「YAML」と判定するのではないでしょうか。 それは「Dateのような日時を表すデータ」を日付としてパースするケースと何が違うのでしょう? > その場合は、この部分は日付であるというのが決め打ちされたJSONになることに注意するしかありません。 その通りで、日付パーサは文法に準拠された文字列であれば、日付として決め打ちする以外に手段はないと思います。 ただし、ファイルとして出力されているのであれば、拡張子で判断する方法はあります。
raccy

2017/08/22 22:23

私が言いたかったのは、JavaScript内のあるデータ(文字列内、複雑な配列やObjectなり)を決められた何らかの形式にシリアライズ化したとき、「JSON」であれば日時と文字列は区別できないけど、「YAML」であれば区別できるという話なだけです。 const x = {a: new Date()} と const y = {a: new Date().toString()} があったとして、xとyそれぞれを区別してシリアライズ化できますか?という話だったのです。 ある文字列が何の形式なのか、JSONなのかYAMLなのかBSONなのかそれともXMLか何かなのかをその文字列だけで判断できるかどうかという話では無いと思うのですか?当たり前ですが、JSONではないものをJSON.parseに読み込ませればエラーになりますし、同じように、YAMLではないものをjs-yamlのloadに読み込ませればエラーになります。YAMLに対するいちゃもんがJSONにも言えることなので、それならシリアライズできるものは全て存在しないように思えます。 すいませんが、結局think49さんが何を言いたいのか、よくわかりませんでした。think49さんが考える、Dateはシリアライズできるのかできないのか、できるならどうすればできるのか、できないならなぜできないのかを教えていただけませんでしょうか?
raccy

2017/08/23 09:43

上のコメントの const y = {a: new Date().toString()} は const y = {a: new Date().toISOString()} の間違いです。ごめんなさい。
think49

2017/08/23 11:38

誤解してほしくないのですが、私は raccy さんに「いちゃもん」を付けているわけではありません。 私はいつでも真面目に回答/指摘しています。 > 私が言いたかったのは、JavaScript内のあるデータ(文字列内、複雑な配列やObjectなり)を決められた何らかの形式にシリアライズ化したとき 分かりました。 > think49さんが考える、Dateはシリアライズできるのかできないのか、 私の回答としては「可能」です。 私は本質問において、「日付単体」をシリアライズする場合を想定しており、JSONのような他のフォーマットに組み込んだ「複合型フォーマット」までは想定していませんでした。 質問者さんは「DateオブジェクトをシリアライズしてJSONに組み込む」とは書いていませんので、複合型は新たに質問を立てるべき別の問題と認識しています。 日付単体としてみれば、「ISO 8601 拡張形式 (RFC3339)」でシリアライズし、`new Date` でパースすれば良いので、何の問題もありません。 new Date('2017-08-22T14:58:32.352Z'); パースする対象は「日付文字列一つのみ」であり、それが日付であるという前提の元でパースします。 ですので、私は「日時っぽい文字列と日時を表す文字列を区別できない」が何を主張しているのか、を理解できていませんでした。 JSONに組み込んだ複合型フォーマットの場合の解決法は、回答本文に記載したgithub/qiitaのリンク先を参照して下さい。
退会済みユーザー

退会済みユーザー

2017/08/24 12:05

ありがとうございます!めちゃくちゃ勉強になりました!ありがとうございました!!!
guest

0

ISO 8601 拡張形式 (RFC3339)

既に回答が出ているように、ECMAScript 2017 では、日付と認識可能な文字列として ISO 8601 拡張形式 を定義しています。
ISO 8601 拡張形式は曖昧な部分がある為、RFC3339 でも再定義しているようです。
しかし、ISO 8601 は有料で文書をDLする仕組みのようで対象の曖昧とする部分は確認できませんでした。
下記に各々のリンク先を示します。

この回答では、ECMAScript 2017が定義する「ISO 8061拡張形式」を基準に説明します。
ECMAScript 2017 では YYYY-MM-DDTHH:mm:ss.sssZ もしくは YYYY-MM-DDTHH:mm:ss.sss[+-]HH:mm の形式として「ISO 8601 拡張形式」を定義しています。

  • YYYY … グレゴリオ暦の0000〜9999桁の10進数。
  • MM … 01(1月)から12(12月)までの年の月。
  • DD … 01から31までの月の日。
  • T … 時間要素の始まりを示す文字(訳注: Time の "T" と思われます)
  • HH … 深夜0時から24侍を表した、00から24までの2桁の10進数(24時間法)
  • : 「時間:分:秒」を区切る為の区切り文字(セパレータ)
  • mm … 何時何分における何分を表す00から59までの2桁の10進数
  • ss … 0分から1分までの間に存在する2桁の10進数00から59からなる秒数
  • . … 「秒.ミリ秒」を区切る為の区切り文字(セパレータ)
  • sss 3桁の10進数に構成されるミリ秒
  • Z … UTC日時の場合、文字列の終端を表す(タイムゾーン指定子)
  • [+-]HH:mm … タイムゾーンとなるUTCからの時差を表す。"+09:00" はUTCから「+9時間」の時差を表し、"-01:00" はUTCから「-1時間」の時差を表す(タイムゾーン指定子)

他にも次のルールがあります。

  • ゼロパディングが必須です。"2017-01-01""2017-1-1" のようにゼロを切り詰めて表現する事は出来ません(MUST)。
  • タームゾーン識別子が省略された場合、UTC として扱われます。
  • 後方にある値は一部省略する事が可能です。省略された値は最も小さな値として扱われます。

省略規則はやや特殊な為、コード事例をあげます。

JavaScript

1new Date("2017-08-23T12:00:00.000+09:00").toISOString(); // "2017-08-23T03:00:00.000Z" 2new Date("2017-08-23T12:00:00.000").toISOString(); // "2017-08-23T03:00:00.000Z" 3new Date("2017-08-23T12:00:00").toISOString(); // "2017-08-23T03:00:00.000Z" 4new Date("2017-08-23T12:00").toISOString(); // "2017-08-23T03:00:00.000Z" 5new Date("2017-08-23T12").toISOString(); // RangeError: Invalid time value 6new Date("2017-08-23").toISOString(); // "2017-08-23T00:00:00.000Z" 7new Date("2017-08").toISOString(); // "2017-08-01T00:00:00.000Z" 8new Date("2017").toISOString(); // "2017-01-01T00:00:00.000Z"

HH:mm から HH に省略できない事を除いて、後ろの要素を省略可能な事が分かります。

JSONにシリアライズした日付文字列を組み込む場合

オブジェクト初期化子や配列に格納した new Date にまつわるシリアライズに関しては、非常に複雑でこの回答には書ききれない為、下記リンク先にまとめました。

Re: hayatomo さん

投稿2017/08/23 11:24

編集2017/08/23 11:24
think49

総合スコア18156

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/08/24 12:04

ご回答ありがとうございます!とても勉強になりました!!
guest

0

ベストアンサー

多分hayatomoさんはイケてるプログラマになるために必死に勉強中なのでしょう。
JSON云々じゃなくて、本来の意味が知りたいハズなので、他の回答者さんとは違う視点で回答するよ。

そもそもシリアライザブルとはserializableと書くように、
末尾に「able」の付く単語は○○が可能であるという意味の名詞。
そしてシリアライザブルというのは直列化を指す。
http://www.task-notes.com/entry/20150925/1443150000

厳密な意味で言うとストリームなんだけど、JSON.stringifyのように「後で復号できる文字列」に変換する事もほぼ同義なので、
文字列化してテキストファイルやDB等に保存する行為もシリアライズと呼んでも良さそうだね。
シリアライズ - wikipedia

Dateオブジェクトのインスタンスはシリアライザブルでしょうか?

そもそもシリアライズという言葉自体がJavaの用語であり、JavaScritptの用語ではない。
なので単に「JavaScript」「シリアライズ」と聞いても誰も回答出来ないわけだ。
ちゃんとモノやライブラリの対象といった範囲を示す必要があるわけだね。

残念ながらJSONの様式ではDateは定義されていないけど、
本来の意味に近い「後で復号可能な文字列に変換出来るか否か」で言えばDateはシリアライザブル。
JavaScriptのDateはミリ秒までを記憶するから、YYYY-MM-DDThh:mm:ss.zzzZの形式の文字列にして、後でnew Dateに突っ込めばいい。

その気になれば関数や正規表現のパターンも文字列にして後で復号できるし、
自分で作ったインスタンスもプロパティを退避させて後でnewして復号出来る作りにしておけば良いわけだ。

JavaScript

1var bk_add = function(a, b) { return a + b; }; 2console.log(bk_add(1, 3)); 3// 4 4 5eval("var add = " + bk_add.toString()); 6console.log(add(1, 4)); 7// 5 8 9var re = /hoge/; 10console.log(re); 11// /hoge/ 12 13// ※ただしJSのRegExpの仕様の問題で復号は少し面倒 14console.log(new RegExp(re.toString())); 15// /¥/hoge¥// 復号出来てないのでアウト 16 17console.log(new RegExp("hoge")); 18// /hoge/ こうすれば良いが区切り文字や正規表現フラグの処理を行う必要あり

JSON程メジャーではないが、出来る限り文字列化を頑張るライブラリも存在する。
serialize-javascript - npm


長々と語ったけど、
そもそも通信やファイル保存が目的になるから
「これはシリアル化出来ませんごめんなさい」は通用しない。

お 前 が 工 夫 し て シ リ ア ル 化 す る ん だ よ !!!

そういう視点で見ると、効率はさておき大抵のものはシリアル化することは可能だから、
広義の意味でいうと大抵のものはシリアライザブルと呼べる。

なので他の回答者は一言目に「JSONだと仮定します」って言ったわけ。

投稿2017/08/22 13:46

編集2017/08/22 14:02
miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/08/24 12:03

なるほど!とても参考になりました!意図を汲み取っていただいてありがとうございました!
guest

0

上の「質問への追記・修正、ベストアンサー選択の依頼」にコメントしましたが、もう少し詳しく回答欄に書いておきます。

Dateオブジェクトのインスタンスはシリアライザブルでしょうか?

JavaScript タグが付いているので、質問者さんの言われる「Dateオブジェクト」とは JavaScript の Data オブジェクトのことと理解しています。

Date
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date

「シリアライザブルでしょうか? 」というのは、JSON 文字列にシリアライズできるか・・・すなわち以下の記事で言う value として設定できるかということだとすると、できないということになります。(直接設定できるのは string, number, object, array, true, false, null)

JSON の紹介
http://www.json.org/json-ja.html

ただし、Date を string に変換して JSON 文字列に設定することはできます。

変換は自力で行わなくても、例えば、JavaScript なら JSON.stringify() メソッド、.NET Framework なら DataContractJsonSerializer クラスを利用すれば自動的に変換してくれます。(ただし変換結果は異なります)

JSON.stringify()
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

DataContractJsonSerializer クラス
https://msdn.microsoft.com/ja-jp/library/system.runtime.serialization.json.datacontractjsonserializer(v=vs.110).aspx

JSON.stringify() メソッドを使用した場合、例えば以下のコードを IE11 で実行し Visual Studio のデバッガで結果を見ると、

var dateTime = new Date(2017, 1, 1, 12, 15, 45); var dateObject = { DateTime: dateTime }; var dateJson = JSON.stringify(dateObject);

dateJson = "{"DateTime":"2017-02-01T03:15:45.000Z"}" となります。

(注:Date コンストラクタの引数で月を表す整数値は 0 (1月) から 11 (12月) なので、1 ⇒ 02 という結果になっている。時間の 12 が 03 になるのは Utc に変換されるため)

DataContractJsonSerializer クラスの方は、JSON.stringify() メソッドとは異なり、.NET Framework の DateTime 型は /Date(836406000000+0900)/ のようにシリアル化されます。詳しくは以下の MSDN ライブラリの記事の「高度な情報 / DateTime ワイヤ形式」のセクションの説明を見てください

スタンドアロン JSON のシリアル化
https://msdn.microsoft.com/ja-jp/library/bb412170(v=vs.110).aspx

投稿2017/08/22 04:31

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/08/24 12:05

リンク先も参考になりました!ご回答ありがとうございます。理解が深まりました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問