Чому б не повернути дати як рядок із бази даних?


41

У типовому веб-додатку дати отримують із сильно набраного шару бази даних (наприклад, у c # як System.DateTime на відміну від System.String).

Коли дату потрібно виразити як рядок (наприклад, відображається на сторінці), перетворення з DateTime у рядок проводиться у рівні презентації.

Чому це? Чому погано перетворювати DateTime у рядок на рівні баз даних?

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


73
Дозвольте запитати вас у цьому: чи просто ви перетворили б кожен тип у рядок? Чим Дата відрізняється?
садок

7
Гарне питання! Будь ласка, дивіться гострі дискусії, що тривають, тут .
Джон Ву

8
Що ж, здається досить очевидним, що інший хлопець помиляється, а всі інші мають рацію. Тут насправді не питання
садовий сад

7
Іноді потрібно робити математику дат поза межами бази даних. Набагато складніше, якщо у вас є струни.
Ерік Кінг

14
Інша проблема - то , що вид рядки вам потрібно? Існує багато способів представити дату в якості рядка. Що робити, якщо у мене була база даних, яка повертала лише поточний час, представлений як # секунди з епохи, як рядок (наприклад, поточний час - "1474496980"). Це було б корисно? Чи хочете ви використовувати таку базу даних?
riwalk

Відповіді:


168

Дати, DateTimes і справді будь-який інший набраний об’єкт, як правило, слід залишити у відповідному форматі до того моменту, як вам потрібно їх перетворити в якийсь інший тип, особливо коли цей тип є читабельною для людини формою, особливо, коли це втрата / однобічний вид перетворення.

Чому? Оскільки передбачається, що тип надає вам багато зручних вбудованих функціональних можливостей, таких як правильне тестування рівності, додавання та віднімання, порівняння (більше, ніж менше), функціонал часового поясу та локалі (особливо важливо для будь-якого часу, пов'язаного з часом), і т. д. Якщо ви вирішили підтримати американців і формат "Місячний день [рік], рік", а також загальний британський стиль "День місяця року" або стандарт ISO "Рік-місяць-день"? Що б ви зробили, якби це був рядок і вам потрібно було внести цю зміну, проаналізувати її назад у Date? Фу, ні дякую - так багато зла та підступних помилок, яких краще уникати цілком.

Більш конкретно, ви згадали багатоярусну архітектуру, яка має презентаційний шар окремим від даних пізніше. Це насправді інша велика причина передавати дату як дату, а не рядок - адже для якого типу форматування рядків слід вводити дату? Англійська, китайська, з або без секунд / мілісекунд, повне ім’я місяця або цифри, чи хочете ви пізніше впорядкувати поле дати (для сортування рядка потрібен певний рядковий формат, якщо ви хочете, щоб він працював правильно) тощо? Це все питання презентації - як користувач повинен переглядати дані - і введення цієї логіки в іншому місці обмежує перевагу наявності багатоярусної архітектури в першу чергу. Базі даних не потрібно знати чи дбати про те, як ви хочете переглянути дату в майбутньому.

Нарешті, майже всі складні програми (для чого потрібні багаторівневі архітектури), які дбають про час, неминуче використовуватимуть часи / дати у багатьох, безлічі різних способів і часто на всіх різних рівнях архітектури. Введені об'єкти, пов’язані з часом та датами, існують із справжньою причиною: сам час, і особливо календарі людських систем, дивні та важкі. Зрештою, часи і дати - це не рядки з тієї ж причини, що цілі числа і плаваючі точки не є рядками, і це ускладнить ваше життя лише якщо ви спробуєте зробити вигляд, що вони справді просто масиви символів, тому що вони просто не є.


26
+1 лише для використання слова зухвало. Я погоджуюся з вашими переконливими аргументами та вичерпним поясненням, але саме тому мені довелося увійти та проголосувати за вас.
Адріан Ларсон

1
Представлення часу дати як секунди, оскільки визначений час у минулому, також є надійним серед різних календарів. Наприклад, ісламські та китайські календарі не використовують жоден з грегогрійських місяців, номер року тощо. Я вважаю б обробку цього на рівні бази даних поганою практикою.
rexkogitans

Дати часто подаються як "X днів тому". Удачі розбираємо це до початкового значення.
Agent_L

5
Не будемо також забувати про зміни DST (та інші подібні) проблеми. Чи стане "6 листопада 2016 р. 1:30:26 ранку" вперше чи вдруге, коли ця дата і час відбудуться? UTC DateTime принаймні унікальний, і ви завжди можете перевести його в місцеве представництво на той час - повернутися назад не завжди можливо.
J ...

3
Why? Because it is assumed that the type provides you with lots of handy built in functionalityНа мою думку, це просто другорядне. Справжня причина полягає в тому, що тип говорить вам про те, що є . Дата - це не рядок, вона просто перекладається на легкий для читання людиною рядок.
Довал

53

Він говорить про використання веб-сервера для перетворення часу даних у рядок. Я говорю це зробити на сервері баз даних, а не на веб-сервері. Чому ти вважаєш, що це краще? - керівник МТ

Я хочу знати тип.

Мені дуже не байдуже, чи зберігає Ваша база даних інформацію в рядку, декількох ints чи байтах, тому що, зрештою, це завжди байти. Ця струна, що займає більше місця, ніж потрібно у вашій базі даних, мене не турбує. Що мене турбує, натрапляють на такі дати:

10.11.2016

І не знаючи, чи це одинадцятий місяць чи десятий місяць.

Але ви це підтвердили, скажете ви. Впевнені, що ви поставили це через процеси перевірки. Дата абсолютно правильна. Але ось я підтримую цю річ, і все, що я знаю, це дата - це рядок. Я навіть не можу сказати тобі, яка це дата.

"Десятого листопада в дві тисячі шістнадцятого року нашого пана".

Це рядок. Одна з наших презентацій потребує цього в такому форматі. Ви сказали, що база даних перетворює всі дати в рядки? Порадуйте цим.

Завдання бази даних - зберігати дані, які не мають даних. Звичайно, ви можете зробити це в рядках, але тоді вам доведеться їх розібрати, щоб було корисно представити інші формати. Зберігання його у стандартній синтаксичній формі для будь-якого типу, що пропонує БД, дає нам настільки ж близьку готовність представити, як ми можемо, не прийнявши рішення про презентацію. Мені насправді не важливо, чи БД підтримує цей тип рядком або ints чи байтами. Поки він знає, що робить.

Але якщо ви не повідомляєте БД про те, що ми маємо справу з датою і зберігаємо дату як рядок, ви передчасно представляєте та надаєте перевагу одній презентації над усіма іншими. Це змушує всіх інших присутніх проаналізувати перед перетворенням. Ні, база даних не є частиною презентаційного шару. Не вимагайте, щоб це було.

Так само презентаційний шар не є частиною бази даних, тому не розумно з'єднувати звіт до деталей бази даних. Діяти на типи набагато надійніше.


Ця відповідь розглядає сховище як рядки. Однак він не стосується загальної схеми зберігання дат у власному типі дати, а потім відформатує їх до рядка в SQL-запиті , використовуючи такі функції, як CONVERT (T-SQL), або що СУБД зазвичай серіалізує свої дати в рядок у настроюваному форматі незалежно від запиту. Наприклад: postgresql.org/docs/9.5/static/…
dcorking

Це звіт. Це відбувається після зберігання. Як перетворення моєї дати народження у мій вік.
candied_orange

2
Я просто хотів закликати вас поширити свою відповідь, оскільки темою ОП є те, як "дати витягуються з шару бази даних". Існує чітко встановлений, хоча і може бути застарілий, шаблон, коли звіт запитує в базу даних для форматованих та локалізованих рядків дати. Я думаю, що ОП хотіла б почути ці аргументи щодо знецінення. Я знаю, що хотів би.
22:00

@dcorking оновлення примітки.
candied_orange

+1 додавання більше води до млина: просто створіть систему на встановленій базі, яка охоплює декілька часових поясів, де абсолютний момент є найважливішим, і подивіться, наскільки добре ви робите з рядковими <-> перетвореннями часових позначок скрізь. Найгірше, зробіть точку розширення для людей, щоб створити свої власні плагіни та надайте їм часові позначки, як рядки, бачите, наскільки стійкими будуть ці часові позначки!
ньютопський

19

Місцевий пошук

Перетворення дати в рядок для цілей презентації вимагає знання налаштувань користувача, оскільки точно та сама дата, як правило, повинна відображатися по-різному для користувачів у різних локалях. Навіть якщо ви використовуєте один додаток у своїй програмі, належна поведінка повинна використовувати локальний додаток замість сервера баз даних; і вони не гарантують однаковість, навіть якщо в цей момент вони збігаються.

Перетворення з універсального типу даних дати в рядок, характерний для локальної локалізації, має відбуватися в презентаційному шарі, оскільки саме той рівень знає, як це перетворення має бути виконано.


3
Для прикладу реального життя невідповідності локальних даних уявіть, як написати додаток для користувачів штату Мейн, США, а потім він розміщений у серверній фермі західного узбережжя Amazon. ;) Насправді це не так малоймовірно.
jpmc26

@ jpmc26 Я не розумію різниці - чи використовує Мен інший формат дати для решти США?
Піт Кіркхем

2
@PeteKirkham Мен та західне узбережжя США використовують часові пояси, розташовані на відстані 3 годин.
jpmc26

1
Або інший сценарій реального життя: уявіть, що у Швейцарії працює сервер, який повинен обслуговувати клієнтів чотирма (німецькою, французькою, італійською, англійською) різними мовами з різною локальністю (і дещо різними правилами форматування). Удачі вибираєте правильний локальний сервер для такої ситуації.
Voo

1
@ jpmc26 часові зони та локалі - це не одне і те ж. Наприклад, у нас є офіси в Шотландії Глазго, Атланта США та Пуне Індії. Консультанти в цих офісах по черзі цілодобово відстежують сайти (кампуси, лікарні, готелі тощо) по всьому світу. База даних додатків працює в UTC, але відображає час за місцевим часом для моніторингу сайту. Консультанти з США визначили дати, локалізовані на MM / DD / YYYY, але місцевості Великобританії та Індії - DD / MM / YYYY - це залежить від місцевості, а не часового поясу сайту чи користувача.
Піт Кіркхем,

9

Це небажано з тієї ж причини, що ви не хочете просто сліпо конвертувати будь-який тип у рядок, як тільки він потрапляє на рівень програми. Існує велика ймовірність, що ви захочете якось використовувати цей об’єкт, перш ніж представляти його користувачеві (якщо ви навіть подаєте його користувачеві). Для цього конкретного прикладу, уявіть, що вам потрібно було зробити деяку математику дати на об'єкті. Немає жодного недоліку просто перетворити об’єкт у рядок точно перед його відображенням.


4

Типи існують з причини, якби вони не додавали користі, ми б їх не мали і не використовували б, і ми просто мали б "тип" і все було б так. Вони не тільки зручні, вони також додають безпеку та ефективність. Далі наведено перелік того, чому завжди слід зберігати типи у їх рідному форматі, а не як рядки . Я використовував DateTimeяк приклад більшу частину часу, але ті самі принципи застосовуються для будь-якого примітивного типу, як цілі числа, десяткові числа, двійкові тощо.


Зберігання даних

Обмеження

Тип обмеження

Практично всі сховища даних дозволяють задавати обмеження на дані, сюди входять обмеження типу. Однією з головних переваг визначення DateTimeпримірника є те, що збережені дані будуть обмежені цим типом. Ніколи не вдасться ввести щось, окрім часу дати, незалежно від того, як дані були вставлені в магазин. Останнє важливо для великих систем, де є кілька процесів, які взаємодіють безпосередньо з магазином. Сюди також входить спроба додати несправні дати, такі як 30 лютого (будь-якого року), оскільки лютий може мати лише 29 днів у високосний рік та 28 днів у не високосні роки.

Обмеження на перевірку

Існують також обмеження перевірки, які можуть бути реалізовані в сховищі даних, наприклад, гарантуючи, що вставлена ​​дата не перевищує поточну дату або що дата початку настає перед кінцевою датою.

Операції

Більшість сховищ даних також мають вбудовані операції / функції, такі як DateAddабо DatePartв MS Sql Server. Це дозволяє почати фільтрувати або вибирати конкретні дані, поки дані все ще зберігаються (ще не отримано в програмі).

Універсально прийнятий формат

Використовуючи нативний тип, іншим розробникам або системам, які також взаємодіють із магазином, не потрібно повідомляти про детальні відомості про збереження цього примітивного типу. Це не так, якщо цей тип зберігався як рядок, то ви повинні переконатися, що всі розуміють формат цього DateTimeрядкового подання. Ця система стає крихкою при роботі з даними, що охоплюють локалі, регіони та культури за походженням даних, фізичним розташуванням програми та атрибутами кінцевого користувача / системи, яка взаємодіє з цими даними. Приклад: формат дати в одній країні може бути MM / dd / yyyy (як в США), але в іншій він може бути dd / MM / yyyy, виявити, що різниця стає майже неможливою.

Швидкість

Швидкість пошуку, швидкість перевірки, швидкість операцій та ефективність зберігання - теж важливі фактори. Приклад швидкості пошуку: сховища даних дозволяють індексувати стовпці, і ці індекси, як правило, можуть бути більш ефективно використані, якщо тип зберігається у рідному форматі.

Застосування

Доступ до даних

Виконання запитів у магазині стає простішим за допомогою системи рідного типу як розробників, знову ж таки, не потрібно здогадуватися про формат зберігання. Практично всі постачальники програм зберігання даних ( наприклад: ado.net ) надають механізми для створення відповідних параметризованих запитів на основі власних типів, переданих тут. Ось приклад додавання частини дати до запиту ado.net до магазину Sql Server, те ж саме з рядками було б дуже громіздким і схильним до помилок.

command.Parameters.Add(new SqlParameter("@startDate", SqlDbType.Date) {Value = myDateInstance.Date});

Операції

Нативні типи в коді також передбачають стандартні операції, такі як тип .net System.Date. Операції, як правило, мають математичний характер, як додавання дат, знаходження різниці між датами тощо. Знову ж, це неможливо зробити легко для типів рядків.

Презентаційний шар

Місцевий пошук

Коли примітивний тип остаточно перетворюється на рядок у шарі презентації ( правильне розташування в програмному стеку, щоб це зробити ), програміст тепер має різні варіанти його правильного відображення відповідно до контексту, в якому він представлений. Цей контекст, як правило, складається з фактичного значення даних та місцевості користувача.

Приклад 1

Екземпляр дати може бути автоматично відформатований на основі мови користувача.

DateTime.Now.ToString("D", CultureInfo.GetCultureInfo(userContext.Culture))
Приклад 2

Десятковий екземпляр може представляти суму (валюту), а локальна частина користувача також повинна відображати суму відповідно до їх переваг. Потім програма c # може відображати значення, використовуючи

amount.ToString("C", CultureInfo.GetCultureInfo(userContext.Culture))

Це може бути критично важливим, оскільки різні культури по-різному відображають цифри. У американський період (.) І кома (,) мають точно зворотне значення, як у Нідерландах.

Місцезнаходження

Це дуже специфічно для DateTimeвипадків. Дата та час являють собою події в конкретний момент часу, але зазвичай це потрібно передати / представити користувачеві залежно від їх власного часового поясу. Приклад: DateTimeекземпляр 2016-09-21T23:38:21.399Zможе бути відображений як 9/21/2016 5:21 PMдля користувача у східному часовому поясі в США. Існує багато способів цього досягти, але це стає майже неможливим, якщо екземпляр часу дати зберігається в пам'яті як тип рядка або в сховищі даних як тип рядка.


Загальне правило

Загальні 2 правила для програми дотримуються, коли мова йде про перетворення будь-якого примітивного типу в рядкове представлення, такі

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

0

З цим насправді немає нічого поганого (це робиться постійно в сервісах), поки ви використовуєте неоднозначний формат для своєї дати. Однозначно, я маю на увазі не тільки чітку дату (наприклад, MM / DD проти DD / MM), але і часовий пояс, в якому вона знаходиться. Отже, наперед, якщо ви збираєтесь представляти свої дати як текст, використовуйте формат ISO . Я дуже віддаю перевагу тимчасовим рядкам на основі UTC.

Плюси:

  • Дата / час на основі стандартів Струни портативні та прості для розуміння
  • Часто дати в БД містять часовий компонент. Якщо це не має сенсу для ваших даних, це може насправді спростити справи.

Мінуси:

  • Розмір даних. Внутрішній формат дати в БД, як правило, використовує набагато менше місця, ніж рядкове відображення цієї дати.
  • Ви, як правило, захочете ввести його в реальну структуру дати або часу на клієнті, щоб у розборі може бути додатковий час.

Якщо хтось сказав, що вони хочуть це зробити, я б запитав "чому?" тому що насправді в цьому немає великого значення. Якщо причиною того, що хтось хоче повернути дату як String, є те, що вони просто відображатимуть її безпосередньо, це не є вагомою причиною для використання Strings з БД.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.