Во время недавнего опыта написания JS-переводчика я много боролся с внутренней работой дат ECMA / JS. Итак, я полагаю, что я брошу свои 2 цента здесь. Надеемся, что совместное использование этого материала поможет другим с любыми вопросами о различиях между браузерами в том, как они обрабатывают даты.
Все реализации хранят свои значения даты внутри себя как 64-разрядные номера которые представляют собой миллисекунды с 01.01.1970 UTC (GMT - это то же самое, что и UTC). Даты, возникающие после 1/1/1970 00:00:00
, являются положительными числами, а даты предшествуют отрицательным.
Поэтому следующий код дает точный результат во всех браузерах.
Date.parse('1/1/1970');
В моей временной зоне ( EST), результат составляет 18000000, потому что это то, сколько мс составляет 5 часов (это всего 4 часа в летние месяцы). Значение будет отличаться в разных часовых поясах. Все основные браузеры делают это одинаково.
Вот и все. Несмотря на то, что в форматах входных строк есть некоторые отклонения, которые основные браузеры будут анализировать как даты, они по существу интерпретируют их одинаково с часовыми поясами и летней экономией. Один из них - формат ISO 8601. Это единственный формат, специально описанный в спецификации ECMA-262 v.5. Для всех других строковых форматов интерпретация зависит от реализации. По иронии судьбы, это формат, в котором браузеры могут отличаться. Ниже приведен сравнительный вывод Chrome vs Firefox для 1/1/1970 на моей машине с использованием формата строки ISO 8601.
Date.parse('1970-01-01T00:00:00Z'); // Chrome: 0 FF: 0
Date.parse('1970-01-01T00:00:00-0500'); // Chrome: 18000000 FF: 18000000
Date.parse('1970-01-01T00:00:00'); // Chrome: 0 FF: 18000000
toString
для соответствия моему входному значению, если я не укажу альтернативный часовой пояс, который я никогда не делаю. Отсутствие спецификатора должно предполагать местный вход времени. Но здесь ситуация ухудшается, FF обрабатывает краткую форму формата ISO 8601 («YYYY -MM-DD ") по-разному, чем обрабатывает длинную форму (« YYYY-MM-DDTHH: mm: ss: sssZ ») без каких-либо логических причин. Вот результат из FF с длинными и короткими форматами даты ISO без спецификатора часового пояса.
Date.parse('1970-01-01T00:00:00'); // 18000000
Date.parse('1970-01-01'); // 0
Итак, чтобы ответить на вопрос первоначального вопросчика, "YYYY-MM-DD"
- это короткая форма ISO 8601 "YYYY-MM-DDTHH:mm:ss:sssZ"
. Таким образом, это интерпретируется как время UTC, тогда как другое интерпретируется как локальное. Вот почему
console.log(new Date(Date.parse("Jul 8, 2005")).toString());
console.log(new Date(Date.parse("2005-07-08")).toString());
console.log(new Date(Date.parse("Jul 8, 2005")).toString());
console.log(new Date(Date.parse("2005-07-08T00:00:00")).toString());
Суть в этом заключается в синтаксическом анализе строк даты. Единственная строка ISO 8601, которую вы можете безопасно анализировать в браузерах, - это длинная форма. И ВСЕГДА используйте спецификатор «Z». Если вы это сделаете, вы можете спокойно перемещаться между локальным и UTC.
console.log(new Date(Date.parse("2005-07-08T00:00:00Z")).toString());
К счастью, большинство современных браузеров другие форматы ввода одинаково, включая наиболее часто используемые форматы «1/1/1970» и «1/1/1970 00:00:00 AM». Все следующие форматы (и другие) рассматриваются как локальный ввод времени во всех браузерах и конвертируются в UTC перед хранением. Таким образом, они совместимы с кросс-браузером. Выходной код этого кода во всех браузерах в моем часовом поясе одинаковый.
console.log(Date.parse("1/1/1970"));
console.log(Date.parse("1/1/1970 12:00:00 AM"));
console.log(Date.parse("Thu Jan 01 1970"));
console.log(Date.parse("Thu Jan 01 1970 00:00:00"));
console.log(Date.parse("Thu Jan 01 1970 00:00:00 GMT-0500"));
. На стороне вывода все браузеры переводили часовые пояса одинаково, но они обрабатывать строковые форматы по-разному. Вот функции toString
и то, что они выводят. Обратите внимание на то, что функции toUTCString
и toISOString
выводят на моей машине 5:00 AM.
Конвертирует с UTC в локальное время перед печатью
- toString
- toDateString
- toTimeString
- toLocaleString
- toLocaleDateString
- toLocaleTimeString
Прямая печать сохраненного времени UTC
- toUTCString
- toISOString
In Chrome
toString Thu Jan 01 1970 00:00:00 GMT-05:00 (Eastern Standard Time)
toDateString Thu Jan 01 1970
toTimeString 00:00:00 GMT-05:00 (Eastern Standard Time)
toLocaleString 1/1/1970 12:00:00 AM
toLocaleDateString 1/1/1970
toLocaleTimeString 00:00:00 AM
toUTCString Thu, 01 Jan 1970 05:00:00 GMT
toISOString 1970-01-01T05:00:00.000Z
In Firefox
toString Thu Jan 01 1970 00:00:00 GMT-05:00 (Eastern Standard Time)
toDateString Thu Jan 01 1970
toTimeString 00:00:00 GMT-0500 (Eastern Standard Time)
toLocaleString Thursday, January 01, 1970 12:00:00 AM
toLocaleDateString Thursday, January 01, 1970
toLocaleTimeString 12:00:00 AM
toUTCString Thu, 01 Jan 1970 05:00:00 GMT
toISOString 1970-01-01T05:00:00.000Z
Я обычно не используйте формат ISO для ввода строки. Единственный раз, когда использование этого формата полезно для меня, - это когда даты нужно сортировать как строки. Формат ISO можно сортировать как есть, а другие - нет. Если у вас есть совместимость с несколькими браузерами, укажите либо часовой пояс, либо используйте совместимый строковый формат.
Код new Date('12/4/2013').toString()
проходит через следующее внутреннее псевдопреобразование:
"12/4/2013" -> toUCT -> [storage] -> toLocal -> print "12/4/2013"
Надеюсь, этот ответ был полезен.
Как упомянуто, статья Wikipedia о IEEE 754 делает хорошее задание показа, как числа с плавающей точкой хранятся в большинстве систем.
Теперь, вот некоторые общие глюки:
Полное объяснение проблем, окружающих числа с плавающей точкой, дано в статье , Что Каждый Программист Должен Знать Об Арифметике С плавающей точкой .
Стандарт IEEE 754 .
, Конечно, существуют другие средства сохранить числа, когда IEE754 не достаточно хорош. Библиотеки как Java BigDecimal
доступны для большинства платформ и отображаются хорошо на тип числа SQL. Символы могут использоваться для иррациональных чисел, и отношения, которые не могут быть точно представлены в двоичной или десятичной плавающей точке, могут быть сохранены как отношение.
Относительно второй части Вашего вопроса, если производительность и эффективность не важны для Вашего проекта, тогда я предлагаю, чтобы Вы передали данные с плавающей запятой как строку по TCP/IP. Это позволяет Вам избежать проблем, таких как выравнивание байта и упростит отладку.
В основном то, о чем необходимо волноваться в числах с плавающей точкой, - то, что существует ограниченное количество цифр точности. Это может вызвать проблемы при тестировании на равенство, или если для программы на самом деле нужно больше цифр точности, чем, что тот тип данных дают Вам.
В C++, хорошее эмпирическое правило должно думать, что плавание дает Вам 7 цифр точности, в то время как двойное дает Вам 15. Кроме того, если Вы интересуетесь знанием, как протестировать на равенство, можно посмотреть этот поток вопроса.
Да существует Стандарт IEEE для Двоичной Арифметики С плавающей точкой (IEEE 754)
, число разделяется на три части, знак, экспоненту и часть, когда сохранено в двоичном файле.
Этот статья , наделенная правом "Стандарт IEEE, 754 Числа с плавающей точкой" могут быть полезными. Чтобы быть честным, я не абсолютно уверен, что понимаю Ваш вопрос, таким образом, я не уверен, что это будет полезным, но я надеюсь, что это будет.
Если Вы действительно волнуетесь по поводу погрешностей округления с плавающей точкой, большинство языков предлагает типы данных, которые не имеют ошибок с плавающей точкой. SQL Server имеет Типы данных decimal и Типы данных money..Net имеет Тип данных decimal. Они не бесконечная точность как BigDecimal в Java, но они точны вниз к количеству десятичных точек, для которых они определяются. Таким образом, Вы не должны волноваться о долларовой стоимости, Вы вводите как 4,58$, будучи сохраненным как значение с плавающей точкой 4,579999999999997
То, что я помню, является плавающей точкой на 32 бита, хранится с помощью 24 битов для фактического числа, и оставаться 8 битов используются в качестве питания 10, определяя, где десятичная точка.
я немного ржав на предмете tho...
In follow up to this question, it appears that some numbers cannot be represented by floating point at all, and instead are approximated.
Correct.
How are floating point numbers stored? Is there a common standard for the different sizes?
As the other posters already mentioned, almost exclusively IEEE754 and its successor IEEE754R. Googling it gives you thousand explanations together with bit patterns and their explanation. If you still have problems to get it, there are two still common FP formats: IBM and DEC-VAX. For some esoteric machines and compilers (BlitzBasic, TurboPascal) there are some odd formats.
What kind of gotchas do I need to watch out for if I use floating point? Are they cross-language compatible (ie, what conversions do I need to deal with to send a floating point number from a python program to a C program over TCP/IP)?
Practically none, they are cross-language compatible.
Very rare occuring quirks:
IEEE754 defines sNaNs (signalling NaNs) and qNaNs (quiet NaNs). The former ones cause a trap which forces the processor to call a handler routine if loaded. The latter ones don't do this. Because language designers hated the possibility that sNaNs interrupt their workflow and supporting them enforce support for handler routines, sNaNs are almost always silently converted into qNaNs. So don't rely on a 1:1 raw conversion. But again: This is very rare and occurs only if NaNs are present.
You can have problems with endianness (the bytes are in the wrong order) if files between different computers are shared. It is easily detectable because you are getting NaNs for numbers.