Русский
Русский
English
Статистика
Реклама

Время

Обработка дат притягивает ошибки или 77 дефектов в Qt 6

16.02.2021 22:10:00 | Автор: admin

PVS-Studio проверяет Qt 6
Относительно недавно состоялся релиз фреймворка Qt 6, и это стало поводом вновь проверить его с помощью PVS-Studio. В статье будут рассмотрены различные интересные ошибки, например, связанные с обработкой дат. Обнаружение всех этих ошибок хорошо демонстрирует пользу, которую может получить проект от использования таких инструментов, как PVS-Studio, особенно если они применяются регулярно.


Это классическая статья о проверке открытого проекта, которая пополнит нашу "доказательную базу" полезности и эффективности использования PVS-Studio для контроля качества кода. Хотя мы уже писали про поверку проекта Qt (в 2011, в 2014 и в 2018), очень полезно сделать это вновь. Так, мы на практике демонстрируем простую, но очень важную мысль: статический анализ должен применяться регулярно!


Наши статьи показывают, что анализатор PVS-Studio умеет находить множество разнообразнейших ошибок. И, как правило, авторы проектов быстро исправляют описанные нами ошибки. Однако всё это не имеет ничего общего с правильной и полезной методикой регулярного применения статических анализаторов. Когда анализатор встроен в процесс разработки, это позволяет быстро находить ошибки в новом или изменённом коде и тем самым их исправление обходится максимально дёшево.


Всё, теория заканчивается. Давайте посмотрим, что интересного нас ждёт в коде. А пока вы будете читать статью, предлагаю скачать PVS-Studio и запросить демонстрационный ключ, чтобы посмотреть, что интересного найдётся в ваших собственных проектах :).


Даты


Кажется, намечается ещё один паттерн кода, где любят собираться ошибки. Он, конечно, не такой масштабный, как функции сравнения или последние строки в однотипных блоках. Речь идёт про фрагменты кода, работающие с датами. Наверное, такой код сложно тестировать, и в итоге эти недотестированные функции будут давать некорректный результат на определённых наборах входных данных. Про пару таких случаев уже было рассказано в статье "Почему PVS-Studio не предлагает автоматические правки кода".


Встретились ошибки обработки дат и в Qt. Давайте с них и начнём.


Фрагмент N1: неправильная интерпретация статуса ошибки


Для начала нам следует посмотреть, как устроена функция, возвращающая номер месяца по его сокращённому названию.


static const char qt_shortMonthNames[][4] = {    "Jan", "Feb", "Mar", "Apr", "May", "Jun",    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};static int fromShortMonthName(QStringView monthName){  for (unsigned int i = 0;       i < sizeof(qt_shortMonthNames) / sizeof(qt_shortMonthNames[0]); ++i)  {    if (monthName == QLatin1String(qt_shortMonthNames[i], 3))      return i + 1;  }  return -1;}

В случае успеха функция возвращает номер месяца (значение от 1 до 12). Если имя месяца некорректно, то функция возвращает отрицательное значение (-1). Обратите внимание, что функция не может вернуть значение 0.


Но там, где только что рассмотренная функция используется, программист как раз рассчитывает, что в качестве статуса ошибки ему будет возвращено нулевое значение. Фрагмент кода, некорректно использующий функцию fromShortMonthName:


QDateTime QDateTime::fromString(QStringView string, Qt::DateFormat format){  ....  month = fromShortMonthName(parts.at(1));  if (month)    day = parts.at(2).toInt(&ok);  // If failed, try day then month  if (!ok || !month || !day) {    month = fromShortMonthName(parts.at(2));    if (month) {      QStringView dayPart = parts.at(1);      if (dayPart.endsWith(u'.'))        day = dayPart.chopped(1).toInt(&ok);    }  }  ....}

Проверки номера месяца на равенство нулю никогда не сработают, и программа продолжит выполняться с некорректным отрицательным номером месяца. Анализатор PVS-Studio видит здесь целый букет несостыковок, о чем сообщает сразу четырьмя сообщениями:


  • V547 [CWE-571] Expression 'month' is always true. qdatetime.cpp 4907
  • V560 [CWE-570] A part of conditional expression is always false: !month. qdatetime.cpp 4911
  • V547 [CWE-571] Expression 'month' is always true. qdatetime.cpp 4913
  • V560 [CWE-570] A part of conditional expression is always false: !month. qdatetime.cpp 4921

Фрагмент N2: ошибка логики обработки даты


Для начала посмотрим на реализацию функции, возвращающей количество секунд.


enum {  ....  MSECS_PER_DAY = 86400000,  ....  SECS_PER_MIN = 60,};int QTime::second() const{    if (!isValid())        return -1;    return (ds() / 1000)%SECS_PER_MIN;}

Рассмотренная функция может вернуть значение в диапазоне [0..59] или статус ошибки -1.


В одном месте эта функция используется очень странным образом:


static qint64 qt_mktime(QDate *date, QTime *time, ....){  ....  } else if (yy == 1969 && mm == 12 && dd == 31             && time->second() == MSECS_PER_DAY - 1) {      // There was, of course, a last second in 1969, at time_t(-1); we won't      // rescue it if it's not in normalised form, and we don't know its DST      // status (unless we did already), but let's not wantonly declare it      // invalid.  } else {  ....}

Предупреждение PVS-Studio: V560 [CWE-570] A part of conditional expression is always false: time->second() == MSECS_PER_DAY 1. qdatetime.cpp 2488


Согласно комментарию, если что-то пошло не так, то лучше ничего не делать. Однако, условие всегда будет ложным, поэтому всегда выполняется else-ветка.


Ошибочно вот это сравнение:


time->second() == MSECS_PER_DAY - 1

MSECS_PER_DAY 1 это 86399999. Функция second, как мы уже знаем, никак не может вернуть такое значение. Таким образом, здесь какая-то логическая ошибка и код заслуживает пристального внимания разработчиков.


Сильная сторона статических анализаторов кода в том, что они проверяют все ситуации, независимо от частоты их выполнения. Таким образом, статический анализ хорошо дополняет юнит-тесты и другие методы контроля качества кода.


Опечатки


Фрагмент N3: неожиданно, мы поговорим о HTML!


QString QPixelTool::aboutText() const{  const QList<QScreen *> screens = QGuiApplication::screens();  const QScreen *windowScreen = windowHandle()->screen();  QString result;  QTextStream str(&result);  str << "<html></head><body><h2>Qt Pixeltool</h2><p>Qt " << QT_VERSION_STR    << "</p><p>Copyright (C) 2017 The Qt Company Ltd.</p><h3>Screens</h3><ul>";  for (const QScreen *screen : screens)    str << "<li>" << (screen == windowScreen ? "* " : "  ")        << screen << "</li>";  str << "<ul></body></html>";  return result;}

Предупреждение PVS-Studio: V735 Possibly an incorrect HTML. The "</ body>" closing tag was encountered, while the "</ ul>" tag was expected. qpixeltool.cpp 707


В PVS-Studio есть диагностики, которые не только проверят сам код, но и выискивают аномалии в строковых константах. Здесь как раз сработала одна из таких диагностик. Это достаточно редкий случай, зато этим он и примечательный.


Дважды используется тег открытия списка. Это явная опечатка. Первый тег, должен открывать список, а второй закрывать. Правильный код:


str << "</ul></body></html>";

Фрагмент N4: повторная проверка в условии


class Node{  ....  bool isGroup() const { return m_nodeType == Group; }  ....};void DocBookGenerator::generateDocBookSynopsis(const Node *node){  ....  if (node->isGroup() || node->isGroup()      || node->isSharedCommentNode() || node->isModule()      || node->isJsModule() || node->isQmlModule() || node->isPageNode())    return;  ....}

Предупреждение PVS-Studio: V501 [CWE-570] There are identical sub-expressions to the left and to the right of the '||' operator: node->isGroup() || node->isGroup() docbookgenerator.cpp 2599


Простая опечатка, но её исправление зависит от того, чего на самом деле хотели достичь в этом коде. Если проверка просто дублируется, то её стоит просто удалить. Но возможен и другой сценарий: не проверено какое-то другое нужное условие.


Фрагмент N5: создание лишней локальной переменной


void MainWindow::addToPhraseBook(){  ....  QString selectedPhraseBook;  if (phraseBookList.size() == 1) {    selectedPhraseBook = phraseBookList.at(0);    if (QMessageBox::information(this, tr("Add to phrase book"),          tr("Adding entry to phrasebook %1").arg(selectedPhraseBook),           QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok)                          != QMessageBox::Ok)      return;  } else {    bool okPressed = false;    QString selectedPhraseBook =       QInputDialog::getItem(this, tr("Add to phrase book"),                            tr("Select phrase book to add to"),                            phraseBookList, 0, false, &okPressed);    if (!okPressed)      return;  }  MessageItem *currentMessage = m_dataModel->messageItem(m_currentIndex);  Phrase *phrase = new Phrase(currentMessage->text(),                              currentMessage->translation(),                              QString(), nullptr);  phraseBookHash.value(selectedPhraseBook)->append(phrase);}

Кстати, если хотите, можете проверить свою внимательность и самостоятельно поискать ошибку. А чтобы вы сразу случайно не увидели ответ, добавлю сюда единорожку из старой коллекции. Возможно, вы даже его ещё не видели :).


Единорог из старой коллекции


Предупреждение PVS-Studio: V561 [CWE-563] It's probably better to assign value to 'selectedPhraseBook' variable than to declare it anew. Previous declaration: mainwindow.cpp, line 1303. mainwindow.cpp 1313


В обоих ветках условного оператора формируется текст, который должен быть записан в переменную selectedPhraseBook. Имя переменной длинное, и программисту было лень писать его заново. Поэтому он скопировал имя переменной из того места, где она объявляется. При этом он чуть поспешил и вместе с именем скопировал и тип переменной:


QString selectedPhraseBook =

В результате в else-блоке появилась ещё одна локальная строковая переменная, которая инициализируется, но не используется. Тем временем переменная с таким же именем во внешнем блоке останется пустой.


Фрагмент N6: приоритет операций


Классический паттерн ошибки, который встречается весьма часто.


bool QQmlImportInstance::resolveType(....){  ....  if (int icID = containingType.lookupInlineComponentIdByName(typeStr) != -1)  {    *type_return = containingType.lookupInlineComponentById(icID);  } else {    auto icType = createICType();    ....  }  ....}

Предупреждение PVS-Studio: V593 [CWE-783] Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. qqmlimport.cpp 754


Значение переменной icID всегда будет иметь значение 0 или 1. Это явно не то, что задумывалось. Причина: в начале происходит сравнение с -1, а только затем инициализация переменной icID.


Современный синтаксис C++ позволяет корректно записать это условие следующим образом:


if (int icID = containingType.lookupInlineComponentIdByName(typeStr);    icID != -1)

Кстати, очень похожую ошибку мы уже обнаруживали в Qt:


char ch;while (i < dataLen && ((ch = data.at(i) != '\n') && ch != '\r'))  ++i;

Но пока на вооружение не будет взят такой анализатор кода, как PVS-Studio, программисты вновь и вновь будут допускать такие ошибки. Никто не совершенен. И да, это тонкий намёк внедрить PVS-Studio :).


Фрагмент N7: коварное деление по модулю


Часто бывает необходимо определить, делится число без остатка на 2 или нет. Правильный вариант это поделить по модулю два и проверить результат:


if (A % 2 == 1)

Но программисты вновь и вновь ошибаются и пишут что-то типа этого:


if (A % 1 == 1)

Это естественно неправильно, так как остаток от деления по модулю на один это всегда ноль. Не обошлось без этой ошибки и в Qt:


bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd){  ....  case Tag_Translation: {    int len = read32(m);    if (len % 1) {                                             // <=      cd.appendError(QLatin1String("QM-Format error"));      return false;    }    m += 4;    QString str = QString((const QChar *)m, len/2);  ....}

Предупреждение PVS-Studio: V1063 The modulo by 1 operation is meaningless. The result will always be zero. qm.cpp 549


Фрагмент N8: перезаписывание значения


QString Node::qualifyQmlName(){  QString qualifiedName = m_name;  if (m_name.startsWith(QLatin1String("QML:")))    qualifiedName = m_name.mid(4);  qualifiedName = logicalModuleName() + "::" + m_name;  return qualifiedName;}

Предупреждение PVS-Studio: V519 [CWE-563] The 'qualifiedName' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1227, 1228. node.cpp 1228


Как я понимаю, случайно использовали не то имя переменной. Предполагаю, здесь должно быть написано так:


QString qualifiedName = m_name;if (m_name.startsWith(QLatin1String("QML:")))  qualifiedName = m_name.mid(4);qualifiedName = logicalModuleName() + "::" + qualifiedName;return qualifiedName;

Фрагмент N9: copy-paste


class Q_CORE_EXPORT QJsonObject{  ....  bool operator<(const iterator& other) const  { Q_ASSERT(item.o == other.item.o); return item.index < other.item.index; }  bool operator<=(const iterator& other) const  { Q_ASSERT(item.o == other.item.o); return item.index < other.item.index; }  ....}

Прудпреждение PVS-Studio: V524 It is odd that the body of '<=' function is fully equivalent to the body of '<' function. qjsonobject.h 155


Такие скучные функции, как операторы сравнения, никто не проверяет. На них обычно не пишут тесты. Их не просматривают или делают это очень быстро на code review. А зря. Статический анализ кода здесь как нельзя кстати. Анализатор не устаёт и с удовольствием проверяет даже такой скучный код.


Реализация операторов < и <= совпадают. Это явно неправильно. Скорее всего, код писался методом Copy-Paste, и затем забыли изменить всё что нужно в скопированном коде. Правильно:


bool operator<(const iterator& other) const{ Q_ASSERT(item.o == other.item.o); return item.index < other.item.index; }bool operator<=(const iterator& other) const{ Q_ASSERT(item.o == other.item.o); return item.index <= other.item.index; }

Фрагмент N10: static_cast / dynamic_cast


void QSGSoftwareRenderThread::syncAndRender(){  ....  bool canRender = wd->renderer != nullptr;  if (canRender) {     auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer);     if (softwareRenderer)       softwareRenderer->setBackingStore(backingStore);  ....}

Предупреждение PVS-Studio: V547 [CWE-571] Expression 'softwareRenderer' is always true. qsgsoftwarethreadedrenderloop.cpp 510


В начале рассмотрим вот эту проверку:


bool canRender = wd->renderer != nullptr;if (canRender) {

Благодаря ей можно быть уверенным, что внутри тела условного оператора значение указателя wd->renderer всегда точно ненулевое. Тогда непонятно, что же хотят проверить следующим кодом?


auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer);if (softwareRenderer)

Если указатель wd->renderer ненулевой, то и указатель softwareRenderer точно ненулевой. Есть подозрение, что здесь опечатка, которая состоит в том, что на самом деле следовало использовать dynamic_cast. В этом случае код начинает приобретать смысл. Если преобразование типа невозможно, оператор dynamic_cast возвращает nullptr, и это возвращенное значение, естественно, следует проверять. Впрочем, возможно, я неправильно интерпретировал ситуацию и код нужно исправлять другим способом.


Фрагмент N11: скопировали блок кода и забыли изменить


void *QQuickPath::qt_metacast(const char *_clname){  if (!_clname) return nullptr;  if (!strcmp(_clname, qt_meta_stringdata_QQuickPath.stringdata0))    return static_cast<void*>(this);  if (!strcmp(_clname, "QQmlParserStatus"))    return static_cast< QQmlParserStatus*>(this);  if (!strcmp(_clname, "org.qt-project.Qt.QQmlParserStatus"))   // <=    return static_cast< QQmlParserStatus*>(this);  if (!strcmp(_clname, "org.qt-project.Qt.QQmlParserStatus"))   // <=    return static_cast< QQmlParserStatus*>(this);  return QObject::qt_metacast(_clname);}

Предупреждение PVS-Studio: V581 [CWE-670] The conditional expressions of the 'if' statements situated alongside each other are identical. Check lines: 2719, 2721. moc_qquickpath_p.cpp 2721


Эти две строчки:


if (!strcmp(_clname, "org.qt-project.Qt.QQmlParserStatus"))  return static_cast< QQmlParserStatus*>(this);

Были размножены с помощью Copy-Paste. После чего они не были модифицированы и не имеют смысла.


Фрагмент N12: переполнение из-за не там поставленной скобки


int m_offsetFromUtc;....void QDateTime::setMSecsSinceEpoch(qint64 msecs){  ....  if (!add_overflow(msecs, qint64(d->m_offsetFromUtc * 1000), &msecs))    status |= QDateTimePrivate::ValidWhenMask;  ....}

Предупреждение PVS-Studio: V1028 [CWE-190] Possible overflow. Consider casting operands of the 'd->m_offsetFromUtc * 1000' operator to the 'qint64' type, not the result. qdatetime.cpp 3922


Программист предвидит ситуацию, что при умножении переменной типа int на 1000 может произойти переполнение. Чтобы этого избежать, он планирует использовать при умножении 64-битный тип qint64. И использует явное приведение типа.


Вот только толку от этого приведения типа никакого нет. В начале всё равно произойдёт переполнение. И только затем выполнится приведение типа. Правильный вариант:


add_overflow(msecs, qint64(d->m_offsetFromUtc) * 1000, &msecs)

Фрагмент N13: не полностью инициализированный массив


class QPathEdge{  ....private:  int m_next[2][2];  ....};inline QPathEdge::QPathEdge(int a, int b)    : flag(0)    , windingA(0)    , windingB(0)    , first(a)    , second(b)    , angle(0)    , invAngle(0){    m_next[0][0] = -1;    m_next[1][0] = -1;    m_next[0][0] = -1;    m_next[1][0] = -1;}

Предупреждения PVS-Studio:


  • V1048 [CWE-1164] The 'm_next[0][0]' variable was assigned the same value. qpathclipper_p.h 301
  • V1048 [CWE-1164] The 'm_next[1][0]' variable was assigned the same value. qpathclipper_p.h 302

Перед нами неудачная попытка инициализировать массив размером 2x2. Два элемента инициализируются повторно, и два остаются неинициализированными. Правильный вариант:


m_next[0][0] = -1;m_next[0][1] = -1;m_next[1][0] = -1;m_next[1][1] = -1;

Я очень люблю такие примеры ошибок, которые встречаются в коде профессиональных разработчиков. Это как раз такой случай. Он показывает, что любой может опечататься, и поэтому статический анализ является вашим другом. Дело в том, что я уже десяток лет веду бой со скептиками, которые уверены, что такие ошибки можно встретить только в лабораторных работах студентов и что они такие ошибки никогда не делают :). Ещё 10 лет назад я написал заметку "Миф второй профессиональные разработчики не допускают глупых ошибок", и с тех пор, естественно, ничего не изменилось. Люди всё также делают такие ошибки и всё также утверждают, что это не так :).


Будь мудр - используй статический анализатор кода


Ошибки в логике


Фрагмент N14: недостижимый код


void QmlProfilerApplication::tryToConnect(){  Q_ASSERT(!m_connection->isConnected());  ++ m_connectionAttempts;  if (!m_verbose && !(m_connectionAttempts % 5)) {// print every 5 seconds    if (m_verbose) {      if (m_socketFile.isEmpty())        logError(          QString::fromLatin1("Could not connect to %1:%2 for %3 seconds ...")          .arg(m_hostName).arg(m_port).arg(m_connectionAttempts));      else        logError(          QString::fromLatin1("No connection received on %1 for %2 seconds ...")          .arg(m_socketFile).arg(m_connectionAttempts));    }  }  ....}

Предупреждение PVS-Studio: V547 [CWE-570] Expression 'm_verbose' is always false. qmlprofilerapplication.cpp 495


Этот код никогда ничего не запишет в лог. Причиной являются противоположные условия:


if (!m_verbose && ....) {  if (m_verbose) {

Фрагмент N15: перетирание значения переменной


void QRollEffect::scroll(){  ....  if (currentHeight != totalHeight) {      currentHeight = totalHeight * (elapsed/duration)          + (2 * totalHeight * (elapsed%duration) + duration)          / (2 * duration);      // equiv. to int((totalHeight*elapsed) / duration + 0.5)      done = (currentHeight >= totalHeight);  }  done = (currentHeight >= totalHeight) &&         (currentWidth >= totalWidth);  ....}

Предупреждение PVS-Studio: V519 [CWE-563] The 'done' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 509, 511. qeffects.cpp 511


Весь условный оператор не имеет смысла, так как значение переменной done всё равно тут же перезаписывается. Возможно, здесь не хватает ключевого слова else.


Фрагмент N16-N20: перетирание значения переменной


Альтернативный вариант перетирания значения переменной.


bool QXmlStreamWriterPrivate::finishStartElement(bool contents){  ....  if (inEmptyElement) {    ....    lastNamespaceDeclaration = tag.namespaceDeclarationsSize;   // <=    lastWasStartElement = false;  } else {    write(">");  }  inStartElement = inEmptyElement = false;  lastNamespaceDeclaration = namespaceDeclarations.size();      // <=  return hadSomethingWritten;}

Предупреждение PVS-Studio: V519 [CWE-563] The 'lastNamespaceDeclaration' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 3030, 3036. qxmlstream.cpp 3036


Возможно, первая запись значения в переменную lastNamespaceDeclaration является лишней и её можно удалить. А возможно, перед нами серьезная логическая ошибка.


Есть ещё 4 предупреждения, указывающих на такой же паттерн ошибки:


  • V519 [CWE-563] The 'last' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 609, 637. qtextengine.cpp 637
  • V519 [CWE-563] The 'm_dirty' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1014, 1017. qquickshadereffect.cpp 1017
  • V519 [CWE-563] The 'changed' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 122, 128. qsgdefaultspritenode.cpp 128
  • V519 [CWE-563] The 'eaten' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 299, 301. qdesigner.cpp 301

Фрагмент N21: путаница между нулевым указателем и пустой строкой


// this could become a list of all languages used for each writing// system, instead of using the single most common language.static const char languageForWritingSystem[][6] = {    "",     // Any    "en",  // Latin    "el",  // Greek    "ru",  // Cyrillic    ...... // Нулевых указателей нет. Используются пустые строковые литералы.    "", // Symbol    "sga", // Ogham    "non", // Runic    "man" // N'Ko};static void populateFromPattern(....){  ....  for (int j = 1; j < QFontDatabase::WritingSystemsCount; ++j) {    const FcChar8 *lang = (const FcChar8*) languageForWritingSystem[j];    if (lang) {  ....}

Предупреждение PVS-Studio: V547 [CWE-571] Expression 'lang' is always true. qfontconfigdatabase.cpp 462


В массиве languageForWritingSystem нет нулевых указателей. Поэтому проверка if(lang) не имеет смысла. Зато в массиве есть пустые строки. Быть может, хотелось сделать проверку именно на пустую строку? Если да, тогда корректный код должен выглядеть так:


if (strlen(lang) != 0) {

Или можно ещё проще написать:


if (lang[0] != '\0') {

Фрагмент N22: странная проверка


bool QNativeSocketEnginePrivate::createNewSocket(....){  ....  int socket = qt_safe_socket(domain, type, protocol, O_NONBLOCK);  ....  if (socket < 0) {    ....    return false;  }  socketDescriptor = socket;  if (socket != -1) {    this->socketProtocol = socketProtocol;    this->socketType = socketType;  }  return true;}

Предупреждение PVS-Studio: V547 [CWE-571] Expression 'socket != 1' is always true. qnativesocketengine_unix.cpp 315


Условие socket != -1 всегда истинно, так как выше происходит выход из функции, если значение переменной socket отрицательное.


Фрагмент N23: так что же всё-таки должна вернуть функция?


bool QSqlTableModel::removeRows(int row, int count, const QModelIndex &parent){  Q_D(QSqlTableModel);  if (parent.isValid() || row < 0 || count <= 0)    return false;  else if (row + count > rowCount())    return false;  else if (!count)    return true;  ....}

Предупреждение PVS-Studio: V547 [CWE-570] Expression '!count' is always false. qsqltablemodel.cpp 1110


Для упрощения выделю самое главное:


if (.... || count <= 0)  return false;....else if (!count)  return true;

Первая проверка говорит нам, что если значение count меньше или равно 0, то это ошибочное состояние и функция должна вернуть false. Однако ниже мы видим точное сравнение этой переменной с нулём, и этот случай уже интерпретируется по-другому: функция должна вернуть true.


Здесь явно что-то не так. Я подозреваю, что на самом деле проверка должна быть не <=, а просто <. Тогда код обретает смысл:


bool QSqlTableModel::removeRows(int row, int count, const QModelIndex &parent){  Q_D(QSqlTableModel);  if (parent.isValid() || row < 0 || count < 0)    return false;  else if (row + count > rowCount())    return false;  else if (!count)    return true;  ....}

Фрагмент N24: лишний статус?


В следующем коде переменная identifierWithEscapeChars выглядит просто как лишняя сущность. Или это логическая ошибка? Или код не дописан? К моменту второй проверки эта переменная в любом случае всегда будет равна true.


int Lexer::scanToken(){  ....  bool identifierWithEscapeChars = false;  ....  if (!identifierWithEscapeChars) {    identifierWithEscapeChars = true;    ....  }  ....  if (identifierWithEscapeChars) {    // <=    ....  }  ....}

Предупреждение PVS-Studio: V547 [CWE-571] Expression 'identifierWithEscapeChars' is always true. qqmljslexer.cpp 817


Фрагмент N25: что делать с девятью объектами?


bool QFont::fromString(const QString &descrip){  ....  const int count = l.count();  if (!count || (count > 2 && count < 9) || count == 9 || count > 17 ||      l.first().isEmpty()) {    qWarning("QFont::fromString: Invalid description '%s'",             descrip.isEmpty() ? "(empty)" : descrip.toLatin1().data());    return false;  }  setFamily(l[0].toString());  if (count > 1 && l[1].toDouble() > 0.0)    setPointSizeF(l[1].toDouble());  if (count == 9) {                           // <=    setStyleHint((StyleHint) l[2].toInt());    setWeight(QFont::Weight(l[3].toInt()));    setItalic(l[4].toInt());    setUnderline(l[5].toInt());    setStrikeOut(l[6].toInt());    setFixedPitch(l[7].toInt());  } else if (count >= 10) {  ....}

Предупреждение PVS-Studio: V547 [CWE-570] Expression 'count == 9' is always false. qfont.cpp 2142


Как должна себя вести функция, если переменная count равна 9? С одной стороны, функция должна выдать предупреждение и завершить свою работу. Ведь явно написано:


if (.... || count == 9 || ....) {  qWarning(....);  return false;}

С другой стороны, для 9 объектов предусмотрено выполнение специального кода:


if (count == 9) {  setStyleHint((StyleHint) l[2].toInt());  setWeight(QFont::Weight(l[3].toInt()));  setItalic(l[4].toInt());  ....}

Этот код, конечно, никогда не выполняется. Код ждёт, чтобы его пришли и исправили :).


Нулевые указатели


Фрагмент N26-N42: использование указателя до его проверки


class __attribute__((visibility("default"))) QMetaType {  ....  const QtPrivate::QMetaTypeInterface *d_ptr = nullptr;};QPartialOrdering QMetaType::compare(const void *lhs, const void *rhs) const{    if (!lhs || !rhs)        return QPartialOrdering::Unordered;    if (d_ptr->flags & QMetaType::IsPointer)        return threeWayCompare(*reinterpret_cast<const void * const *>(lhs),                               *reinterpret_cast<const void * const *>(rhs));    if (d_ptr && d_ptr->lessThan) {        if (d_ptr->equals && d_ptr->equals(d_ptr, lhs, rhs))            return QPartialOrdering::Equivalent;        if (d_ptr->lessThan(d_ptr, lhs, rhs))            return QPartialOrdering::Less;        if (d_ptr->lessThan(d_ptr, rhs, lhs))            return QPartialOrdering::Greater;        if (!d_ptr->equals)            return QPartialOrdering::Equivalent;    }    return QPartialOrdering::Unordered;}

Предупреждение PVS-Studio: V595 [CWE-476] The 'd_ptr' pointer was utilized before it was verified against nullptr. Check lines: 710, 713. qmetatype.cpp 710


Ошибка на первый взгляд может быть не заметна. Но на самом деле всё просто. Проследим, как работают с указателем d_ptr:


if (d_ptr->flags & ....)if (d_ptr && ....)

Вначале выполняется условие, в котором указатель разыменовывается. И только из следующей проверки становится ясно, что этот указатель, оказывается, может быть нулевой.


Это один из самых распространённых паттернов ошибки в языке C и С++. Пруфы. В исходных Qt кодах тоже встречается немало ошибок этой разновидности:


  • V595 [CWE-476] The 'self' pointer was utilized before it was verified against nullptr. Check lines: 1346, 1351. qcoreapplication.cpp 1346
  • V595 [CWE-476] The 'currentTimerInfo' pointer was utilized before it was verified against nullptr. Check lines: 636, 641. qtimerinfo_unix.cpp 636
  • V595 [CWE-476] The 'lib' pointer was utilized before it was verified against nullptr. Check lines: 325, 333. qlibrary.cpp 325
  • V595 [CWE-476] The 'fragment.d' pointer was utilized before it was verified against nullptr. Check lines: 2262, 2266. qtextcursor.cpp 2262
  • V595 [CWE-476] The 'window' pointer was utilized before it was verified against nullptr. Check lines: 1581, 1583. qapplication.cpp 1581
  • V595 [CWE-476] The 'window' pointer was utilized before it was verified against nullptr. Check lines: 1593, 1595. qapplication.cpp 1593
  • V595 [CWE-476] The 'newHandle' pointer was utilized before it was verified against nullptr. Check lines: 873, 879. qsplitter.cpp 873
  • V595 [CWE-476] The 'targetModel' pointer was utilized before it was verified against nullptr. Check lines: 454, 455. qqmllistmodel.cpp 454
  • V595 [CWE-476] The 'childIface' pointer was utilized before it was verified against nullptr. Check lines: 102, 104. qaccessiblequickitem.cpp 102
  • V595 [CWE-476] The 'e' pointer was utilized before it was verified against nullptr. Check lines: 94, 98. qquickwindowmodule.cpp 94
  • V595 [CWE-476] The 'm_texture' pointer was utilized before it was verified against nullptr. Check lines: 235, 239. qsgplaintexture.cpp 235
  • V595 [CWE-476] The 'm_unreferencedPixmaps' pointer was utilized before it was verified against nullptr. Check lines: 1140, 1148. qquickpixmapcache.cpp 1140
  • V595 [CWE-476] The 'camera' pointer was utilized before it was verified against nullptr. Check lines: 263, 264. assimpimporter.cpp 263
  • V595 [CWE-476] The 'light' pointer was utilized before it was verified against nullptr. Check lines: 273, 274. assimpimporter.cpp 273
  • V595 [CWE-476] The 'channel' pointer was utilized before it was verified against nullptr. Check lines: 337, 338. assimpimporter.cpp 337
  • V595 [CWE-476] The 'm_fwb' pointer was utilized before it was verified against nullptr. Check lines: 2492, 2500. designerpropertymanager.cpp 2492

Фрагмент N43: использование указателя до его проверки в рамках одного выражения


Та же самая ситуация, что и выше. Однако в этот раз разыменование и проверка указателя находятся в одном выражении. Классическая ошибка, возникающая из-за невнимательности при написании кода и при последующем code-review.


void QFormLayoutPrivate::updateSizes(){  ....  QFormLayoutItem *field = m_matrix(i, 1);  ....  if (userHSpacing < 0 && !wrapAllRows && (label || !field->fullRow) && field)  ....}

Предупреждение PVS-Studio: V713 [CWE-476] The pointer 'field' was utilized in the logical expression before it was verified against nullptr in the same logical expression. qformlayout.cpp 405


Минутка отдыха


Я устал. Думаю, устали и читатели. Тут устанешь, даже если просто бегло просматривать текст статьи :). Поэтому я пошёл за второй чашечкой кофе. Первую я выпил где-то на рубеже 12-ого фрагмента. Приглашаю и читателей сходить за любимым напитком.


И пока все ушли на кухню, рекламная пауза. Приглашаю команду, занимающуюся разработкой проекта Qt рассмотреть вопрос приобретения лицензии на анализатор кода PVS-Studio. Запросить прайс можно здесь. С нашей стороны поддержка и помощь в настройке. Да, согласен, я сегодня более навязчив, чем обычно. Это эксперимент :).


Минутка отдыха с единорогом


Фрагмент N44-N72: нет проверки, что вернула функция malloc


void assignData(const QQmlProfilerEvent &other){  if (m_dataType & External) {    uint length = m_dataLength * (other.m_dataType / 8);    m_data.external = malloc(length);                          // <=    memcpy(m_data.external, other.m_data.external, length);    // <=  } else {    memcpy(&m_data, &other.m_data, sizeof(m_data));  }}

Предупреждение PVS-Studio: V575 [CWE-628] The potential null pointer is passed into 'memcpy' function. Inspect the first argument. Check lines: 277, 276. qqmlprofilerevent_p.h 277


Нельзя просто взять и использовать указатель, который вернула функция malloc. Нужно обязательно проверить, не является ли этот указатель нулевым, даже если очень лень всем этим заниматься. Для этого есть 4 причины, которые описаны в статье "Почему важно проверять, что вернула функция malloc".


Перед нами один из случаев, где отсутствует необходимая проверка. Есть и другие предупреждения, но из-за их количества включать весь список в статью не хочется. На всякий случай, я выписал 28 предупреждений в файл: qt6-malloc.txt. Но на самом деле разработчикам, конечно, лучше самим перепроверить проект и самостоятельно изучить предупреждения. У меня не было задачи выявить как можно больше ошибок.


Что интересно, на фоне важных забытых проверок есть совершенно ненужные. Речь идёт о вызове оператора new, который в случае ошибки выделения памяти сгенерирует исключение std::bad_alloc. Вот один из примеров такой избыточной проверки:


static QImageScaleInfo* QImageScale::qimageCalcScaleInfo(....){  ....  QImageScaleInfo *isi;  ....  isi = new QImageScaleInfo;  if (!isi)    return nullptr;  ....}

Предупреждение PVS-Studio: V668 [CWE-570] There is no sense in testing the 'isi' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. qimagescale.cpp 245


P.S. Здесь читатели всегда задают вопрос, учитывает ли анализатор placement new или "new (std::nothrow) T"? Да, учитывает и не выдаёт для них ложные срабатывания.


Избыточный код ("код с запахом")


Встречаются ситуации, когда анализатор выдаёт предупреждения на код, который является корректным, но избыточным. Например, есть повторная проверка одной и той же переменной. При этом даже затруднительно сказать, ложное это срабатывание или нет. Формально анализатор прав, но и настоящую ошибку он не нашёл.


Пожалуй, можно сказать, что это "код с запахом". Раз этот код смущает анализатор, он может смущать и программистов, усложняя поддержку такого кода. Приходится тратить больше времени, чтобы понять, что происходит.


Часто я вообще не рассматриваю в статьях такие предупреждения. Это неинтересно. Однако в проекте Qt вдруг таких случаев оказалось удивительно много. Явно больше, чем обычно. Поэтому я решил уделить этому немного внимания и разобрать несколько таких случаев. Думаю, будет полезно провести рефакторинг этих и многих других аналогичных мест. Для этого потребуется использовать полный отчёт, не руководствоваться тем, что я выпишу в статью.


Итак, взглянем на несколько показательных случаев.


Фрагмент N73: "код с запахом" обратная проверка


void QQuick3DSceneManager::setWindow(QQuickWindow *window){  if (window == m_window)    return;  if (window != m_window) {    if (m_window)      disconnect(....);    m_window = window;    connect(....);    emit windowChanged();  }}

Предупреждение PVS-Studio: V547 [CWE-571] Expression 'window != m_window' is always true. qquick3dscenemanager.cpp 60


Если window==m_window, то функция завершает работу. Последующая обратная проверка не имеет никакого смысла и только загромождает код.


Фрагмент N74: "код с запахом" странная инициализация


QModelIndex QTreeView::moveCursor(....){  ....  int vi = -1;  if (vi < 0)    vi = qMax(0, d->viewIndex(current));  ....}

Предупреждение PVS-Stduio: V547 [CWE-571] Expression 'vi < 0' is always true. qtreeview.cpp 2219


Что это? Зачем так писать?


Что это? Зачем так писать? Код можно упростить до одной строки:


int vi = qMax(0, d->viewIndex(current));

Фрагмент N75: "код с запахом" недостижимый код


bool copyQtFiles(Options *options){  ....  if (unmetDependencies.isEmpty()) {    if (options->verbose) {      fprintf(stdout, "  -- Skipping %s, architecture mismatch.\n",              qPrintable(sourceFileName));    }  } else {    if (unmetDependencies.isEmpty()) {      if (options->verbose) {        fprintf(stdout, "  -- Skipping %s, architecture mismatch.\n",                  qPrintable(sourceFileName));      }    } else {      fprintf(stdout, "  -- Skipping %s. It has unmet dependencies: %s.\n",              qPrintable(sourceFileName),              qPrintable(unmetDependencies.join(QLatin1Char(','))));    }  }  ....}

Предупреждение PVS-Studio: V571 [CWE-571] Recurring check. The 'if (unmetDependencies.isEmpty())' condition was already verified in line 2203. main.cpp 2209


На первый взгляд перед нами респектабельный код, формирующий подсказку. Но давайте приглядимся. Если первый раз условие unmetDependencies.isEmpty() выполнилось, то второй раз этого уже не произойдёт. Это нестрашно, так как автор планировал вывести то же самое сообщение. Настоящей ошибки нет, но код переусложнён. Он может быть упрощен до следующего варианта:


bool copyQtFiles(Options *options){  ....  if (unmetDependencies.isEmpty()) {    if (options->verbose) {      fprintf(stdout, "  -- Skipping %s, architecture mismatch.\n",              qPrintable(sourceFileName));    }  } else {    fprintf(stdout, "  -- Skipping %s. It has unmet dependencies: %s.\n",            qPrintable(sourceFileName),            qPrintable(unmetDependencies.join(QLatin1Char(','))));  }  ....}

Фрагмент N76: "код с запахом" сложный тернарный оператор


bool QDockAreaLayoutInfo::insertGap(....){  ....  QDockAreaLayoutItem new_item    = widgetItem == nullptr      ? QDockAreaLayoutItem(subinfo)      : widgetItem ? QDockAreaLayoutItem(widgetItem)                    : QDockAreaLayoutItem(placeHolderItem);  ....}

Предупреждение PVS-Studio: V547 [CWE-571] Expression 'widgetItem' is always true. qdockarealayout.cpp 1167


Возможно, здесь мы имеем дело с настоящей ошибкой. Но я больше склоняюсь к тому, что этот код несколько раз менялся и в результате получился бессмысленно сложным (избыточным). Его можно сократить до:


  QDockAreaLayoutItem new_item    = widgetItem == nullptr      ? QDockAreaLayoutItem(subinfo) : QDockAreaLayoutItem(widgetItem);

Фрагмент N77: "код с запахом" избыточная защита


typedef unsigned int uint;ReturnedValue TypedArrayCtor::virtualCallAsConstructor(....){  ....  qint64 l = argc ? argv[0].toIndex() : 0;  if (scope.engine->hasException)    return Encode::undefined();  // ### lift UINT_MAX restriction  if (l < 0 || l > UINT_MAX)    return scope.engine->throwRangeError(QLatin1String("Index out of range."));  uint len = (uint)l;  if (l != len)    scope.engine->throwRangeError(      QStringLiteral("Non integer length for typed array."));  ....}

Предупреждение PVS-Studio: V547 [CWE-570] Expression 'l != len' is always false. qv4typedarray.cpp 306


Кто-то очень переживает, что значение 64-битной переменной не вмещается в 32-битную переменную unsigned. И использует сразу две проверки корректности. При этом вторая проверка избыточна.


Вот этого условия более чем достаточно:


if (l < 0 || l > UINT_MAX)

Приведенный ниже фрагмент можно смело удалить, и программа менее надёжной не станет:


uint len = (uint)l;if (l != len)  scope.engine->throwRangeError(    QStringLiteral("Non integer length for typed array."));

Дальше продолжать не буду. Думаю, идею вы поняли.


Здесь можно сделать маленький вывод: результатом использования анализатора PVS-Studio будет не только устранение ошибок, но и упрощение кода.


Другие ошибки


Я остановился после того, как описал 77 дефектов. Это красивое число и выписанного более чем достаточно, чтобы написать статью. Однако это не значит, что нет других ошибок, которые способен выявить PVS-Studio. При изучении лога я был весьма поверхностен и пропускал всё, где нужно было разбираться более пары минут, ошибка это или нет :).


Поэтому я всегда прошу не полагаться на ошибки, которые мы описываем в наших статьях, а выполнять анализ проектов самостоятельно.


Заключение


Статический анализ это круто! После внедрения PVS-Studio будет экономить время и нервы, выявляя множество ошибок сразу после написания кода. Намного лучше искать на code review с коллегами не опечатки, а высокоуровневые ошибки и обсуждать эффективность реализованного алгоритма. Тем более, как показывает практика, эти дурацкие опечатки всё равно отлично прячутся при просмотре кода глазами. Так что пусть их лучше ищет программа, а не человек.


Если у вас ещё остались вопросы или возражения, приглашаю познакомиться со статьёй "Причины внедрить в процесс разработки статический анализатор кода PVS-Studio". С вероятность 90 % вы найдете в ней ответ на ваши вопросы :). В оставшихся 10 % случаев напишите нам, пообщаемся :).


Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Andrey Karpov. Date Processing Attracts Bugs or 77 Defects in Qt 6.

Подробнее..

Перевод Работа с датой и часовыми поясами в JavaScript

16.04.2021 16:20:16 | Автор: admin

От переводчика

Однажды каждый разработчик коснется работы с часовыми поясами и будет фрустрирован необходимостью съесть слона для реализации, казалось бы, минорной задачи. Оказавшись в такой ситуации я нашел эту статью, которая дала ответы на все мои вопросы.

В моем случае она была столь полезной, что я решил перевести ее, чтобы запомнить получше и дебютировать с переводом на хабре.

Статья немаленькая и делится логически на две части:

  • Детальная информация о принятом сегодня стандарте времени и часовых поясов, которая будет полезна для разработчика на любом языке

  • Обзор подхода к работе со временем, принятого в JS, объекта Date, его методов и лучших практик в клиент-серверной разработке, и вывод почему для полноценной работы с датой вам не обойтись без задействования специальных библиотек.

Поехали!

Обработка часового пояса в JavaScript

Недавно я работал над задачей добавления функции часового пояса в TOAST UI Calendar, библиотеку календарей JavaScript, созданную моей командой. Я хорошо знал, что поддержка часовых поясов в JavaScript довольно таки слабая и надеялся, что абстрагирование существующих инструментов легко решило бы многие проблемы.

Однако моя надежда не оправдалась, и продвигаясь в решении задачи, я все больше осознавал, насколько непросто обрабатывать часовой пояс в JavaScript. Реализация функций часовых поясов помимо простого форматирования времени и вычисления временных данных с помощью сложных операций (например, календаря) была поистине сложной задачей. По этой причине я приобрел бесценный опыт решения проблемы, который привел к возникновению новых проблем.

Цель это статьи - обсудить проблемы и решения, связанные с особенностями применения часовых поясов в JavaScript.. В процессе написания этой объемной статьи, я внезапно понял, что корень моей проблемы кроется в моем плохом понимании концепции часовых поясов. Поэтому я сначала подробно рассмотрю определение и стандарты, относящиеся к часовому поясу, а затем расскажу о JavaScript.

Что такое часовой пояс (time zone)?

Часовой пояс - это регион, который использует единый, законодательно установленный стандарт времени. У многих стран свой уникальный часовой пояс, а в некоторых крупных странах, таких как США или Канада, даже несколько часовых поясов. Интересно, что хотя Китай достаточно велик, чтобы иметь несколько часовых поясов, он использует только один часовой пояс. По этой причине солнце встает в западной части Китая около 10:00 утра.

GMT, UTC и Offset

GMT (время по Гринвичу)
Местное время в Корее обычно GMT+09:00. GMT - это сокращение от среднего времени по Гринвичу, которое является временем на часах Королевской Обсерватории в Гринвиче, Великобритания, расположенной на долготе 0. Система GMT начала распространяться 5 февраля 1925 года и была мировым стандартом времени до 1 января 1972 года.

UTC (универсальное глобальное время)
Многие считают GMT и UTC одним и тем же, и во многих случаях они взаимозаменяемы, но на самом деле у них есть существенные отличия. UTC было создано в 1972 году для компенсации проблемы замедления вращения Земли. Эта система времени основана на Международном атомном времени, которое использует атомную частоту цезия для установки стандарта времени. Другими словами, UTC - более точная система. Хотя фактическая разница во времени между ними мала, UTC является более точным выбором для разработчиков программного обеспечения.

Когда система еще находилась в разработке, англоязычное население хотело назвать систему CUT (всемирное координированное время), а франкоязычное население хотело назвать ее TUC (Мировое время). Однако ни одна из сторон не выиграла бой, поэтому они пришли к соглашению об использовании аббревиатуры UTC, поскольку она содержала все основные буквы (C, T и U).

Offset (смещение часового пояса относительно часового пояса UTC)
+09:00 в UTC+09:00 означает, что местное время на 9 часов опережает стандартное время UTC. Это означает, что когда в Корее 21:00, в регионе UTC+00:00 - полдень, 12:00. Разница во времени между стандартным временем UTC и местным временем называется смещением (offset), которое выражается следующим образом: +09:00, -03:00 и т. д.

Часто страны называют свои часовые пояса своими уникальными именами. Например, часовой пояс Кореи называется KST (стандартное время Кореи) и имеет определенное значение смещения, которое выражается как KST = UTC+09:00. Однако смещение +09:00 также используется не только Кореей, но и Японией, Индонезией и многими другими, что означает, что отношение между смещениями и именами часовых поясов не 1:1, а 1:N. Список стран со смещением +09:00 можно найти в википедии на странице UTC+09:00.

Некоторые смещения не производятся строго на почасовой основе. Например, в Северной Корее в качестве стандартного времени используется +08:30, а в Австралии +08:45 или +09:30, в зависимости от региона.

Полный список смещений UTC и их названия можно найти здесь: Список смещений времени UTC.

Time zone !== offset?
Как я уже упоминал ранее, мы используем названия часовых поясов (KST, JST) взаимозаменяемо со смещением, не различая их. Но это неправильно рассматривать время и смещение в определенном регионе одинаково, по следующим причинам:

Летнее время (DST)
Хотя это понятие может быть неизвестно в некоторых странах, во многих странах летнее время официально принято - в частности, в США, Великобритании и странах Европы. На международном уровне летнее время обычно называется Daylight Saving Time (DST). Во время перехода на DST мы переводим стрелки часов на один час вперед от стандартного времени в летнее время.

Например, в Калифорнии в США зимой используется PST (стандартное тихоокеанское время, UTC-08:00), а летом - PDT (тихоокеанское летнее время, UTC-07:00). Регионы Северной Америки, в которых используются два часовых пояса, вместе называются Тихоокеанским временем (PT), и это название принято во многих регионах США и Канады.

Теперь самое интересное: когда именно начинается и заканчивается лето? На самом деле, даты начала и окончания летнего времени остаются на собственное усмотрение каждой страны. Это раз. А два - страна может поменять время и до изменения мы должны будем учитывать одно время, а после изменения - другое.

Например, до 2006 года в США и Канаде летнее время начиналось с первого воскресенья апреля в 02:00 и длилось до последнего воскресенья октября в 12:00, а с 2007 года стало начинаться во второе воскресенье марта с 02:00 и длиться до 2:00 первого воскресенья ноября. В европейских странах летнее время применяется единовременно по всей стране, в то время как в США летнее время поочередно применяется к часовым поясам.

Меняется ли часовой пояс?

Как я вкратце упомянул ранее, каждая страна имеет собственное право определять, какой часовой пояс использовать, а это означает, что ее часовой пояс может быть изменен по любым политическим или экономическим причинам. Например, в штатах период перехода на летнее время был изменен в 2007 году, поскольку президент Джордж Буш подписал энергетическую политику в 2005 году. Египет и Россия использовали летнее время, но перестали его использовать с 2011 года.

В некоторых случаях страна может изменить не только летнее время, но и стандартное время. Например, Самоа использовало смещение UTC-10:00, но позже перешло на UTC+14:00, чтобы уменьшить потери в торговле, вызванные разницей во времени между Самоа и Австралией и Новой Зеландией. Это решение привело к тому, что страна пропустила весь день 30 декабря 2011 года, и об этом сообщили газеты по всему миру.

Нидерланды использовали смещение +0:19:32.13, которое является излишне точным с 1909 года, но изменили его на смещение +00:20 в 1937 году, а затем снова изменили на смещение +01:00 в 1940 году, и придерживаются его до сих пор.

1 часовой пояс : N смещений

Подводя итог: один часовой пояс может иметь одно или несколько смещений. Какое смещение страна будет использовать в качестве стандартного времени в определенный момент, может варьироваться по политическим или экономическим причинам.

Это не большая проблема в повседневной жизни, но эта проблема возникает при попытке систематизировать работу со временем на основе определённых правил. Представим, что вы хотите установить стандартное время для своего смартфона с помощью смещения (offset). Если вы живете в регионе, где применяется летнее время, время на вашем смартфоне следует корректировать всякий раз, когда летнее время начинается и заканчивается. В этом случае вам понадобится концепция, которая объединяет стандартное время и летнее время в один часовой пояс (например, Тихоокеанское время).

Но это не может быть реализовано с помощью пары простых правил. Например, поскольку штаты изменили даты начала и окончания летнего времени в 2007 г., 31 мая 2006 г. вам следовало жить по PDT (тихоокеанское летнее время, UTC-07:00), а после 31 марта 2007 г. уже по PST (стандартное тихоокеанское время, UTC-08:00). Это означает, что для обращения к определенному часовому поясу вы должны знать все исторические данные о стандартных часовых поясах или момент времени, когда правила летнего времени были изменены.

Вы не можете просто сказать: Часовой пояс Сан-Франциско - PST, UTC-08:00. Вы должны быть более конкретными и сказать: Сан-Франциско в настоящее время использует PST как стандартное время.

Пример, как страны Северной Америки стандартизировали эти расхождения для себя - это объединение PST и PDT в PT, учитывающее текущее стандартное время и его летнее время. Однако, это только североамериканская практика и перед нами продолжает стоять задача работы с датами в прошлом и будущем, на которые влияют произошедшие и ожидаемые изменения в правилах, во всех часовых поясах. Хорошо бы чтобы все это было оформлено в международный стандарт.

База данных часовых поясов IANA

По правде говоря, часовые пояса - это скорее база данных, чем набор правил, потому что они должны содержать все соответствующие исторические изменения. Существует несколько стандартных баз данных, предназначенных для обработки проблем с часовыми поясами, и наиболее часто используемой из них является База данных часовых поясов IANA. База данных часовых поясов IANA, также называемая базой данных tz (или tzdata), содержит исторические данные о местном стандартном времени по всему миру и изменениях летнего времени. Эта база данных организована так, чтобы содержать все исторические данные, которые в настоящее время можно проверить, чтобы гарантировать точность времени, начиная со времени Unix (1970.01 / 01 00:00:00). В ней также есть данные до 1970 года, но их точность не гарантируется.

Соглашение об именовании соответствует правилу Area/Location. Area обычно относится к названию континента или океана (Азия, Америка, Тихий океан), в то время как Location - к названию крупных городов, таких как Сеул и Нью-Йорк, а не к названию стран (это потому, что продолжительность жизни страны намного короче, чем города). Например, часовой пояс Кореи - Азия / Сеул, а часовой пояс Японии - Азия / Токио. Хотя эти две страны находятся географически в регионе, где принят стандартный offset UTC+09:00, они имеют разную историю изменений часовых поясов. Вот почему в этом стандарте они обрабатываются с использованием разных часовых поясов.

База данных часовых поясов IANA поддерживается многочисленными сообществами разработчиков и историков. Новые исторические факты и политические решения сразу же попадают в базу данных, что делает ее наиболее надежным источником. Более того, многие ОС на базе UNIX, включая Linux и macOS, а также популярные языки программирования, включая Java и PHP, используют эту базу данных.

Обратите внимание, что Windows отсутствует в приведенном выше списке поддержки. Это потому, что Windows использует собственную базу данных под названием Microsoft Time Zone Database. Однако эта база данных неточно отражает исторические изменения и поддерживается только Microsoft. Следовательно, она менее точна и надежна, чем IANA.

JavaScript и База данных часовых поясов IANA

Как я вкратце упомянул ранее, поддержка часового пояса в JavaScript довольно плохая. Поскольку по умолчанию он следует часовому поясу определенного региона (точнее, часовому поясу, выбранному во время установки ОС), возможности изменить его на новый часовой пояс нет. Кроме того, его спецификация для стандарта баз данных так же плохо определена, вы заметите это, если внимательно ознакомитесь с документацией ES2015. В отношении местного часового пояса и доступности летнего времени существует лишь несколько неопределенных заявлений. Например, летнее время определяется следующим образом: ECMAScript 2015 - Настройка летнего времени.

В зависимости от реализации, алгоритм использует наилучшую доступную информацию о часовых поясах для определения местной поправки на летнее время DaylightSavingTA (t), измеряемой в миллисекундах. Ожидается, что реализация ECMAScript сделает все возможное, чтобы определить местное летнее время.

Похоже, он просто говорит: Эй, ребята, попробуйте и сделайте все возможное, чтобы это сработало. Это создает проблемы в кроссбраузерности. Вы можете подумать: Это небрежно!, и тогда вы заметите еще одну строчку прямо под ним:

ПРИМЕЧАНИЕ. рекомендуется использовать информацию о часовых поясах из базы данных часовых поясов IANA http://www.iana.org/time-zones/.

Спецификации ECMA признается, что не имеет специальной стандартной базы данных в JavaScript и рекомендует использовать базу данных часовых поясов IANA. В результате разные браузеры используют свои собственные решения для расчета часовых поясов, и они часто несовместимы друг с другом. Позже в ECMA-402 добавили возможность использовать часовой пояс IANA в виде Intl.DateTimeFormat для ECMAScript Internationalization API. Однако этот вариант по-прежнему гораздо менее надежен, чем в других языках программирования.

Часовой пояс в серверно-клиентской среде

Мы предположим простой сценарий, в котором необходимо учитывать часовой пояс. Допустим, мы собираемся разработать простое приложение-календарь, которое будет обрабатывать информацию о времени. Когда пользователь вводит дату и время в поле на странице реестра в клиентской среде, данные передаются на сервер и сохраняются в БД. Затем клиент получает зарегистрированные данные расписания с сервера и отображает их на экране.

Однако здесь есть то, что нужно учесть. Что, если некоторые из клиентов, обращающихся к серверу, находятся в разных часовых поясах? Расписание, зарегистрированное на 11 марта 2017 г. в 11:30 в Сеуле, должно отображаться как 10 марта 2017 г. в 21:30 при просмотре расписания в Нью-Йорке. Чтобы сервер поддерживал клиентов из разных часовых поясов, расписание, хранимое на сервере, должно иметь абсолютные значения, на которые не влияют часовые пояса. Каждый сервер имеет свой способ хранения абсолютных значений, и это выходит за рамки данной статьи, поскольку все зависит от сервера или среды базы данных. Однако для того, чтобы это работало, дата и время, передаваемые от клиента на сервер, должны быть значениями, основанными на том же смещении (обычно в формате UTC) или значениями, которые также включают данные часового пояса клиентской среды.

Обычно такие данные передаются в форме времени Unix на основе UTC или ISO-8601, содержащего информацию о смещении. В приведенном выше примере, если 11:30 утра 11 марта 2017 г. в Сеуле необходимо преобразовать во время Unix, это будет целочисленный тип со значением 1489199400. В соответствии с ISO-8601 это будет строковый тип, значение которого: 2017-03-11T11:30:00+09:00.

Если вы работаете с этим с помощью JavaScript в среде браузера, вы должны преобразовать введенное значение, как описано выше, а затем преобразовать его обратно в соответствии с часовым поясом пользователя. Необходимо учитывать обе эти две задачи. В языке программирования первый называется синтаксическим анализом, а второй форматированием. Теперь давайте выясним, как это обрабатывается в JavaScript.

Даже когда вы работаете с JavaScript в серверной среде с использованием Node.js, возможно вам может потребоваться проанализировать данные, полученные от клиента. Однако, поскольку серверы обычно синхронизируют свой часовой пояс с базой данных, а задача форматирования обычно предоставляется клиентам, вам нужно учитывать меньше факторов, чем в среде браузера. В этой статье мы будем рассматривать примеры в среде браузера.

Объект даты в JavaScript

В JavaScript задачи, связанные с датой или временем, обрабатываются с помощью объекта Date. Это собственный объект, определенный в ECMAScript, также, как Array или Object. Он в основном реализован в собственном коде, таком как C++. Его API хорошо описан в документации MDN. На это большое влияние оказывает класс Java java.util.Date. В результате он наследует некоторые нежелательные черты, такие как характеристики изменяемых данных и месяц, начинающийся с 0.

Объект Date в JavaScript внутренне управляет данными времени, используя абсолютные значения, такие как время Unix. Однако конструкторы и методы, такие как функции parse(), getHour(), setHour() и т.д. находятся под влиянием местного часового пояса клиента (точнее, часовой пояса операционной системы, в которой запущен браузер). Следовательно, если вы создаете объект Date с использованием данных, вводимых пользователем, данные будут напрямую отражать местный часовой пояс клиента.

Как я упоминал ранее, JavaScript не предоставляет произвольного способа изменения часового пояса. Поэтому я предполагаю здесь ситуацию, когда можно напрямую использовать настройку часового пояса браузера.

Создание объекта даты с пользовательским вводом

Вернемся к первому примеру. Предположим, что пользователь вошел в 11:30 11 марта 2017 г. на устройстве, которое соответствует часовому поясу Сеула. Эти данные хранятся в 5 целых числах 2017, 2, 11, 11 и 30, каждое из которых представляет год, месяц, день, час и минуту соответственно. (Поскольку месяц начинается с 0, значение должно быть 31 = 2.) С помощью конструктора вы можете легко создать объект Date, используя числовые значения.

const d1 = new Date(2017, 2, 11, 11, 30);d1.toString(); // Sat Mar 11 2017 11:30:00 GMT+0900 (KST)

Если вы посмотрите на значение, возвращаемое d1.toString(), то узнаете, что абсолютное значение созданного объекта составляет 11:30 11 марта 2017 г. на основе смещения +09:00 (KST).

Вы также можете использовать конструктор вместе со строковыми данными. Если вы передаете строку при создании Date, он внутренне вызывает Date.parse() и вычисляет правильное значение. Эта функция поддерживает спецификации RFC2888 и ISO-8601. Однако, как описано в документе MDN Date.parse(), возвращаемое значение этого метода варьируется от браузера к браузеру, и формат типа строки может повлиять на предсказание точного значения. Таким образом, рекомендуется не использовать этот метод.

Например, строка вида 2015-10-12 12:00:00 возвращает NaN в Safari и Internet Explorer, в то же время эта строка возвращает местный часовой пояс в Chrome и Firefox. В некоторых случаях она возвращает значение, основанное на стандарте UTC.

Создание объекта даты с использованием данных сервера

Предположим теперь, что вы собираетесь получать данные с сервера. Если данные имеют числовое значение времени Unix, вы можете просто использовать конструктор для создания объекта Date. Когда конструктор Date получает единственный числовой параметр, он распознается, как значение времени Unix в миллисекундах. (Внимание: JavaScript обрабатывает время Unix в миллисекундах. Это означает, что стандартное числовое значение времени Unix необходимо умножить на 1000) Результат выполнения следующего кода будет таким же, как и в предыдущем примере.

const d1 = new Date(1489199400000);d1.toString(); // Sat Mar 11 2017 11:30:00 GMT+0900 (KST)

Что если вместо времени Unix использовать строковый тип, такой как ISO-8601? Как я объяснил в предыдущем абзаце, метод Date.parse() ненадежен, и его лучше не использовать. Однако, поскольку в ECMAScript 5 и более поздних версиях указана поддержка ISO-8601, вы можете использовать строки в формате, указанном в ISO-8601, для конструктора Date в Internet Explorer 9.0 или более поздней версии, который поддерживает ECMAScript 5 при осторожном использовании.

Если вы поддерживаете старые браузеры, не забудьте добавить букву Z в конце. Без неё старые браузеры иногда интерпретируют строку на основе вашего местного времени, а не UTC. Ниже приведен пример запуска в Internet Explorer 10.

const d1 = new Date('2017-03-11T11:30:00');const d2 = new Date('2017-03-11T11:30:00Z');d1.toString(); // "Sat Mar 11 11:30:00 UTC+0900 2017"d2.toString(); // "Sat Mar 11 20:30:00 UTC+0900 2017"

Согласно техническим характеристикам результирующие значения в обоих случаях должны быть одинаковыми. Однако, как вы можете видеть, результирующие значения отличаются как d1.toString() и d2.toString(). В последней версии браузера эти два значения будут одинаковыми. Чтобы избежать проблем такого рода, всегда следует добавлять Z в конце строки, если нет данных о часовом поясе.

Создание данных для передачи на сервер

Теперь используя созданный ранее объект Date, вы можете свободно добавлять или вычитать время в зависимости от местных часовых поясов. Но не забудьте преобразовать данные обратно в предыдущий формат в конце обработки, прежде чем передавать их обратно на сервер.

Если это время Unix, вы можете просто использовать метод getTime() для выполнения этого. (Обратите внимание на использование миллисекунд.)

const d1 = new Date(2017, 2, 11, 11, 30);d1.getTime(); // 1489199400000

А как насчет строк формата ISO-8601? Как объяснялось ранее, Internet Explorer 9.0 или выше, который поддерживает ECMAScript 5 или выше, поддерживает формат ISO-8601. Вы можете создавать строки формата ISO-8601, используя метод toISOString() или toJSON(). toJSON() может использоваться для рекурсивных вызовов с JSON.stringify() или другими. Оба метода дают одинаковые результаты, за исключением случая, когда они обрабатывают недопустимые данные:

const d1 = new Date(2017, 2, 11, 11, 30);d1.toISOString(); // "2017-03-11T02:30:00.000Z"d1.toJSON();   // "2017-03-11T02:30:00.000Z"const d2 = new Date('Hello');d2.toISOString(); // Error: Invalid Dated2.toJSON();   // null

Вы также можете использовать метод toGMTString() или toUTCString() для создания строк в формате UTC. Поскольку они возвращают строку, удовлетворяющую стандарту RFC-1123, вы можете использовать это по мере необходимости.

Объекты Date включают toString(), toLocaleString() и их методы расширения. Однако, поскольку они в основном используются для возврата строки, основанной на местном часовом поясе, и возвращают различные значения в зависимости от используемого браузера и ОС, они не очень полезны.

Изменение местного часового пояса

Теперь вы можете видеть, что в JavaScript слабая поддержка часовых поясов. Что делать, если вы хотите изменить настройку местного часового пояса в своем приложении, не соблюдая настройки часового пояса вашей ОС? Или что, если вам нужно отображать несколько часовых поясов одновременно в одном приложении? Как я уже говорил несколько раз, JavaScript не позволяет вручную изменять местный часовой пояс. Единственное решение этой проблемы - добавить или удалить значение смещения от даты при условии, что вы уже знаете значение смещения часового пояса. Но пока не расстраивайтесь. Посмотрим, есть ли какое-нибудь решение, чтобы обойти это.

Давайте продолжим предыдущий пример, предполагая, что часовой пояс браузера установлен на Сеул. Пользователь входит в 11:30 11 марта 2017 г. по сеульскому времени, но хочет видеть местное время Нью-Йорка. Сервер передает данные времени Unix в миллисекундах и вы можете преобразовать их, если знаете смещение местного часового пояса, а мы знаем, что для Нью-Йорка это -05:00

Для вычисления смещений вы можете использовать метод getTimeZoneOffset(). Этот метод - единственный API в JavaScript, который можно использовать для получения информации о местном часовом поясе. Он возвращает значение смещения текущего часового пояса в минутах относительно часового пояса UTC.

const seoul = new Date(1489199400000);seoul.getTimeZoneOffset(); // -540

Возвращаемое значение -540 означает, что часовой пояс Сеула опережает UTC на 540 минут. Обратите внимание, что знак минус перед значением противоположен знаку плюса в стандартном обозначении смещения (+09:00). Не знаю почему, но вот так это отображается. Если мы вычислим смещение Нью-Йорка с помощью этого метода, мы получим 60 * 5 = 300. Преобразуйте разницу 840 в миллисекунды и создайте новый объект Date. Затем вы можете использовать get-методы этого объекта для преобразования значения в любой формат по вашему выбору. Давайте создадим простую функцию форматирования для сравнения результатов.

function formatDate(date) {return date.getFullYear() + '/' +(date.getMonth() + 1) + '/' +date.getDate() + ' ' +date.getHours() + ':' +date.getMinutes();}const seoul = new Date(1489199400000);const ny = new Date(1489199400000 - (840 * 60 * 1000));formatDate(seoul); // 2017/3/11 11:30formatDate(ny);   // 2017/3/10 21:30

formatDate() показывает правильную дату и время в соответствии с разницей часовых поясов между Сеулом и Нью-Йорком. Похоже, мы нашли простое решение. Тогда можем ли мы преобразовать его в местный часовой пояс, если мы знаем смещение региона? К сожалению, ответ отрицательный. Помните, что я сказал ранее? Что данные часового пояса - это своего рода база данных, содержащая историю всех изменений смещения? Чтобы получить правильное значение часового пояса, вы должны знать значение смещенияна момент даты (а не на текущую дату).

Проблема преобразования местного часового пояса

Если вы продолжите работать с приведенным выше примером еще немного, вы скоро столкнетесь с проблемой. Пользователь хочет проверить время по местному времени Нью-Йорка, а затем изменить дату с 10-го на 15-е. Если вы используете метод setDate() объекта Date, вы можете изменить дату, не изменяя другие значения.

ny.setDate(15);formatDate(ny);  // 2017/3/15 21:30

Выглядит достаточно просто, но здесь есть ловушка. Что бы вы сделали, если бы вам пришлось передать эти данные обратно на сервер? Поскольку данные были изменены, вы не можете использовать такие методы, как getTime() или getISOString(). Следовательно, вы должны отменить преобразование, прежде чем отправлять его обратно на сервер.

const time = ny.getTime() + (840 * 60 * 1000); // 1489631400000

Некоторые из вас могут задаться вопросом, почему я добавил использование преобразования данных, когда мне все равно нужно преобразовать их обратно перед возвратом. Выглядит так, будто я могу просто обработать их без преобразования и временно создать преобразованный объект Date только при форматировании. Однако не все так просто. Если вы измените дату объекта Date по сеульскому времени с 11-го на 15-е, добавляются 4 дня (24 * 4 * 60 * 60 * 1000). Однако по местному времени Нью-Йорка, поскольку дата была изменена с 10-го на 15-е, в результате было добавлено 5 дней (24 * 5 * 60 * 60 * 1000). Это означает, что для получения корректного результата вы должны рассчитывать даты на основе местного смещения часового пояса относительно часового пояса UTC.

Проблемы на этом не заканчиваются. Вот еще одна, где вы не получите желаемое значение, просто добавив или вычтя смещения часового пояса. Поскольку 12 марта является датой начала летнего времени по местному времени Нью-Йорка, смещение часового пояса 15 марта 2017 г. должно быть -04:00, а не -05:00. Поэтому, когда вы отключаете преобразование, вы должны добавить 780 минут, что на 60 минут меньше, чем раньше.

const time = ny.getTime() + (780 * 60 * 1000); // 1489627800000

Напротив, если местным часовым поясом пользователя является Нью-Йорк и он хочет узнать время в Сеуле, нет необходимости применять летнее время, что вызывает другую проблему.

Проще говоря, вы не можете использовать только полученное смещение для выполнения точных операций в зависимости от выбранного вами часового пояса. Если вы вспомните то, что мы обсуждали в предыдущей части этого документа, вы поймете, что в этом преобразовании все еще есть дыра, при условии что вы помните правила летнего времени. Чтобы получить точное значение, вам нужна база данных, содержащая всю историю изменений смещения часового пояса, например База данных часовых поясов IANA.

Чтобы решить эту проблему, необходимо сохранить всю базу данных часовых поясов и всякий раз, когда данные о дате или времени извлекаются из объекта Date, найти дату и соответствующее смещение часового пояса, а затем преобразовать значение, используя описанный выше процесс. Теоретически это возможно. Но на самом деле это требует слишком больших усилий, и проверка целостности преобразованных данных также будет сложной задачей. Но пока не расстраивайтесь. До сих пор мы обсуждали некоторые проблемы JavaScript и способы их решения. Теперь мы готовы использовать хорошо созданную библиотеку.

Moment timezone

Moment - это хорошо зарекомендовавшая себя библиотека JavaScript, которая является почти стандартом для обработки даты. Предоставляя различные API-интерфейсы для дат и форматирования, в последнее время многие пользователи признают moment стабильным и надежным. И есть модуль расширения Moment Timezone, который решает все проблемы, описанные выше. Этот модуль расширения содержит данные базы данных часовых поясов IANA для точного расчета смещений и предоставляет различные API-интерфейсы, которые можно использовать для изменения и форматирования часового пояса.

В этой статье я не буду подробно обсуждать, как использовать библиотеку или структуру библиотеки. Я просто покажу вам, насколько просто решить проблемы, которые я обсуждал ранее. Если кому-то интересно, см. Документацию Moment Timezone.

Давайте решим проблему, показанную на картинке, с помощью Moment Timezone.

const seoul = moment(1489199400000).tz('Asia/Seoul');const ny = moment(1489199400000).tz('America/New_York');seoul.format(); // 2017-03-11T11:30:00+09:00ny.format();  // 2017-03-10T21:30:00-05:00seoul.date(15).format(); // 2017-03-15T11:30:00+09:00ny.date(15).format();   // 2017-03-15T21:30:00-04:00

Как вы видите в результате, смещение seoul останется прежним, в то время как смещение ny было изменено с -05:00 на -04:00. И если вы используете функцию format(), вы можете получить строку в формате ISO-8601, которая точно применила смещение. Видите насколько это просто по сравнению с тем, что мы делали ранее.

Заключение

До сих пор мы обсуждали API-интерфейсы часовых поясов, поддерживаемые JavaScript, и их проблемы. Если вам не нужно вручную изменять местный часовой пояс, вы можете реализовать необходимый функционал даже с помощью базовых API-интерфейсов при условии, что вы используете Internet Explorer 9 или выше. Однако, если вам нужно вручную изменить местный часовой пояс, все становится очень сложным. В регионе, где нет летнего времени и политика часовых поясов практически не меняется, вы можете частично реализовать ее, используя getTimezoneOffset() для преобразования данных. Но если вам нужна полная поддержка часовых поясов, не создавайте ее с нуля. Лучше используйте такую библиотеку, как Moment Timezone.

Я сам пытался реализовать часовой пояс, но у меня ничего не вышло, что неудивительно. Вывод здесь после нескольких неудач - лучше использовать библиотеку. Когда я впервые начал писать эту статью, я не знал, о каком выводе собираюсь написать, и вот мы здесь. В заключение я бы сказал, что слепое использование внешних библиотек без знания того, какие функции они поддерживают в JavaScript и какие у них проблемы, плохая идея. Как всегда, важно выбрать правильный инструмент для вашей ситуации. Надеюсь, эта статья помогла вам принять собственное решение.

Заключение от переводчика

Очевидно, здесь повествование обрывается, и хотелось бы углубиться в особенности различных библиотек для работы с датами в JS, в частности в Moment.JS, Luxon и Date FNS. Возможно в дальнейшем здесь появится ссылка на статью посвященную этим библиотекам.

Благодарю за помощь в подготовке перевода Степана Омельченко и Марию Пилипончик. Это было не просто, но мы смогли!

Подробнее..

Некоторые программистские заблуждения о времени

04.03.2021 14:05:18 | Автор: admin

Наверняка вы уверены, что какие-то а то и все из этих утверждений истинны всегда и везде:

  • В сутках всегда 24 часа.
  • Неделя всегда начинается и заканчивается в том же месяце.
  • Неделя (или месяц) всегда начинается и заканчивается в том же году.
  • У времени нет начала и конца.
  • В месяце может быть 28, 29, 30 или 31 день.
  • Високосный год бывает раз в 4 года.
  • В каждом месяце везде всегда одно и тоже количество дней.
  • На сервере и на клиенте всегда будет одно и то же время.
  • Можно легко вычислить количество часов и минут начиная с какого-то момента времени.
  • В каждой минуте 60 секунд.

Хотя практика показывает, что полагаться на истинность этих заявлений при разработке ПО заблуждение. Ну ладно, не все эти пункты относятся к заблуждениям. Некоторые утверждения были бы верны, если бы не досадные баги и пограничные случаи, которых за историю ПО накопилось несметное количество.

В каждой минуте 60 секунд, а в сутках всегда 24 часа


К примеру, в старых версиях KVM в CentOS был любопытный баг минута могла длиться сколько угодно. Дело в том, что KVM не знала, что она исполняется не на физическом оборудовании, и если хостинговая ОС временно приостанавливала виртуальную машину, то виртуализированные часы продолжали идти с момента приостановки. Например, если машина вставала на паузу в 13:00 и активировалась в 15:00, системные часы в ней продолжали показывать 13:00. В результате, после каждой приостановки возникало расхождение с реальным временем. Была даже cron-задача, которую можно было установить для синхронизации виртуализированных часов с аппаратными. Но при создании новой виртуалки легко можно было об этом забыть, и это было весело. Позднее баг исправили.

Если же отвлечься от багов, то существует ещё и високосная (дополнительная) секунда: она добавляется к UTC (всемирному скоординированному времени) в конце суток 30 июня или 31 декабря, чтобы компенсировать постепенное замедление вращения Земли и накопление разницы между сутками в системе СИ и астрономическими солнечными сутками.

Что же касается длительности суток, то главный враг программиста переход на летнее время, который где-то есть, где-то его нет, причём раньше его могли отменить или ввести. Это отдельная большая проблема исторических данных учёт летнего времени.

А если прибавить к этому смену часовых поясов


Про недели, месяцы и годы


А вот истории насчёт разной длительности месяцев и лет связаны с разным календарным исчислением у разных народов мира. К примеру, еврейский календарь оперирует лунными месяцами: то есть начало и конец месяца привязаны к фазам Луны. Думаете, можно это легко учесть, добавив для Израиля корректировку с подгрузкой лунных фаз? Не выйдет. В еврейском високосном году добавляется лишний месяц. Более того, следите за руками и простой, и високосный год в еврейском календаре может иметь три разных длительности. Итого год может иметь шесть разных длительностей от 354 до 383 дней. Думаете, на этом отличия от нашего календаря закончились? Куда там: в еврейском календаре сутки имеют разную длительность и отсчитываются от заката до заката (формально, когда на небе становятся видны три звезды).

Считаете, что это только у евреев всё не как в григорианском календаре (к вопросу о Великой октябрьской социалистической революции, которую в СССР праздновали 7 ноября)? В арабских странах:

  • Неделя начинается не в понедельник, а в воскресенье.
  • Выходными считаются пятница и суббота, а в некоторых странах четверг и пятница. За последние 10 лет некоторые государства перенесли выходные на пятницу и субботу, чтобы способствовать международной торговле. А ещё не во всех арабских странах выходные состоят из двух дней.
  • Религиозные праздники зависят от наблюдений за лунным циклом, поэтому их сроки наступления и завершения не могут быть точно предсказаны.

Что касается того, что недели и месяцы могут заканчиваться в следующем году, то это опять же характерно для стран с лунными и солнечно-лунными календарями в них новый год наступает не 1 января. Навскидку можно сразу вспомнить китайцев, у которых новый год наступает в промежуток с 21 января по 21 февраля по григорианскому календарю. А в эфиопском календаре новый год вообще 29 или 30 августа, да ещё и количество лет у них на 8 меньше, чем в системе от рождества Христова.

На сервере и на клиенте всегда будет одно и то же время


А вот интересный факт, из-за которого время на разных компьютерах может не просто не совпадать, но даже размер расхождения может меняться. При своей загрузке Linux берёт текущее аппаратное время, а затем отсчитывает его дальше, добавляя данные от внутренних часов процессора (TSC). Эти часы могут быть весьма неточны. Например, из-за масштабирования тактовой частоты, которая динамически меняет частоту TSC. И если поменять частоту часов на хосте, то все гостевые аккаунты даже не заметят этого. Если масштабировать частоту TSC на 50 %, время начнёт идти вдвое медленнее. Кроме того, на некоторых серверах BIOS может масштабировать частоту процессора, не уведомляя ОС, что тоже добавляет погрешность. На более современных процессорах частоты TSC теперь фиксированы. К слову, Windows не использует TSC, так что в этой ОС у отсчёта времени другие проблемы :)

Можно легко вычислить количество часов и минут начиная с какого-то момента времени


Если в языке программирования нет чего-то вроде питоновской tzinfo(), вы не сможете получить конкретную дату и время, просто добавив часы и минуты к какой-нибудь дате в прошлом. Нужно учесть временные зоны (и их возможную смену, как это не раз происходило в истории), затем учесть все изменения в переходе на летнее время В Windows это вообще невозможно, потому что Microsoft предоставляет только начало и конец текущего года. Удивительно, что после стольких патчей обработки перехода на летнее время компания до сих пор не реализовала в Windows аналог tzinfo(). Вероятно, и не реализует.

У времени нет начала и конца


Вашей программе никогда не понадобится обрабатывать даты до 1970 года. В Unix-системах (в том числе Linux и iOS) время отсчитывается в количестве секунд начиная с 00:00:00 1 января 1970 года по UTC (Всемирному скоординированному времени). Всё, что раньше, в Unix будет уже отрицательным временем. Причём время представлено в 32-битном целочисленном выражении, и самой ранней датой, возможной в Unix-системах, является 13 декабря 1901 года. А сверху Unix-время ограничено 19 января 2038 года, когда количество секунд с начала отсчёта достигнет 231, а это число системы могут посчитать отрицательным.



Всё это лишь малая часть огромного количества нюансов и багов, с которыми приходится справляться разработчикам любых продуктов, использующих время. Наверняка вам тоже есть что рассказать из своего опыта, пишите в комментариях.
Подробнее..

15 советов Локализация сервисов под разные страны

16.10.2020 16:14:58 | Автор: admin

Пока ещё на политической карте мира границы есть. В мире интернета они давно стёрты.


И вашим сервисом, если он по-настоящему массовый, может воспользоваться и американец, и француз, и аргентинец, и китаец. А значит, вы должны это учитывать в локализации вашего продукта.


И это совсем не так просто, как кажется. Одним переводом массивов текстов о компании и описаний услуг\товаров ограничиться не получится.



Структурируем всё по пунктам:


1. В одной стране не просто может быть, а скорее и очень быстро потребуется более одного языка. Например, в России потребуется русский и английский. В некоторых регионах необходим татарский, украинский, башкирский. Особенности федеративного устройства.


2. Перевод одного и того же языка для разных стран будет разным. Например, переводы маркетинговых текстов на английский для России и Великобритании будут отличаться. Просто потому что совсем другой культурный, исторический, ментальный бэкграунд. Другие триггеры нужны, чтобы цеплять пользователя. Скажи-ка, дядя, ведь не даром Москва, спаленная пожаром, французу отдана? даже переведённая, эмоционально ничего не скажет американцу. Точно так же, как Twas brillig, and the slithy toves, Did gyre and gimble in the wabe: All mimsy were the borogoves, And the mome raths outgrabe вызовет у американца просто пожатие плеч от бессмыслицы. Зато англичанин захлопает в ладоши.


Небольшой околопример перевода и ситуации в проекте:


На одном из прошлых моих стартапов собрались в 2014м году выкатываться в Индонезию. Продуктовая часть вроде бы была готова, добивали техническую.Примерно за 2 недели до часа Х стучится ко мне в скайп наш Гениальный (время примерно  18:00 мск):- Дим! Ну чё там по срокам?- Всё норм, всё по срокам.- Ты знаешь... Я тут в Джакарту прилетел, у меня завтра в 7:00мск презентация нашего сервиса местным министрам! Успеваешь?OMG...  Ситуация - жесть полная. Но что-то же делать надо? Что у нас в наличии? В принципе действительно всё неплохо, надо пару мест подпилить - и выкатится без пары фич. Тогда преза получится норм. Договариваюсь со всей командой... Ну, как договариваюсь... Кому-то денег обещаю, кому-то отпуск, кому-то увольнение... Решили, короче. Собрались и начали жечь. Не хватает самой малости - листа текста А4 на индонезийском языке. Без него сервис не будет говорить на родном языке индонезийцев. И обойтись никак. Стучусь к Гениальному:- Нужен текст на индонезийском!- Ничем не могу помочь. Я даже на английском не разговариваю!- Эм.... А ты сейчас где? В такси? Можешь попросить таксиста текст перевести с английского на индонезийский?- Не могу. Он не знает никакого языка кроме индонезийского...Ооооооооок! Чешу репу. Снова стучусь к Гениальному:- А отели какие-то знаешь в Джакарте?- Да! Хилтон!Время уже где-то 00:30 мск. Чё делать-то? Терять нечего. Позади Москва. Звоним в отель, объясняем, что мы несчастные русские стартаперы, хотим осчастливить Индонезию своим стартапом, не хватает листа А4 на индонезийском. - Вот наш e-mail, присылайте текст, мы переведём!Через 2 часа мы получили текст и запустились. 

3. Если у вас хоть сколько-нибудь нормальный клиент-сайд интерфейс, то переводы потребуется и для него на стороне клиента.


4. Вам потребуется хранить переводы двух типов короткие фразы (используются для интерфейсов и поясняющих подписей) и длинные тексты (маркетинг, faq, юридическая документация типа оферты и другое).


5. Вам потребуется перевод картинок на языки (причём для разных стран, опять-таки разные). Причём есть картинки, как интерфейсного плана, так и тупо скриншоты. Причём совершенно точно известно, что не все картинки поддадутся автоматизированному наложению генерящихся надписей. И, кстати, для некоторых языков потребуется отдельно доставлять шрифтовые пекеджи.


6. Когда вы соберётесь передавать тексты в перевод, то для каждого короткого текста потребуется комментарий, который описывает контекст, в котором нужен перевод (одно и то же слово, как вы знаете, может иметь несколько значений). Более того, в такой комментарий потребуется точный перечень мест в интерфейс (и как до него добраться), где оно используется. А то напишите для индийской локализации: Это так же легко, как съесть корову и трудно даже представить.


7. Во всех переводах (коротких и длинных) надо не забыть проставить макросы для вставки как минимум доменных имён и валют, если они отличаются.


8. Таймзона в БД имеет значение. И вывод времени в нужной таймзоне тоже. В отдельных сложных случаях без костылей не обойтись. Например, по тайландскому летоисчислению, сейчас 2560 год. Хотя есть мнение, что в базах лучше не иметь никаких таймзон, UTC и только UTC.


9. Проект must be in UTF-8. Иначе вымрете сразу же. Вывод чисел с десятичной точкой в разных странах отличается. Будьте готовы.


10. Поиск по сайту. Каким бы вы не пользовались движком, важно знать все европейские языки, включая русский, процессятся специальным стеммером Snowball. Это такая штука, без которой орфография не будет работать вообще. Но для некоторых языков Snowball не хватит и движок заточенный под работу с ним станет бесполезной обузой. Чтобы решить проблему придётся порыться по интернету и разобраться, какие бывают стеммеры. Да, кстати, для некоторых языков символ пробела (" ") не является разделителем слов (сюрприз!).


Andrey Shetukhin добавляет:
Во-первых, орфография к стеммеру отношения не имеет никакого. Вообще. Ноль.
Во-вторых, тот же сфинкс имеет лемматизатор AOT, изначально написанный Сокирко.
В-третьих, стемминг и лемматизация абсолютно разные вещи, их нельзя путать.
В-четвёртых, 99.995% разработчиков сайтов самостоятельно решить проблему с поиском не могут. Даже в Авито не справились.
Касаемо разделителя слов. Пробел не самая большая проблема.
Гораздо важнее знаки препинания, дефисы, специальные символы и модификаторы UTF. Внезапно, й или ё в UTF-8 можно записать несколькими способами. Слово чёрно-красный, в зависимости от контекста должно искаться не только исключительно как чёрно-красный, но и чернокрасный, чёрнокрасный, чёрный красный.
Знаки переноса так же должны учитываться, иначе в поиске будет треш. Кроме того, если речь идёт о русском языке или текстах на русском и английском языках одновременно, необходимо учитывать опечатки в общих буквах для латинского и кириллического алфавитов.
Необходимо уметь обрабатывать сокращения и понимать, где точка используется как знак конца предложения, а где как элемент сокращения С.С.С.Р., A.C.A.B и т.п.
Всё это очень сложно и большинство разработчиков не имеет достаточного уровня чтобы написать не то, что поиск, а даже токенизатор текста.

11. Никогда не храните исходники текстов в html вы его тупо не сможете отдать на перевод, который тарифицируется посимвольно. Используйте форматы типа wiki-markup или markdown.


12. Никогда не храните в .po файлах (это способ хранения коротких фраз для переводов с использование упомянутого тут всеми gettext) в лексемах что-либо кроме самого текста.


13. Для gettext не используйте в качестве идентификаторов лексем русскую фразу вы не сможете такой набор экспортировать на client-side.


14. Время в базе всегда должно быть в UTC. А таймзоны настройка рендеринга. Как темная и светлая тема, например. (от Сергея Васильева)


15. Не стоит забывать про RTL, про учёт разной плотности на разных языках, про разные сочетания календаря и отображения цифр на разных языках, про сложность выбора формата перевода (.po не очень удобен), про переводческие системы.


Примеры граблей, советов и не всегда с ходу понятных полезностей привёл Дмитрий Симонов ctorecords, СТО и создатель канала Техдирские заметки и его многочисленные умудрённые опытом коллеги.


P.S. А кому хочется с пользой усложнить себе жизнь и добавить знания 1113 декабря пройдёт онлайн-интенсив SRE.

Подробнее..

Рождение вселенной, природа времени и причина корпускулярно-волнового дуализма

31.07.2020 22:07:35 | Автор: admin
Недавно мною была опубликована статья про теорию возникновения Вселенной из базового логического противоречия, которая совершенно справедливо была засыпана минусами за недостаточное раскрытие темы. Поэтому я решил написать пост, в котором мысль и рассуждения из предыдущей статьи представлены в более развернутом и понятном виде.

Только существующие вещи могут быть определены


Все определения рождаются только из нашего опыта. Мы можем дать чему-то определение только, если это что-то существует.

Возможно дать определение чему-то несуществующему. Возьмем, к примеру, пегасов. Их не существует, но мы можем их определить как лошадей с крыльями. Но это определение является суммой из двух определений существующих вещей лошадей и крыльев. Таким образом определения несуществующих вещей получаются только из сложения определений существующих вещей.

Более подробно этот принцип представлен в философии эмпиризма Дэвида Юма.

Рождение вселенной


Возьмем два утверждения.

0 Вселенная не существует !U
1 Вселенная существует U

В начале не было ничего. То есть было верно утверждение, что Вселенная не существует (!U=true). Но чтобы это выражение было верным, нужно чтобы U была определена. Но чтобы U была определена U должна существовать. Значит верно утверждение, что Вселенная существует (U=true). Но тогда оказывается неверным утверждение что вселенная не существует (!U=false). Так мы перешли из состояния 0 в состояние 1. Но если вселенная существует (U=true), то мы можем определить U. А если U может быть определено, то утверждение, что Вселенная не существует могло быть верным с самого начала (!U=true). И так мы возвращаемся назад в состояние 0.

Природа времени


Таким образом мы ходим по кругу в рекурсии между этими двумя состояниями. Если мы запишем все итерации перехода между этими состояниями, то у нас получится стремящаяся к бесконечности последовательность 010101010101 Это и есть время. А каждый переход от одного состояния 1 к следующему 1 через 0 (переход через 0), мы называем моментом.

Исходя из свойств этой последовательности, мы можем сделать вывод, что время имеет начало, но не имеет конца, и течет всегда в одном направлении в будущее.

Природа корпускулярно-волнового дуализма и принцип неопределенности Гейзенберга


Представим себе полностью статичный мир, в котором нет времени. Существует ли в таком мире импульс частицы? Нет не существует, ведь частица всегда будет находится в одном месте пространства. Без времени нету скорости. Без скорости нет импульса. Это значит, что импульс частицы не является частью Вселенной, а является виртуальной величиной, существующий лишь только при существовании времени. А существует ли в таком мире координаты частицы? Конечно, существуют. Существование координат не зависит от существования времени.

Понимая это, мы можем легко понять принцип неопределенности Гейзенберга. Если импульс является только виртуальной величиной, зависимой от времени (переход через 0), а координаты частицы являются реальной величиной, существующей в бытие (состояние 1), то в один момент времени мы можем с точностью определить только один из них.

Такая же природа и у корпускулярно-волнового дуализма. Корпускула часть реального мира (состояние 1), а волна не существует без времени она виртуальна (переход через 0). Именно поэтому любая частица является и частицей, и волной одновременно.

Заключение


Эта противоречивая и не соответствующая критерию Поппера теория в будущем может стать на один уровень с наследием либо Альберта Эйнштейна, либо Тэрри Дэвиса. Что именно ее ждет определит лишь будущее.
Подробнее..

Все есть бит

04.09.2020 20:08:16 | Автор: admin
Бог это вечная и бесконечная истина, не имеющая ценности и смысла.
Барух Бенедикт Спиноза

Сегодня я хочу рассказать вам о самой смелой и красивой гипотезе в современной теоретической физике. Многие ученые относятся к ней крайне скептически, некоторые называют ее откровенно шизофреническим бредом, а другие находят крайне интересной. Давайте же пустимся в путешествие, которое может навсегда изменить ваше представление о Вселенной.



В поисках теории всего


Начиная с середины 20-ого века самой сложной и перспективной задачей теоретической физики является поиск так называемой теории всего, которая объединит в себе общую теорию относительности и квантовую механику, тем самым дав точное объяснение всем наблюдаемым физическим явлениям. На роль такой теории претендуют многочисленные теории струн, теория квантовой петлевой гравитации и многие другие. Но мы будем говорить не о них. Мы сделаем шаг еще дальше.

Профессор MIT Макс Тегмарк в своей книге Наша математическая Вселенная призывает нас задуматься о самом удивительном свойстве всех существующих физических теорий, которое обычно люди считают само собой разумеющимся все наши физические теории описываются математикой.

С точки зрения эмпиризма (философия первичности материи по отношению к идее) в этом нет ничего удивительного, человек изобретал язык математики, наблюдая за реальным миром.Мы изобрели цифры и счет, чтобы считать предметы, мы изобрели геометрию, чтобы строить прочные здания. Со временем наши математические инструменты становились все более сложными и отдаленными от повседневных нужд мы изобретали дифференциалы, интегралы, математический анализ, теорию групп, топологию. Но в конце концов мы всегда находили физические явления, которые поразительно хорошо описывались с помощью этих самых инструментов.

Но давайте взглянем на математичность физических законов с точки зрения идеализма (философия первичности идеи по отношению к материи). Все математические законы живут в пространстве идей и не зависят даже от существования нашей Вселенной. Если даже ничего не существовало бы, дважды два все также равнялось бы четырем. Рождение галактик и звезд, движение планет, химические реакции и генетические мутации строго следовали математическим формулам задолго до появления людей. Мы лишь открыли эти законы, но не изобрели их.

Так что же будет с теорией относительности, квантовой механикой или пресловутой теорией всего, если мы выкинем из них всю словесную шелуху, вроде слов квант, пространство, свет. Там останутся только формулы, и ничего больше. И в этом месте рассуждений Макс Тегмарк задает интереснейший вопрос: что может полностью описываться чистой математикой? И он дает на него единственно разумный ответ. Чистой математикой может быть описана лишь сама чистая математика. Таким образом Тегмарк приходит к самой поразительной из возможных гипотез: вся наша Вселенная это математическая структура.

Все из бита


Макс Тегмарк не был первым, кто пришел к такой идее. Задолго до него эту идею выдвигал знаменитый американский физик, научный руководитель Ричарда Фейнмана, Хью Эверетта и Кипа Торна, а также автор терминов черная дыра и кротовая нора Джон Уилер.

В своей статье it from bit Джон Уилер задумывался над тем фактом, что все свойства элементарных частиц вроде массы, заряда, спина, цвета, странности и красоты не имеют никакого собственного смысла, а лишь проявляются при взаимодействиях с другими частицами. Таким образом, все эти свойства являются по сути битом информации в некоторой математической структуре. Уилер писал:
Все сущее каждая частица, каждое силовое поле, даже сам пространственно-временной континуум получают свою функцию, свой смысл и, в конечном счёте, самое своё существование даже если в каких-то ситуациях не напрямую из ответов, извлекаемых нами с помощью физических приборов, на вопросы, предполагающие ответ да или нет, из бинарных альтернатив, из битов. Всё из бита символизирует идею, что всякий предмет и событие физического мира имеет в своей основе в большинстве случаев в весьма глубокой основе нематериальный источник и объяснение; то, что мы называем реальностью, вырастает в конечном счёте из постановки да-нет-вопросов и регистрации ответов на них при помощи аппаратуры

Чтобы вы лучше поняли, что имел в виду Джон Уилер, я приведу вам в пример картинку из книги Макса Тегмарка о том, как отношения между точками пространства (ребра куба) можно представить в виде матрицы битов:



Сами вершины этого куба, обозначенные индексом от 1 до 8, не несут никакого смысла, а вот матрица отношений между ними (ребер куба) уже обладает некоторыми уникальными свойствами: например, вращательной симметрией. Наша Вселенная, конечно же, устроена на порядки сложнее куба, но в ее основе лежат те же самые принципы. Поняв это, мы можем двигаться дальше.

Инфляционная модель Вселенной и фракталы


Если мы все-таки живем в математической модели, то в какой?

Давайте посмотрим на нашу Вселенную: она состоит из множества скоплений миллиардов галактик, галактики состоят из миллиардов звезд, у многих звезд есть несколько планет, а у многих планет есть некоторое количество спутников. Более того, согласно гипотезе вечной инфляции, являющейся объяснением и расширением инфляционной модели развития вселенной, в отдаленном от нас пространстве ежесекундно происходят миллионы больших взрывов, порождающих свои пузыри Вселенных.

Но вернемся к нашему миру: все скопления, галактики, звезды и планеты, в какой бы части Вселенной они не находились, очень похожи между собой, но все же уникальны. Какая математическая структура обладает такими свойствами? Это фрактал.



Фрактал порождается простейшей рекуррентной формулой, но развивается в красивейшую циклическую картину, каждый маленький кусочек которой одновременно и уникален, и похож на общую структуру.

Асимметрия времени и вычисление рекурсивной функции


И как раз фрактальная структура нашей Вселенной открывает нам глаза на самую главную загадку современной физики время. Идет ли время только вперед? Линейно ли оно?

Современная физика говорит о существовании так называемой асимметрий времени или стрел времени. Первая стрела времени психологическая: мы помним прошлое, но не будущее. Эта ассиметрия является частным случаем более общей второй стрелы времени причинно-следственной. Причины порождают следствия, но не наоборот. С другой стороны это может быть лишь частью нашего восприятия и при обратном ходе времени мы бы приняли причины за следствия, а следствия за причины. Но существуют третья абсолютно объективная асимметрия времени, также называемая вторым законом термодинамики энтропия в замкнутой системе со временем всегда растет. То есть, при обратном ходе времени она бы падала.

Как это можно объяснить? Одним из первых объяснение, согласующееся с гипотезой математической Вселенной, дал немецкий пионер компьютеростроения и автор первого языка программирования высокого уровня Конрад Цузе. Он предположил, что наша Вселенная является не статичной математической моделью, а постоянно вычисляющийся чистой рекурсивной функцией. На вход такой функции поступает результат вычисления предыдущей итерации. Каждый тик такой функции является планковским временем, а проще говоря мгновением. Такая гипотеза очень хорошо объясняет все стрелы времени. Результат вычисления такой функции зависит от ее входа будущее зависит от прошлого, но не наоборот. Со временем количество информации в такой системе будет расти, а значит будет расти и энтропия. И главное, эта гипотеза очень хорошо согласуется с фрактальностью нашей Вселенной, ведь фрактал результат вычисления рекуррентной функции.

Таким образом, мы можем дать определение времени таким образом: время это процесс вычисления чистой рекурсивной функции расчета развития нашей Вселенной.

Вы можете возразить, что наша Вселенная недетерминирована и при коллапсе волновой функции Шредингера результат выхода кванта из суперпозиции непредсказуем. Но согласно многомировой интерпретации квантовой механики Эверетта в момент коллапса волновой функции наша Вселенная просто разделяется на две параллельных реальности, в одной из которых суперпозиция переходит в одно состояние, а в другой в противоположное.

Также стоит учесть, что это время не то же самое, что описывается в общей теории относительности Эйнштейна. Это абсолютное время тики процессора вычисляющего нашу Вселенную.

Матрица и антропный принцип


Но если вся наша Вселенная это вычислительная машина, то как определить, что мы живем не в Матрице? С одной стороны это недоказуемо и неопровергаемо. С другой стороны, если мы живем в Матрице и крутимся на компе у какого-то программиста из реальной вселенной, то его вселенная тоже будет подчинятся законам математики и тоже может оказаться Матрицей второго уровня, которая существует в реальном мире. Этот ряд можно продолжать до бесконечности и ни в одном уровне Матрицы не будет возможности доказать, существует или нет реальный мир более высокого уровня.

В любом случае, у Макса Тегмарка есть более красивое объяснение математичности нашей Вселенной. Для начала зададимся вопросом: почему мы живем именно в такой математической структуре, а не в какой-то другой? Тегмарк находит ответ на этот вопрос в антропном принципе: все непротиворечивые математические структуры существуют, но лишь в немногих из них может зародится такая тонко настроенная Вселенная, которая позволяет существовать нейронным сетям, способным осознать причинно-следственные связи.

Заключение



У гипотезы математической вычислимой Вселенной существуют интересные последствия: герои книг, фильмов, историй и даже ваш выдуманный друг столь же реальны, как и вы сами, так как точно так же являются математическими структурами, придуманными другой математической структурой внутри громадной математической структуры. Это заставляет задуматься над самим значением слова реальность.

Для более глубокого ознакомления с данной темой я рекомендую книгу Макса Тегмарка "Наша математическая Вселенная" и статью в википедии про цифровую физику.
Подробнее..

Как вернуться в прошлое и прожить заново? Как изменить событие в прошлом? Что такое время?

11.01.2021 20:23:30 | Автор: admin

Громкий заголовок статьи, конечно, не означает, что в ней будут прямо даны способы это сделать. Пардон, если дал ложные надежды. Эта статья - один из первых шагов к пониманию сути времени и вариантам управления им (конечно, имеется в виду не тайм-менеджмент) с уклоном на практическую реализацию. Множество людей хотели бы вернуться на некоторое время в прошлое и исправить свои ошибки или даже спасти близкого человека от смерти. К сожалению, наука пока не предлагает нам готовых вариантов для этого, а все теории для перемещения в прошлое основаны на фантастических малореальных кротовых норах, чёрных дырах и сверхсветовом движении, на которых реальную "машину времени" не построишь, нужны более приземлённые варианты.

Здесь приведены мои рассуждения на эту тему и попытки понять, куда двигаться дальше, чтобы откат назад во времени и изменение своего прошлого в реальности когда-нибудь стало возможным, как сейчас с лёгкость возможно то, что ещё 50-100 лет назад казалось фантастикой. Статья написана для тех, кто хочет лучше разобраться в понимании сути времени, а также для мотивирования энтузиастов заняться практическими исследованиями и экспериментами по созданию агрегатов для перемещения в прошлое / изменения его, а также организационной работы по привлечению учёных, лабораторий, финансов для решения этой проблемы.

Я верю, что в будущем (даже в обозримом) появятся какие-то хронотехнологии, которые дадут возможность нам в жизни, как в компьютере, нажать кнопку "отмена".


Перемещение в будущее - уже реальность: при околосветовых скоростях или высокой гравитации время замедляется (например, по сравнению с земным). Для испытателя при этом проходит 1 секунда, а для остального мира, скажем, год (зависит от значения скорости или гравитации). Испытатель попал в будущее на год (без одной секунды) вперёд. Также есть биологический способ: анабиоз (для живых организмов).

С прошлым всё сложнее, так как не разработана не только физика перемещения в прошлое, но даже и логика неоднозначна. Варианты логики при перемещении в прошлое.

  1. Классический вариант. Окружающий мир для испытателя отгоняется назад во времени, а сам испытатель (тело и мозг) продолжают идти в будущее. Например, 25-летний испытатель переместится на 20 лет в прошлое, и ему там так же будет 25 лет, но он ещё и встретит в прошлом себя 5-летнего. Этот вариант маловероятен, потому что можно собрать рядом бесконечное число своих копий, перемещаясь каждый раз всего на секунду назад (каждая копия будет младше предыдущей на 1 сек), и во Вселенной просто не хватит вещества для такого количества копий. А также возникают другие парадоксы типа "убитого дедушки" (правда, его обычно гипотетически решают так, что при перемещении в прошлое создаётся параллельная реальность, с которой путешественник не связан причинно-следственными связями, но это всё лишь теория).

  2. Время отгоняется назад и у окружающего мира, и у испытателя. Тогда при перемещении на 20 лет назад испытатель становится младше на 20 лет, причём и телом, и мозгом, опытом, памятью, знаниями. Он не будет ничего помнить о перемещении во времени, потеряет весь накопленный за 20 лет опыт, в общем, полностью вернётся в свой 5-летний возраст и никакой более младшей своей копии не встретит. И не сможет, конечно, целенаправленно изменить свою жизнь (если только нельзя будет как-то взять с собой эту информацию из настоящего времени). В этом случае также не получится "полететь к динозаврам", а максимум - на время своего возраста. Зато - никаких парадоксов и возможность прожить жизнь заново с самого детства.

  3. "Полететь к динозаврам" без парадоксов, параллельных реальностей и опасности невозврата (см. ниже ПАРАЛЛЕЛЬНЕ РЕАЛЬНОСТИ) можно на "машине времени прошлого read-only". Т.е. такая машина как-то получит точную инфу, ЧТО происходило в таком-то месте 65 млн лет назад, и представит это в виде виртуальной реальности, видеофайла или голограммы, которую мы сможем наблюдать. И это будет не компьютерное моделирование, а самые настоящие события того времени, но мы сможем только наблюдать их, - потрогать, изменить что-то мы не сможем (и слава богу). И конечно, не будет никакой опасности, что динозавры сожрут нас, а древние римляне возьмут в плен. И именно благодаря невмешательству (read-only) в ход истории мы сможем видеть "чистую" историю, никак не изменённую нами.

Зачем многим вообще перемещение в прошлое? Вряд ли для того, чтобы полететь к динозаврам, - большинству скорее требуется изменить какие-то события в своей жизни, например предотвратить смерть близкого человека или изменить свою судьбу. Этакая кнопка "отмена", как в компьютере. Вот на такой результат и должно быть рассчитано практическое применение машины времени для прошлого и физическое её воплощение.

В дальнейших рассуждениях я буду рассматривать в качестве перемещения в прошлое вариант 2 как наиболее реальный и наиболее полезный для жизни каждого человека, кто хочет изменить какие-то события в своём прошлом.

ПАРАЛЛЕЛЬНЕ РЕАЛЬНОСТИ, или почему нельзя предсказать будущее

Если полететь в будущее (путём замедления времени), затем вернуться в прошлое, а потом снова улететь в будущее, то будущее2 будет отличаться от будущего1. Причина в том, что когда мы летим в прошлое, мы стираем всю информацию о событиях, прошедших со времени этого "прошлого" (т.к. отгоняем назад время), т.е. возвращаемся в исходное состояние, в котором у людей и природы снова появляется свобода выбора. И им ничто не может помешать поступить иначе, чем в "первый" раз (будущее1). Конечно, люди будут руководствоваться здравым смыслом и законами физики, но всегда будут случайности, которые и будут вносить изменения.

Что здесь подразумевается? Есть жёсткие причинно-следственные связи и есть случайности. Например, человек работает на заводе и получает травму, с которой он больше не может там работать. Ему приходится уйти с завода. Это жёсткая причинно-следственная связь: если травма - уход с завода, нет травмы - работа на заводе (утрированно). Связь реализуется в любой ветке событийности, она делает ветки похожими друг на друга. А случайность - это всё-таки получит человек травму или нет. Случайность будет отличать одну ветку от другой. Т.е. в любой реальности, где чел получает такую травму, он уходит с завода. В любой, где не получает, - продолжает работать там. Именно за счёт случайностей разные реальности будут отличаться одна от другой. Чем больше случайностей, тем больше отличий.

Т.о., даже слетав в будущее, мы не сможем, вернувшись в прошлое, стать предсказателями, т.к. когда мы вернёмся и будем идти в будущее "в ногу со всеми" (т.е. без замедления своего времени), это будет уже будущее2. В этом и суть параллельных миров/реальностей/Вселенных: каждый раз реализуется новая ветка событийности. Именно это и не позволит нам получать информацию из будущего: его ещё нет, оно создаётся, выстраивается тогда, когда испытатель перемещается в него. (Правда, я исхожу из предположения, что Вселенная многовариантна, что скорее всего так и есть. Если же она инвариантна, то сколько ни летай в прошлое - все "случайности" будут повторяться в точности, т.к. предопределены заранее. Думаю, что это не так.)

Исходя из этого, полететь в прошлое и посмотреть на динозавров грозит опасностью невозврата. Если полететь в прошлое, то вернуться в своё настоящее будет уже нельзя. Например, из 2020-го летим в -5000 до н.э. А затем сразу - обратно, в наше настоящее, в 2020-й (но теперь это для нас будущее). В 2020-й-то мы попадём, но он будет совершенно не такой, из к-го мы прилетели. Скорее всего не будет нашего дома, семьи, города и даже страны! Почему?

  1. Когда мы прилетели в -5000 год до н.э., то вернулись в исходную точку, в которой у людей и природы снова есть свобода выбора. Они ничего не знают о тех 7000 лет, которые мы своим перемещением стёрли. А раз нет информации, то и выбор свой, решения люди будут принимать заново. Конечно, они будут руководствоваться здравым смыслом, но всегда будут случайности, которые будут отличать 7000 лет, нам известные, от 7000 лет новых. Вначале различия будут небольшими, но потом они будут накапливаться, и за 7000 лет отличия будут очень значительными.

  2. В будущее (в 2020-й) летим путём замедления времени, т.е. замедляемся и смотрим, как с огромной скоростью (с нашей точки зрения) человечество и планета начинают проживать эти 7000 лет до 2020-го.

Это ещё круче, чем "эффект бабочки": прилетев в прошлое, даже не надо никого давить: сам факт нашего перемещения в прошлое уже вносит изменение в него и обеспечит другое будущее.

Кроме того, "парадокс дедушки" может произойти, даже если вы его не убьёте в прошлом: это может сделать кто-то другой, да и просто из-за свободы выбора ваши дедушка с бабушкой могут не встретиться в новой ветке событийности.

Физические принципы перемещения в прошлое

Верный способ перемещения в прошлое: т.к. перемещение в будущее возможно уже сейчас, то перемещаемся в будущее вплоть до того момента, когда люди изобретут машину времени для прошлого, и пользуемся.

Что значит "вернуться в прошлое"?

Что значит "вернуться в прошлое" или "вернуть прошлое"? Рассмотрим простую двумерную систему, состоящую из двух материальных точек А и В (рис.1). В момент времени t1 (рис.1) точки находятся по указанным координатам. В момент времени t2 точки перемещаются в другие координаты (рис.2). В момент времени t3 (рис.3) точки возвращаются в исходные координаты, как в момент времени t1. Отличить состояние системы в t1 и t3 нельзя, т.к. в системе нет никаких часов (а есть только две материальные точки). Соответственно, можно сказать, что система вернулась в прошлое, и время t3 - прошлое относительно времени t2. Таким образом, возврат в прошлое - это приведение всех точек системы в исходное положение (т.е. всей системы - в исходное состояние).

Но логикой мы понимаем, что t1<t2<t3, то есть вроде бы никакого возврата в прошлое нет. Парадокс заключается в том, что t1, t2 и t3 - это время по часам внешнего наблюдателя за системой. Т.к. у него часы всегда идут вперёд, он легко сможет сказать, что t3 произошло позже t1, то есть для него система всегда идёт в будущее. А вот по меркам самой системы (внутри системы) никакого t3 нет - есть возврат в прошлое, в t1.

Это проще понять, если использовать систему, состоящую из человека (испытателя) и часов. А снаружи находится наблюдатель со своими часами. Пусть в момент времени t1 часы наблюдателя и испытателя показывают 12:00. В момент времени t2 пусть часы того и другого показывают 12:01. А в момент времени t3 часы наблюдателя продолжают идти вперёд и показывают 12:02. Система же из испытателя и его часов по условию приводится точно в такое состояние, как в t1, т.е. тело и мозг испытателя приводятся в состояние, как в t1, и его часы приводятся к 12:00. Наблюдатель видит, что просто физический процесс прошёл назад, но само время идёт вперёд, т.к. его часы идут вперёд (именно по ним он отмеряет время, кроме того мозг подсказывает ему, что у испытателя сначала было время 12:00, потом 12:01, а ПОСЛЕ этого - снова 12:00, т.е. череда событий идёт в будущее). А для испытателя нет момента времени t3, т.к. при приведении его тела, мозга и часов в состояние, как в t1, он даже не будет знать, что был момент t2, т.к. в момент t1 никакого t2 ещё не было. Для испытателя и его часов именно само время пошло назад.

Это как в СТО: у испытателя, двигающегося с околосветовой скоростью, время замедляется по часам неподвижного наблюдателя, но по часам самого испытателя - ускоряется время наблюдателя. Так и здесь: всё относительно того, с какой стороны системы мы находимся. Итак: перемещение назад во времени это просто восстановление всех материальных точек в исходное состояние. Данный вывод показывает, что "время" гораздо проще, чем пытаются представить некоторые теоретики. Время - это просто движение любой материи. Нет движения - нет времени. Движение в обратку - возврат в прошлое.

А если только часть материи вернётся в исходное положение? Снова обратимся к нашим материальным точкам. Рассмотрим рис.4, к-ый показывает другой вариант реализации событий во времени t3: точка А вернулась в исходное положение, а точка В - нет. Здесь t3 чётко отличается от t1, и уже нельзя сказать, что между t1 и t3 нет разницы. Здесь уже и внутри системы t1<t2<t3. Но если мы мысленно выделим точку А в отдельную подсистему, то по её "часам" t1 и t3 по-прежнему неразличимы, т.е. она вернулась в своё прошлое, а точка В - нет. Т.е. можно назвать это возвратом в прошлое локального участка.

А существует ли прошлое, настоящее и будущее?

Усложним систему, добавив движения точек. Допустим, двигаясь хаотично, они перемещаются так:

1. А (5;6) В (7;8)

2. А (10;15) В (13;18)

3. А (11;4) В (29;5)

4. А (5;6) В (7;8)

5. А (9;11) В (3;16)

6. А (4;54) В (20;20)

7. А (5;6) В (7;8)

8. А (11;4) В (29;5)

9. А (4;54) В (20;20)

1-9 - это моменты времени по внешним часам, идущим вперёд.

Видно, что в момент времени 4 (по внешним часам) и 7 точки расположены так же, как в момент 1, т.е. это два возврата в прошлое. Кроме того, идентичны моменты 3 и 8, а также 6 и 9, т.е. здесь точки также принимают своё прошлое положение (перемещаются в прошлое). Т.е. простая система из двух точек вообще может не иметь направления времени (однако ход времени всё-таки есть). И здесь неважно, что в исходную позицию точки возвращаются не по тому же пути, по которому они ушли из этой позиции, потому что нам важно лишь их положение. В таких простых системах, которые лишь колеблются около исходной позиции, никуда не стремясь, действительно нет направления времени, такие системы не развиваются, и нет никакой возможности отличить их "предыдущее" состояние от "последующего".

Более сложные системы, которые всегда двигаются направленно и развиваются, всегда имеют направление времени.

Теперь о внешних часах. В них главное движение - это вращение стрелок (износ механизма и пр. не рассматриваем, т.к. для нас он незаметен). Т.к. часы много раз могут принимать одно и то же положение (например, 12:00), то можно сказать, что они некоторых образом возвращаются в прошлое. И действительно, по часам мы можем сказать лишь о времени суток (точнее, об одном из двух его вариантов, т.к. на циферблате 12 часов, а в сутках - 24), но не о дне недели и месяце года. Если бы у нас не было календаря и других внешних признаков, то никакой разницы между сегодняшними показаниями часов 12:00 и вчерашними не было бы. И мы не смогли бы сказать, следующие это 12:00 или прошлые. Это очень легко проверить, если сесть в комнату, куда не будут проникать внешние раздражители. Посмотрев на часы и зафиксировав 12:00, вы откладываете их и какое-то время не смотрите на них. Потом снова смотрите и видите, например, 11:00. Какие гарантии того, что стрелки в это время не шли назад на час? Или не перепрыгнули на 11:00 и остановились. Логикой мы это понимаем, но стопроцентно утверждать не можем (можно ведь изготовить такие часы, которые будут специально иногда двигать стрелки назад, перепрыгивать). Итак, даже идущие часы не могут служить признаком движения времени вперёд в отсутствие других внешних факторов.

Подходя к понятию времени с таких позиций, можно сказать, что действительно не существует "прошлого", "настоящего" и "будущего". Но это только для простых систем. Более сложные практически никогда не возвращаются в исходное положение, поэтому всегда видна направленная стрела времени.

Итак, что же такое время?

Таким образом, время - это просто движение. Нет движения (любого, хотя бы на уровне элементарных частиц) - нет времени, потому что в неподвижном мире просто невозможно будет отличить один момент времени то другого, поэтому понятие времени просто будет бессмысленно. С другой стороны, чтобы человек мог ощущать ход времени, движения только на микроскопическом уровне недостаточно - должны происходить значимые для человека события. Именно по их смене он и чувствует ход времени. А если человека посадить в камеру без часов, без смены дня и ночи, то скоро он просто запутается и не сможет сказать, прошёл час или день, более того, его циркадные ритмы тоже собьются.

Итак, время - это движение. Пока существует движение - существует время, ход времени. Вернуться назад во времени - вернуть все точки (частицы) в исходное положение. Чьи точки возвращаем в исходное положение - тот объект и возвращается в прошлое. Вернём точки своего организма - наш организм переместится в прошлое (тело, мозг, т.е. мы станем младше). Проделаем это с каким-то предметом - вернется в своё прошлое он, даже если наши часы будут идти вперёд и показывать, что время движется тоже вперёд. Мы уже говорили, что часы по отношению к этому предмету - внешний объект, с точки зрения которого предмет идёт в будущее, хоть точки предмета и возвращаются в исходное состояние. Но для самого предмета это будет именно возвращением в прошлое.

Так как время - это движение, и движение в трёхмерном пространстве (т.е. именно ДВИЖЕНИЕ в трёхмерном пространстве и является четвёртым измерением - временем), а двигаться в нашем пространстве мы можем вперёд и назад и вообще произвольно в любую точку, то потенциально мы можем и перемещаться назад во времени. Для этого необходимо ВСЕ точки локального участка пространства, который мы хотим вернуть в прошлое, заставить двигаться назад, т.е. по обратной траектории. Впрочем, достаточно даже просто вернуть все точки этого локального участка в исходное состояние, но необязательно по тому же пути. При этом какие-то частицы покинули макрообъект, какие-то - добавились с того времени, в какое мы хотим его вернуть. И у нас нет никакой информации о положении частиц в прошлом. Есть только текущее положение частиц - это единственное, от чего можно отталкиваться.

Если пользоваться определениями термодинамики, то можно сказать, что одному макросостоянию системы соответствует множество микросостояний. А так как нас интересует именно возврат в прошлое на макроуровне (нашем уровне, а не уровне элементарных частиц), то достаточно привести локальный участок в любое из микросостояний, которое отвечает нужному макросостоянию. Т.е. необязательно двигать все атомы и молекулы строго в обратном направлении - нужно лишь добиться их такого положения, которое отвечает исходному макросостоянию нашего локального участка, т.е. достаточно восстановить структуру макрообъекта. Но даже с таким допущением всё равно нет полной информации о структуре (например, мы хотим восстановить мозг умершего человека - где мы будем брать информацию о связях нейронов?).

У нас нет никакой информации о микросостояниях и даже подробной информации о макросостоянии какого-либо участка пространства в прошлом. Всё, что у нас есть, - это дата прошедшего события, куда мы хотим вернуться (точнее, на какой момент хотим восстановить макросостояние), и наша память о некоторых деталях этого прошлого. И ещё есть текущее положение частиц в выбранном локальном участке (которым, кстати, может быть вся наша планета, если мы говорим о таком перемещении назад, как описывается в фантастике) - это единственное, от чего можно отталкиваться. Надо как-то вернуть частицы в их исходное положение (точнее, вернуть в любое из микросостояний, отвечающих нужному макросостоянию).

Второй закон термодинамики запрещает уменьшение энтропии в замкнутой системе. Например, возьмём три разных деревянных предмета и сожжём их. Во всех трёх случаях у нас получатся одни и те же газы (пар, углекислота, оксиды серы, азота и пр.), и сказать, какому предмету соответствовал данный набор газов - невозможно (если не знать заранее). А для возврата в прошлое требуется восстановить эту информацию и затем положение частиц.

Впрочем, вся человеческая деятельность - это именно уменьшение энтропии, уменьшение хаоса и возведение нужного людям порядка. Однако это в открытой, отдельно взятой системе, и на это приходится тратить немало энергии. Мне неизвестны пока методы, способные полностью обратить процесс вспять (хоть по тому же пути, хоть по контрольным точкам прошлого). Конечно, если со стола упала ручка - мы можем её поднять и положить на то же место. Это локальный возврат в прошлое (разумеется, без учёта старения материала самой ручки). Ещё пример: если человеку оторвало руку - часто её можно пришить обратно, это тоже возврат в прошлое на определённом уровне. Но для более сложных систем полноценный возврат в какое-то произвольно взятое исходное состояние очень сложен в условиях современных технологий. Поэтому локальный возврат в прошлое - это во многом инженерная проблема, и удел энтузиастов - взяться за её решение (хоть в теории, на чертежах, хоть на практических испытаниях - лишь бы был интерес).

Есть ли способы обхода этого закона для нужной нам цели? Мне такие неизвестны, однако есть немало случаев, когда один закон физики обходит другой, как бы доминирует над ним. Например, есть сила земного притяжения, которая не позволяет людям парить в воздухе. Однако птицы и самолёты могут летать - они для этого используют законы аэродинамики. Т.е. аэродинамика оказалась сильнее притяжения и обошла (но не отменила!) закон тяготения. Другой пример из микробиологии: известно, что содержание натрия вне клеток выше, чем внутри клеток. По закону осмоса натрий должен устремляться в клетку, где его меньше. Однако внутри клетки есть механизм, который выталкивает ионы натрия, как только они туда поступают. Т.е. этот механизм обходим законы осмоса (но, опять же, не отменяет их, т.к. по закону осмоса натрий всё равно стремится попасть в клетку, только ему мешают). Отсюда следует, что нужно вести поиск такого закона, явления природы/науки, который бы обошёл термодинамические запреты.

С другой стороны, возможно, что исходя из текущего положения частиц в локальном участке можно как-то получить информацию об их предыдущем местоположении, а дальше вернуть их в него - дело техники. Квантовые компьютеры? Нейросети?

Возможно, это будет что-то вроде блокчейна: там каждый следующий блок содержит ссылку на предыдущий (точнее, хэш предыдущего), таким образом можно распутать всю цепочку. Может быть, с положениями частиц точно так же: по текущему можно будет вернуть предыдущее и так далее, по частям (микрошагами). Для этого надо попытаться вернуться назад во времени (или вернуть назад во времени локальный участок пространства) хотя бы на секунду, миллисекунду, планковское время.

Одним из препятствий видится то, что развернуть назад события всей Вселенной невозможно, потому что потребуется аппарат размером больше самой Вселенной. Однако вспомним про перемещение в будущее путём замедления времени: для замедлившегося человека видится, что окружающий мир, все процессы в нём ускорились, а по окончании замедления времени он увидит, что это не мираж, а действительно мир убежал вперёд во времени. Однако человек здесь не создавал никакого гигантского аппарата для ускорения времени всей Вселенной! Однако эффект ускорения времени окружающего мира проявился и без этого.

Отсюда следует, что нам необходимо что-то подобное и для отгона Вселенной в прошлое: не реальный аппарат размерами больше Вселенной, а эффект, относительно которого время Вселенной будет идти назад.

Чтобы найти тот закон природы, создать такую технологию, которая позволит нам вернуть прошлое, необходим труд учёных, конструкторов, инженеров. Интернет позволяет нам связаться с разными людьми и организациями, и этим надо пользоваться. Сильно заинтересованный человек (а также отчаявшийся человек, для которого изменение прошлого - единственная надежда) может приложить немало усилий для организации процесса поисков, сбора средств на исследования, да и самостоятельных изысканий тоже (если он достаточно подкован в соответствующих областях науки). Наверняка придётся задействовать немало ресурсов, перелопатить кучу информации, найти разные исследовательские группы по вопросу перемещения в прошлое. Это того стоит. Не факт, что современные технологии вот так сразу позволят нам отогнать время (читай: физические процессы) в обратном направлении, однако прогресс с каждым годом развивается всё стремительнее.

Не правда ли, парадоксально: прогресс должен идти вперёд, чтобы научиться возвращаться назад?! Но как бы фантастично это ни звучало, в некоторых ситуациях возврат прошлого, возврат в прошлое - единственный способ для людей и конкретного человека исправить ситуацию (например, спасти человека от смерти, точнее, после смерти вернуть его из прошлого живым). Значит, надо обойти этот парадокс.

Какие доступы к прошлому у нас уже есть? Самое очевидное - это фото, видео, аудио, которые могут показать нам то, что мы успели отфиксировать. Эта информация может храниться сколь угодно долго после того, как отфиксированные события давно ушли в прошлое. Но мы можем возвращаться к ним всякий раз, когда захотим. До изобретения фото- и видеотехники такое, конечно, было недоступно (рисовать всё-таки сложнее и менее точно). Сейчас же мы без проблем смотрим фотографии наших молодых бабушек и дедушек, которые уже давно умерли. Следующий шаг прогресса по сохранению (точнее, восстановлению) прошлого - это восстановление его по какой-то остаточной информации в выбранном участке пространства. То есть, такой прибор должен восстановить положение всех частиц в выбранном локальном участке. Причём если он просто высчитает их расположение и покажет нам, то мы увидим только картинку, а если сможет прямо собрать, как конструктор, то получим предмет/человека наяву.

Насколько это сложно? Чем дальше в прошлое, тем бОльшие вычислительные мощности нужны. Уже появились квантовые компьютеры, которые приближаются к решению подобных проблем. Они работают на порядки быстрее даже суперкомпьютеров. Физикам и инженерам удалось организовать систему так, что она превзошла все предыдущие.

Однако мало иметь мощные технологии, важно ещё направить их в нужное русло. Много ли лабораторий всерьёз занимается исследованиями, как вернуть/изменить своё прошлое? Большинство же учёных (тот же Стивен Хокинг) уверяют нас, что это невозможно, нарушится причинность и т. д. Как видите из статьи, эта проблема обходится. Иными словами, для решения такой проблемы нужно подключать как можно больше мыслящих людей, использовать мозговой штурм и организовывать процесс исследования.

Нужна ли нам машина времени для прошлого? Безопасно ли изменять прошлое?

С одной стороны, возврат в прошлое откатит и все технологии обратно, в прошлое. Такое людям не нужно. С другой стороны, можно локально вернуть прошлое, например, спасти погибшего человека, изменив прошлое, или изменить собственное решение в прошлом, не затрагивая прошлое положение частиц других людей (по крайней мере, напрямую). Поэтому, обходя все парадоксы и отрицательные явления, решая спорные моменты, можно сказать, что технология возврата прошлого, утраченного локального состояния вполне имеет право на существование.

Существуют опасения, что кто-то отправится в прошлое, чтобы глобально изменить мир, например, изменить исход второй мировой войны. И что мир при этом может просто перестать существовать. Уверяю вас, это не так. Атомными бомбами тоже можно уничтожить всё человечество, однако же ядерное оружие создано в немалых количествах, а человечество живо и здорово. Или, например, огнестрельным оружием люди могут перестрелять друг друга, значит, оно тоже опасно. Тем не менее в Америке и других странах огнестрел есть у многих, но ни одна страна пока от этого не вымерла. Поэтому проблема опасности машины времени - исключительно надуманная проблема. В любом случае, за изменением прошлого, когда такое станет возможно, будет поставлен контроль. Кроме того, большинству людей (даже президентов, чиновников и пр.) вряд ли придёт в голову всерьёз отправиться в прошлое, чтобы глобально изменить мир. Скорее, как и обычные люди, они захотят подправить в прошлом какие-то события своей жизни. А это - самое оправданное применение машины времени для изменения прошлого.

Друзья, пишите в комментариях мысли на эту тему, устраивайте мозговой штурм, подавайте свежие идеи, как практически воплотить в реальность перемещение назад во времени и исправить там свои ошибки (желательно на современном уровне развития технологий). Не зацикливайтесь на ТО Альберта, она работает для перемещения в будущее; возможно, для прошлого подойдёт другой принцип.

Подробнее..

Перевод Как избежать парадокса убитого дедушки или Квантовая механика решает загадки путешествия во времени

19.02.2021 14:12:20 | Автор: admin

Путешествия во времени давно стали основным жанром фильмов, романов и телевизионных шоу, которые служили всем: от заднего плана до подростковых весёлых комедий, таких как Назад в будущее, и вдумчивого созерцания в новелле Рэя Брэдбери И грянул гром. Часто эти истории сосредотачиваются вокруг одного аспекта путешествия во времени возможных последствий того, что путешественник во времени поменяет прошлые события, в результате чего по возвращении в будущее произойдёт кошмарный сценарий, влияя на способность вообще путешествовать в прошлое.

Эта дилемма, известная как парадокс убитого дедушки, отражает главное возражение философов и физиков против путешествий во времени возможное нарушение причинности. В то время как само путешествие во времени остаётся в области чистой спекуляции, возможные результаты нарушения принципа причинности и то, как природа может предотвратить их, являются горячо обсуждаемыми темами, с такими известными физиками и философами, как Стивен Хокинг и Кип Торн, размышляющими о возможных решениях. Возможно ли, что многомировая интерпретация квантовой механики может спасти несчастного (и неуклюжего) путешественника во времени?



Что такое парадокс убитого дедушки, и почему он так важен?


Парадокс убитого дедушки излагает гипотетическую ситуацию, в которой путешественник во времени отваживается вернуться в прошлое только для того, чтобы непреднамеренно вызвать событие, которое приводит к тому, что путешественник во времени вообще никогда не существовал (обычно случайная смерть дедушки) или какое-то другое событие, которое делает путешествие невозможным. Парадокс возникает потому, что если путешественник во времени никогда не существовал, как он мог отправиться в прошлое, чтобы совершить отцеубийство? Таким образом, сама идея путешествия во времени поднимает возможность нарушения причинности причина всегда должна предшествовать её следствию.

Фрай, неуклюжий главный герой Футурамы Мэтта Грейнинга, имел довольно неудачную встречу со своими дедушкой и бабушкой в титулованном эпизоде Розуэлл, который хорошо кончается

Чтобы рассмотреть этот парадокс, давайте представим себе ситуацию, в которой одарённый молодой изобретатель Марти создаёт машину времени в 2018 году. Поскольку Марти никогда не видел своего деда, он решает совершить путешествие в прошлое, чтобы встретиться с ним. После тщательного исследования Марти выясняет, где именно будет находиться его дед, ещё молодой и бездетный, 23 ноября 1963 года. Он входит в свою машину и начинает путешествие в прошлое.

Набросок путешествия Марти, если мы допустим существование только одной мировой линии

К сожалению, Марти очень буквальный парень, и, когда мы сказали, что он точно знает, где будет находиться его дед, это не было преувеличением. Марти приземляется точно в том месте, где должен был быть его дед, с предсказуемыми результатами. После быстрого анализа ДНК, чтобы убедиться, что это действительно его дед, Марти терпеливо ждёт своего исчезновения

Решение дилеммы Марти?


Физики и философы размышляли над несколькими решениями этого парадокса. Принцип самосогласованности Новикова, также известный как Закон сохранения истории Нивена, разработанный русским физиком Игорем Дмитриевичем Новиковым в конце 1970-х годов (Эволюция Вселенной (1979)), предложил использовать геодезические (аналогичные тем, которые используются для описания кривизны пространства в общей теории относительности Эйнштейна) для описания кривизны времени. Эти замкнутые временные кривые (CTCS) предотвратили бы нарушение любых причинно-связанных событий, лежащих на одной и той же кривой. Подход также предполагает, что путешествие во времени возможно только в тех областях, где эти временные кривые существуют, например в червоточинах, как предположил Кип Торн и его коллеги в статье 1988 года Червоточины, машины времени и состояние слабой энергии. События будут цикличными и самосогласованными. Это подразумевает, что путешественники во времени не смогут изменить прошлое, независимо от того, физически ли им препятствуют или они действительно не имеют возможности сделать это. Поэтому, как бы Марти ни старался, он не смог бы приземлить свою машину именно в этом месте, даже если бы намеревался убить своего деда.

Эта идея была расширена студентами Калифорнийского технологического института Фернандо Эчеверриа и Гуннаром Клинкхаммером вместе с физиком Кипом Торном в статье, где было показано, как бросить бильярдный шар в прошлое через червоточину по траектории, которая будет препятствовать попаданию шара в червоточину. Они утверждали, что физические свойства червоточины изменят траекторию шара таким образом, что он не сможет мешать самому себе, или что именно вмешательство это то, что в первую очередь заставляет шар войти в червоточину.

Визуальное представление решения Эчеверрии и Клинкхаммера (Brightroundircle)

Итак, согласно теории Новикова, любые действия, предпринимаемые путешественником во времени, просто становятся частью ранее существовавшей истории, и наблюдателям запрещается видеть эти события из-за так называемого горизонта Коши.

Вернувшись в 2018 год, наш герой Марти обнаруживает, что его семейный дом исчез, как и все следы его существования. Читая о теории Новикова и пересекающихся бильярдных шарах, он проклинает бездеятельность Вселенной. Именно в этот момент он понимает, что, возможно, Вселенная не вмешивалась, поскольку для этого требовалось какое-то другое корректирующее действие. Вдохновлённый статьёй о столкновении бильярдных шаров, он мчится к своей машине времени, чтобы помешать себе и спасти собственное будущее.

Решение Новикова может показаться вам несколько произвольным, поскольку оно, безусловно, требует существования множества механизмов, в настоящее время неизвестных физике, достаточных для того, чтобы навсегда затупить бритву Оккама. Именно по этой причине такое решение парадокса убитого дедушки обычно отвергается научным сообществом. Физик Мэтт Виссер, исследователь в области общей теории относительности, предполагает, что принцип самосогласованности Новикова слишком заточен под конкретный случай, чтобы его можно было принять как спасение причинности.

Есть ли более экономное решение парадокса убитого дедушки, основанное на ранее существовавших аспектах физики, введённых другими теориями или дисциплинами?

Так уж случилось, что одно из таких решений может дать хорошо известный аспект квантовой физики: интерпретацией квантовой механики многих миров.

Многомировая интерпретация квантовой механики


Многомировая интерпретация квантовой механики была впервые предложена Хью Эвереттом III в 1950-х годах как решение проблемы коллапса волновой функции, продемонстрированной в печально известном эксперименте Юнга с двумя щелями.

Основная схема двухщелевого эксперимента. Электроны могут проходить через одну из двух щелей

Электроны, выпущенные по отдельности, начинают создавать характерную интерференционную картину на экране за пределами двух щелей

Поскольку электрон движется, его можно описать как волновую функцию с конечной вероятностью прохождения либо через щель S1, либо через щель S2. Когда электрон появляется на экране, он не размазывается по нему, как волна. Он проявляется как точка. Мы называем это коллапсом волновой функции, поскольку волнообразное поведение исчезло, и это ключевой фактор так называемой копенгагенской интерпретации квантовой механики. Но оставался вопрос: почему волновая функция коллапсирует?

Эверетт задал другой вопрос. Коллапсирует ли вообще волновая функция?


Он представил себе ситуацию, в которой вместо коллапса волновой функции она продолжает расти экспоненциально. Настолько, что в конечном счёте вся Вселенная оказывается лишь одним из двух возможных состояний. Мир, в котором частица прошла через S1, и мир, в котором частица прошла через S2. Эверетт также утверждал, что одно и то же расщепление состояний произойдёт для всех квантовых событий с различными исходами, существующими в разных мирах в состоянии суперпозиции. Волновая функция просто выглядит так, как будто она разрушилась для нас, потому что мы находимся в одном из этих миров, которым запрещено взаимодействовать.

Это значит, что когда Марти вернётся в 1963 год, произойдет раскол. Он больше не в том мире, из которого пришёл, назовём его Миром 1. Вместо этого он создал новый мир. Когда он путешествует вперёд во времени, он путешествует по временной шкале этого мира. Он никогда не существовал в этом мире и, по правде говоря, не убивал своего деда. Его дед существует в целости и сохранности ещё в 1963 году в Мире 1.


Набросок путешествия Марти, если применить многомировую интерпретацию квантовой механики. Попытка путешествовать назад вызывает нисходящий прыжок мировых линий

Итак, что происходит, когда Марти возвращается в прошлое в попытке спасти свой мир? Он непреднамеренно создаёт другое состояние, Мир 3. Этот мир может походить на Мир 1 почти всеми мыслимыми способами, но, согласно применению интерпретации, он не является тем же самым из-за одного события. Столкновение двух машин времени 23 ноября 1963 года.

Если Марти снова попытается вернуться в 1963 год, чтобы исправить первоначальное вмешательство, он просто создаст другое состояние, Мир 3

Правда в том, что если это правильное решение парадокса убитого дедушки, то Марти никогда не сможет вернуться в Мир 1. В интерпретации многих миров заложено, что наложенные миры не могут взаимодействовать друг с другом. Марти может двигаться вниз, потому что именно его присутствие в определённый момент создает мир. Невзаимодействие означает, что независимо от того, какие меры он принимает, каждый раз, когда он возвращается в прошлое, он создаёт новый мир и прыгает вниз в этот мир, а затем может двигаться только вперёд во времени по этой линии. Где-то в своём мире, Мире 1, Марти, изобретательный молодой человек, просто исчез в один прекрасный день, чтобы никогда не вернуться.

Заключение


Конечно, ничего из этого не делает путешествия во времени более возможными или вероятными. Специальная теория относительности Эйнштейна и ограничения на скорость объекта с массой сильно ограничивают эту возможность. Но это даёт интересное решение к логической головоломке. Ирония заключается в том, что наиболее правдоподобное решение парадокса убитого дедушки исходит из единственной концепции в физике, которая создала ещё более фантастические истории и приключения, концепции множественных Вселенных, даже если в этом случае упомянутые вселенные сосуществуют в невзаимодействующей гипотезе состояний.

Интерпретация многих миров предполагает почти бесконечное количество возможных миров, даже тот, где вы решили отрастить бороду и стать злее.

Интересно, что это приложение также отвечает на другую загадку, часто задаваемую о путешествиях во времени. Если такая технология когда-нибудь перейдёт от диких предположений к реальности, то где же путешественники во времени? Почему они не пришли к нам, чтобы обсудить своё удивительное открытие?

Ответ может заключаться в том, что мы существуем в первичном мире, в котором будут созданы машины времени. Изобретатели и пассажиры таких машин просто исчезают в других мирах собственного творения. Для нас изобретение машин времени будет просто отмечено множеством исчезающих физиков.

Оказывается, Марти было легко.

image
Узнайте подробности, как получить Level Up по навыкам и зарплате или востребованную профессию с нуля, пройдя онлайн-курсы SkillFactory со скидкой 40% и промокодом HABR, который даст еще +10% скидки на обучение:

Подробнее..

Учет рабочего времени MeteorHR

14.09.2020 06:15:13 | Автор: admin

Здравствуйте! Меня зовут Руслан и разрабатываю сервис meteorhr.com для управления персоналом, который позволит Вам сосредоточиться на людях, а не процессах. Разработка сервиса я занимаюсь полностью один и сервис бесплатный.

Сегодня сервис поможет Вам наладить учет рабочего времени с использованием функции мобильного телефона геопозиции. Сотрудник отмечает свой приход (уход) на работу (с работы) в мобильном приложении, и если позиция совпадает с данными позиции офиса происходит отметка. В сентябре я планирую добавить в приложение дополнительно авторизацию к геопозиции по сканированию лица. Приложение написано на Flutter, вчера разместил в play market жду валидации от google.

Сегодня я занят в разработки удобных графиков выходов, что бы сравнивать время и дату графика и фактическую дату и время отметки, для ведения рейтинга сотрудников и табеля рабочего времени. На основание табеля рабочего времени сотрудник в реальном времени видит свою ЗП.

Я бы хотел обратиться к сообществу habr за помощью, так как мой проект бесплатный мне нужны тестировщики, может кто-то поможет с дизайном приложения, у кого-то есть идеи, прощу дать информацию на email: ruslan.khissamov@meteorhr.com. Одна из целей помочь мало бизнесу.

Всем заранее спасибо за обратную связь.

PS Пишу не так часто, особенно статьи, поэтом сделайте скиду если что-то не так. ;)

Подробнее..

Античные хакеры как древнегреческий ученый Ктесибий научился время измерять

13.06.2021 14:11:44 | Автор: admin

Люди чаще всего интересуются погодой и временем любым, включая время суток, время ухода с работы, продолжительность антракта в театре или режим работы государственного учреждения. Погоду, вероятно, люди начали обсуждать сразу же после появления речи.

Со временем все несколько иначе. Понятно, что люди всегда ощущали течение времени. Закаты и восходы, смена сезонов, все это наши предки видели и пытались каким-то образом не только интерпретировать, но и использовать в быту. Но одно дело приблизительно определять время, и совсем другое точно его измерять. Одним из тех, кто изобрел шкалу времени стал древнегреческий инженер, живший в Александрии примерно в 250 г. до н.э.

Насколько известно ученым, Ктесибий научился не только измерять время, но и изобрел орган, что дало толчок развитию пневматики. Его можно назвать одним из первых хакеров в изначальном понимании этого термина.

Наверное, вы слышали о приборах, которые измеряли время и существовали задолго до Ктесибия. Это правда, но у таких систем были существенные недостатки. Например, те же солнечные часы измеряют время лишь при наличии Солнца. Ночью или в облачный день они ничего не способны измерить. Аналогичным образом дело обстоит и на корабле попавшем в шторм никакие солнечные часы ничего не покажут.

Вода и время


Одним из первых почти универсальных инструментов для измерения времени была клепсидра. Это, фактически, сосуд с носиком у донышка, который можно закрывать пробкой. Греки использовали такие сосуды в ходе судебных прений для того, чтобы участники имели равное время в ходе обсуждения проблемы.


Хронометрист наполнял клепсидру до нужно отметки и открывал пробку. Как только вода прекращала течь оратору нужно было замолчать. Носик клепсидры можно было закрыть и в ходе каких-либо непредвиденных обстоятельств, пауз.

Клепсидры древнейший прибор для измерения времени. Они существовали в Китае в 4000 году до н.э. и в Индии в 2500 до н.э. В Египте они появились примерно в 1400 н. до н.э. Возможно, существовали они задолго до этих дат, просто археологам известны находки именно с такой датировкой. Кстати, у китайцев и индусов конструкция клепсидры была не такой, как у других народов. У них пустая чаша с небольшим отверстием плавала в емкости с водой. Таким образом, время отмерялось благодаря заполнению чаши жидкостью.


Проблема с клепсидрой в том, что из-за давления воды напор струи из носика тем сильнее, чем больше самой воды в сосуде. И одной клепсидрой нельзя измерить равные промежутки времени, подставив, например, другой сосуд (чашку) под носик. Первая чашка наполнится быстрее, чем последняя.

Ктесибий, насколько известно, работал в цирюльне своего отца. Как по-настоящему назывались предприятия такого типа в 200 г до н.э. мы не знаем. Но, судя по всему, там всегда было много воды. И именно она навела Ктесибия на мысль о создании точного прибора для измерения времени. Благодаря этому человеку мир получил самую точную до момента появления маятниковых часов систему, позволявшую измерять время. Клепсидры в том либо ином виде массово использовались вплоть до 1656 года.

Как это работает?


Ктесибий усовершенствовал традиционную клепсидру. Он поместил более емкий сосуд с водой над клепсидрой. И этот источник наполняет саму клепсидру быстрее, чем вода сливается из носика. Более того, у клепсидры появился специальный клапан для избавления от лишней воды.

С течением времени Ктесибий стал изготавливать все более совершенные и сложные механизмы.


Здесь 1 спрятанная трубка подачи воды (2) из источника (3). Вода, подаваемая по трубке, поступала в слезные протоки амура, так что казалось, что он плачет. Затем эти слезы попадали в проток (4), после чего в резервуар (5). Это был еще один резервуар внутри клепсидры. Внутри был поплавок (6). Когда вода поднималась, то поднимался и он. К поплавку, в свою очередь, был прикреплен амур, указывающий перстом на определенное деление цилиндра (9). Деления означали часы, причем было разделение на часы от заката до восхода и от восхода до заката. 1 час был равен 1/12 дня или ночи. Клепсидра была настолько точной, что позволяла учитывать разницу в продолжительности дня и ночи в летнее и зимнее время. Кроме того, клепсидра показывала и дни года, для чего служили вертикальные линии (11). Всего эти линии делили цилиндр на 365 частей. Чем больше был уровень воды в резервуаре, тем сильнее наполнялся сифон (12). Когда уровень воды достигал изгиба этого сифона, то вода выливалась в сосуд (13) на одной из лопастей вращающегося колеса (14). Затем колесо под тяжестью воды проворачивалось, вызывая вращение зубчатого колеса (15). Оно, в свою очередь, поворачивало ось (16) и цилиндр на 1/365 часть окружности. Соответственно, амур указывал перстом уже на следующий день. Ну а сосуд (13) при повороте колеса (14) опрокидывался, и вода попадала в новый цикл.

Какими бы точными ни были водные часы, у них была проблема. И эта проблема вязкость воды, которая меняется в зависимости от температуры. В разное время года часы шли немного по-разному. Понятно, что при нуле градусов часы не могли функционировать вообще, что тоже было недостатком.

Что еще изобрел Ктесибий?


Предполагается, что он был автором еще нескольких изобретений, включая:
  • Гидравлический орган, где тоже использовалась вода для выравнивания давления. Сначала конструкция органа включала поршневые насосы, затем их заменили мехами, да и воду убрали, установив регулирующих давление мех.
  • Поршневой насос для подъема воды из колодцев остатки насосов найдены археологами в ряде поселений.
  • Основные детали насоса, изобретенного Ктесибием, присутствуют в современных ручных пожарных насосах, так что изобретение живет в веках.
  • Оружие камнестрел и самострел, которые работали на сжатом воздухе.

Считается, хотя и не доказано, что Ктесибий был первым заведующим Александрийского Музея. Он написал научные работы об использовании сжатого воздуха в разных механизмов. Стал основателем таких дисциплин и направлений, как пневматика, гидравлика и теории упругости воздуха. К сожалению, о работах Ктесибия известно из сохранившихся сообщений других деятелей того времени. Так, о сочинении Hypomnevata (Записки) с изложением основ пневматики нам известно из трудов Витрувия. Работы Ктесибия, к сожалению, не дошли до нашего времени.

Один из кратеров на Луне был назван в честь Ктесибия такое имя объекту дал Международный астрономический союз в 1976 году.
Подробнее..

У английского языка нет будущего (времени)

26.03.2021 20:17:43 | Автор: admin

Английский язык не имеет будущего времени, потому что у него нет ни словоформ будущего времени в том виде, в котором они есть в других языках, ни других грамматических форм, которые могли бы обозначать только будущее время.

Мартин Эндли, доктор наук по лингвистике, автор книги Linguistic Perspectives on English Grammar: A Guide for EFL Teachers.

Но погодите. А чем тогда является Future Tense, который изучают уже на уровне Elementary?

На самом деле, все сложнее, чем кажется на первый взгляд. Говорим сегодня о будущем времени в английском языке. Поехали.

Такое разное будущее

Абсолютное большинство людей, в том числе и носителей языка, считают, что предложения I will do that и Im going to do that однозначно считаются будущим временем.

Они действительно описывают события, которые произойдут в будущем, поэтому глупо думать иначе.

Но уже здесь для простого студента, который учит английский как второй, начинаются сложности. Потому что будущее время это не только Future Simple. Его можно выразить и через настоящее.

Выглядит это как намерение осуществить что-то.

Im going to do my homework. Я собираюсь сделать домашнюю работу.

Основной глагол здесь собираюсь и он в настоящем времени. Но словосочетание going to обозначает намерение сделать что-либо в будущем. Оно двусмысленное, ведь конкретное время здесь не указано.

Im going to do my homework now. Я собираюсь сделать домашнюю работу сейчас.

Im going to do my homework tomorrow. Я собираюсь сделать домашнюю работу завтра.

Второй вариант вполне легальный грамматический ход. Хоть предложение остается в Present Continuous, оно недвусмысленно говорит о будущем.

В ряде случаев будущее можно выразить и через Present Simple.

When will you visit the park next time?

I visit the park on Sundays.

Когда ты пойдешь в парк в следующий раз?

Я хожу в парк по воскресеньям.

Формально здесь нет акцентуации на времени. Но исходя из контекста, собеседник сообщил, что он зачастую посещал парк по воскресеньям в прошлом и собирается делать это в будущем. То есть, что в следующее воскресенье он, вероятнее всего, снова туда пойдет.

Но у всех этих вариантов есть один общий момент. Основной глагол никак не меняется. К нему можно добавить will или going to, обозначить время отдельным словом tomorrow или подать информацию через контекст. Но основной глагол будет в той же форме, что и в настоящем времени.

Еще одно интересное наблюдение: будущее в английском языке измеряется твердостью намерений.

I will do my homework tomorrow. Я сделаю домашнюю работу завтра.

Здесь нет абсолютной уверенности, а лишь предположение. Его можно передать следующим образом:

I suppose I will do my homework tomorrow. Я думаю, что сделаю домашнюю работу завтра.

Теперь дальше.

I may do my homework tomorrow. Вероятно, я смогу сделать домашнюю работу завтра.

Или не смогу. Это предположение, в котором не очень много уверенности. Это лишь одна из возможностей, которые могут случиться.

И последнее:

Im going to do my homework tomorrow. Я собираюсь сделать домашнюю работу завтра.

Здесь есть явное намерение и уверенность насчет завтрашних действий. Нюансы довольно тонкие, но они действительно много значат в грамматике языка.

В английском есть только две временные словоформы глаголов

Давайте вспомним формы английских глаголов. Всего их три.

  • Первая форма: глагол настоящего времени. Как инфинитив, но без частицы to. Work, dance, read, write, see и так далее.

  • Вторая форма: глагол прошлого времени. Их здесь разделяют на два типа: правильные и неправильные. Правильные формируются с помощью окончания -ed (worked, danced), а неправильные как попало, поэтому нужно учить таблицу (wrote, saw).

  • Третья форма: причастие прошедшего времени. Нужно для создания предложений в группе времен Perfect. Точно так же делятся на правильные с тем же окончанием -ed (worked, danced) и неправильные (written, seen).

Где же словоформа будущего времени? А ее нет.

Во французском языке, к примеру, у глаголов есть словоформы, которые прямо обозначают будущее время.

Je verrai la peinture Я увижу картину.

Je vois la peinture Я вижу картину.

Изображение глагола прямо показывает будущее время. Слово verrai может перевести только как увижу в будущем времени и никак иначе.

В английском есть только словоформа прошлого времени и причастия. Чтобы выразить будущее время, нужно использовать слово will. Но с ним тоже интересно.

Will модальный глагол с широкими полномочиями

Модальные глаголы в английском не используются самостоятельно и не означают какого-либо действия или состояния. Вместе с основным глаголом они формируют так называемое составное модальное сказуемое.

Список модальных глаголов:

Can, may, must, ought to, have to, be to, need, should, would, dare, use to.

Чаще всего используются первые три, но не суть. Штука в том, что глагол will тоже модальный.

Одно из самых простых доказательств этому слово will не склоняется с местоимениями третьего лица. Нельзя сказать He wills только He will.

Некоторые англоязычные лингвисты даже говорят о том, что Future Simple будет правильнее называть Present Tense Modal. Ведь формально мы не создаем отдельную словоформу, а используем составное модальное сказуемое.

В учебнике The Cambridge Grammar of the English Language есть любопытная таблица, которая отражает создание всех 16 комбинаций временных форм составных глаголов:

Will здесь выступает в качестве именно модального глагола. Вместо него можно подставить may или should смысл предложения немного изменится, но форма нет.

В форме существительного слово will переводится как воля, желание, намерение.

И предложение I will do my homework tomorrow можно расширить до I have a will to do my homework tomorrow (У меня есть намерение сделать домашнюю работу завтра).

Так почему в английском языке нет будущего времени?

Причина в развитии праиндоевропейского языка. Примерно в 2500 году до нашей эры праязык стал распадаться на отдельные языковые группы, германские языки банально не переняли словоформы будущего языка в свою грамматику.

К примеру, в романских, кельтских и славянских языках используются словоформы, которые формируют будущее время.

Bruidhinnidh mi Я скажу (гэльский)

Narrabo tibi Скажу тебе (латынь)

Je dirai Я скажу (французский)

Скажу на русском тоже словоформа будущего времени. А в украинском языке, к примеру, есть даже словоформы будущего несовершенного времени Я казатиму (Я буду говорить).

В германских языках такие словоформы оказались утрачены еще на заре развития языков. Поэтому формально можно считать, что во всех языках германской группы нет будущего времени, ведь для его обозначения используются модальные глаголы. Но отсутствие особых словоформ будущего времени для глаголов никак не уменьшает инструментарий для его грамматической передачи.

Надеемся, что понимание, как на самом деле устроено будущее время в английском языке, поможет вам лучше разобраться в грамматике языка. А если вам понравился такой формат подачи информации и вы хотите еще, записывайтесь на бесплатный пробный урок с преподавателем и учите английский задорно и с удовольствием.

Онлайн-школа EnglishDom.com вдохновляем выучить английский через технологии и человеческую заботу

Только для читателей Хабра первый урок с преподавателем в интерактивном цифровом учебнике бесплатно! А при покупке занятий получите до 3 уроков в подарок!

Получи целый месяц премиум-подписки на приложение ED Words в подарок. Введи промокод march2021 на этой странице или прямо в приложении ED Words. Промокод действителен до 01.05.2021.

Наши продукты:

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru