Обзор исследований и разработок

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

Цель

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

Пока я писал это, я узнал кое-что новое, и определение моего внутреннего процесса является конструктивным.

Темы

  1. Определение проблемы
  2. Исследования
    а. java.util.Date
    б. java.util.Calender
    c. Григорианский календарь
  3. Развитие
    а. Требования
    б. Технические характеристики
    в. TDD
  4. Вывод
  5. Код

Определение задачи

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

На этой странице находится индикатор последней активности пользователей. Я хотел бы, чтобы он отображал 1 из 4 цветов. Это зависит от того, когда пользователь в последний раз был замечен с какой-либо активностью.

Я хотел бы, чтобы государства были:

  1. сейчас в сети
  2. онлайн на этой неделе
  3. онлайн в этом месяце
  4. не в сети в течение последнего месяца

Это приложение для Android, поэтому языком программирования является Java.
Начнем.

Мой профильный класс

Начнем с понимания того, что мы можем делать с java.util.Date.

Спроси у часовщика

Исследовательская работа

java.util.Дата



В первом абзаце объясняется, что этот класс в значительной степени устарел. Это означает, что он устарел. Также поясняется, что для конверсий следует использовать Calendar, а синтаксический анализ должен выполняться классом DateFormat. С момента создания java.util.Date был проделан значительный объем работы. Мы всегда должны искать более новые и лучшие доступные варианты.

Начиная с JDK 1.1, класс Calendar следует использовать для преобразования между полями даты и времени, а класс DateFormat следует использовать для форматирования и анализа строк даты. Соответствующие методы в Date устарели.

Много говорят о UTC и дополнительных секундах. Мы знаем, что 1 день = 24 × 60 × 60 = 86400 секунд. Однако в UTC время от времени к суткам добавляются дополнительные секунды. Как это делается, определяется вашей версией JDK. И большинство фреймворков дат, включая Joda-Time, этого не учитывают.

Это становится глубже, поэтому я просто свяжу вас с отличным постом SO.
http://stackoverflow.com/a/30989049

Мы хотим сделать некоторые расчеты. Итак, как упоминалось ранее, нам нужен класс Calendar.

java.util.Календарь

https://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html

Класс Calendar — это абстрактный класс, который предоставляет методы для преобразования между определенным моментом времени и набором полей календаря, таких как YEAR, MONTH, DAY_OF_MONTH, HOUR.

Момент времени может быть представлен значением в миллисекундах, которое является смещением от эпохи, 1 января 1970 г., 00:00:00.000 по Гринвичу (по григорианскому календарю).

Метод getInstance календаря возвращает объект Calendar, поля календаря которого были инициализированы текущей датой и временем:

Мы знали, что этот класс хорош для вычисления времени, но теперь мы знаем, что вызов getInstance() возвращает нам текущую дату и время. Это кажется очевидным, но полезно знать наверняка.

Снисхождение!

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

Поля календаря можно изменить с помощью трех методов: set(), add() и roll().

установить(f, значение)

Довольно стандартно, вы вручную устанавливаете переменную, такую ​​​​как Calendar.MONTH, на значение, например 0 или Calendar.JANUARY.

Хотя поле календаря f изменяется немедленно, значение времени календаря в миллисекундах не пересчитывается до следующего вызова get(),

Это означает, что внутренние вычисления не выполняются до первого вызова get(). Просматривая методы, я заметил internalGet(). Мое первое предположение состоит в том, что это позволит мне предварительно вычислить его значения.

protected final int internalGet(int field)
Returns the value of the given calendar field. This method does not involve normalization or validation of the field value.
See Also:get(int)

Это немного расплывчато. Давайте посмотрим, как это сравнивается с get(), может быть, есть дополнительная информация.

public int get(int field)
Returns the value of the given calendar field. In lenient mode, all calendar fields are normalize.  In non-lenient mode, all calendar fields are validated and this method throws an exception if any calendar fields have out-of-range values.

Это подтверждает мои предположения о том, что internalGet получит значения до нормализации.

добавить(f, дельта)

add(f, delta) добавляет дельту к полю f

Это понятие переполнения. Это позволит вам сказать: «Мне нужна дата, если бы это была сегодняшняя дата плюс 131 день».

рулон (f, дельта)

roll(f, delta)добавляет дельту в поле f без изменения больших полей.

Это позволит вам свернуть дату, не корректируя более крупные компоненты. Пример, который они приводят, работает следующим образом:
Дата: 31 января 1999 года, и вы добавляете месяц. Это становится
31.02.1999, но так как в феврале нет 31 дня, у вас есть 3 дополнительных дня, не учтенных. Если добавить их, получится 03.03.1999.

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

БОНУС: он сохраняет исходную дату в экземпляре и восстанавливает ее до 31-го числа месяца при повторном увеличении.
Итак… 31/1 – 28/2 – 31/3.
Герои не всегда носят плащи

Добавить против рулона

Это немного сложно представить, вот отличная блок-схема

Резюме

  1. getInstance возвращает текущую дату и время инициализации объекта
  2. set() — Устанавливает вещи, например, месяцы или годы.
  3. add() — Добавляет данные, например, месяцы + дни.
  4. roll() — добавляет что-то умное (хорошо для пользовательского интерфейса), например, месяцы + дни, но не все глупости с дополнительными днями.
  5. снисходительность — Щелкает кнутом (исключение), если вы делаете что-то дикое.

Григорианский календарь

Поскольку мы знаем, что Calendar — это абстрактный класс. нам нужно что-то более конкретное. Que Григорианский календарь

Он открывается информацией о юлианско-григорианской гибридной функциональности. Юлианский и григорианский кажутся очень похожими, но по-разному обрабатывают високосный год. Григорианский язык был принят совсем недавно (последние 100 лет) в нескольких странах. Это означает, что для выполнения расчетов даты класс должен учитывать миграцию календаря. Если вы достигли более 100 лет в прошлом в России, календарь должен учитывать юлианскую дату, потому что она использовалась в то время.

...юлианский календарь в настоящее время отстает от григорианского на 13 дней.

GregorianCalendar — Функциональность

Значения, рассчитанные для поля WEEK_OF_YEAR, находятся в диапазоне от 1 до 53.

53 недели в году?
Ну, 365/7 больше, чем 52!
Итак, какое дополнительное время приходится на неделю 1 или 53? Ответ 53

Первая неделя календаря – это первая семидневка, начиная с воскресенья (первый день недели).

7-дневная неделя и воскресенье настраиваются! Во Франции первый день недели — понедельник.

Умный дизайн

Разработка

Требования

  1. сейчас в сети
  2. онлайн на этой неделе
  3. онлайн в этом месяце
  4. не в сети в течение последнего месяца

Характеристики

  1. Онлайн сейчас означает последний активный в течение последнего 1 часа
  2. Онлайн на этой неделе означает, что последняя активность была от 1 часа до 7 дней.
  3. Онлайн в этом месяце означает, что последняя активность была между 7 и 31 днями.
  4. Не в сети в течение последнего месяца больше 31 дня

TDD — разработка через тестирование

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

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

При написании этого метода я сначала попытался использовать Calendar.HOUR, но заметил, что Calendar.HOUR_OF_DAY при использовании HOUR вам также потребуется установить Календарь.AM_PM. В противном случае будет использоваться текущее время. Поскольку getInstance возвращает текущее время. HOUR_OF_DAYот 1–24

Следующий тест мы напишем для «На этой неделе» или «Недавно в сети».

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

onlineStatus.setOnlineThresholdHrs(1);
calendar = Calendar.getInstance();
calendar.set(
        Calendar.DAY_OF_YEAR,
        currentDay - recentOnlineThresholdDays
);

Date recentOnlineThreshold = calendar.getTime();
if (lastOnline.after(recentOnlineThreshold)) {
    state = States.Recent;
}

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

Конец истории

Вывод

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

Специальная благодарность

Я хотел бы поблагодарить вас за то, что вы нашли время, чтобы прочитать мою статью. И отдельное спасибо моим друзьям и семье за ​​корректуру. Мне очень понравилось создавать это, и если вы хотите поделиться этим или использовать что-либо здесь, у вас есть мое полное разрешение.

Возможно скоро сделаю еще один

Спасибо!

Окончательный источник

Код