날짜와 시간을 다루는 것이 쉽다고 생각할 수도 있습니다. 1분은 60초, 한 시간은 60분, 하루는 24시간, 일주일은 7일, 한 달은 28~31일 등이 있습니다.
물론 여기에는 로켓 과학이 필요하지 않습니다…
글쎄, 사실과 더 다를 수는 없습니다!
애플리케이션 및 백엔드 개발 과정에서 발생할 수 있는 날짜 및 시간과 관련된 함정과 함정을 보여드리겠습니다.
가장 작은 것부터 가장 큰 것까지 측정 단위부터 시작하겠습니다.
매일 사용하는 가장 작은 단위는 1초입니다. 유닉스 타임의 기반이기도 합니다.
그러나 Java와 같은 일부 프로그래밍 언어에서 가장 일반적인 단위는 System.currentTimeMillis() 메서드와 같이 밀리초(1/1000초)입니다.
그러한 차이로 인해 많은 오류가 발생할 수 있습니다.
시스템 외부에서 숫자 값을 받으면 설명서를 읽은 후에도 어떤 측정 단위를 사용하는지 한눈에 명확하지 않을 수 있습니다!
SMS 및 MMS 콘텐츠 제공업체(데이터베이스) 열의 DATE 필드를 확인하세요. 두 문서 모두 다음과 같이만 말합니다.
메시지를 받은 날짜
유형: INTEGER(긴)
그러나 SMS는 밀리초를 사용하는 반면 MMS는 초를 사용합니다. 놀란? 특히 이러한 API를 다른 사람이 설계한 경우에는 이런 일이 발생할 수 있습니다.
이런 경우 어떻게 처리할 수 있나요? 어떻게 피할 수 있나요?
다행히도 다음과 같은 몇 가지 일반 규칙을 공식화할 수 있습니다.
수신되는 데이터의 형식을 항상 확인하세요.
문서가 부정확하거나 오래되었을 수 있으므로 현장에서 확인하세요. 일반적으로 개발하는 동안 오류를 찾아보면 5만 년이나 훨씬 먼 미래의 날짜와 같은 오류를 발견하는 것은 매우 쉽습니다. 이 결과는 예를 들어 밀리초를 초로 처리할 때 발생합니다.
값을 보내는 측에 영향력이 있는 경우(예: 시스템을 설계 중인데 아직 아무도 사용하지 않는 경우) 표준화된 텍스트 형식을 고려하세요.
여기서는 일부 사용자 정의 형식이 아니라 표준화(예: ISO 8601)를 강조합니다(나중에 이 주제를 확대할 예정입니다). 몇 년이 지나면 원래 팀의 누구도 더 이상 해당 프로젝트에 참여하지 않게 되며, 새로운 개발자가 문서를 볼 필요가 없기 때문에 텍스트 형식이 이해하기 쉽습니다.
그러나 성능이 중요한 기기에서는 숫자 형식이 더 나을 수 있습니다.
**날짜/시간/기간 값을 처리하려면 원시 정수 대신 전용 클래스를 사용하세요.
**Java 세계에는 Instant 또는 Duration과 같은 유용한 클래스가 많이 포함된 java.time 패키지가 있습니다. 예를 들어 정수가 있는 경우. eventDuration, 초 단위인지 밀리초 단위인지, 아니면 며칠 단위인지 알 수 없습니다.
레거시 코드와 같이 완전히 리팩토링할 수 없는 원시 값(정수, long 등)을 처리해야 하는 경우 변수/필드 이름에 측정 단위를 포함하세요 .
예를 들어, eventDurationSeconds는 명확합니다.
아마도 윤년에 대해 들어보셨을 것입니다. 하루가 더 길다는 점에서 "보통"과 다릅니다. 윤초도 있어요!
비윤년초보다 긴가요?
상황에 따라 다릅니다!
먼저 약간의 이론부터 시작해 보겠습니다. 지구 속도가 느려지고 있습니다. 실제로 그것은 철학적인 진술이 아니라 과학적으로 입증된 사실입니다. 델타-T라고 합니다.
그럼 이것이 실제로 우리에게 어떤 의미가 있을까요? 음, 시간 측정 단위에는 표준화된 길이가 있습니다. 기본 단위는 초이며 다음과 같이 정의됩니다.
세슘-133 원자의 교란되지 않은 기본 바닥 상태의 두 초미세 준위 사이의 전이에 해당하는 방사선의 9 192 631 770 주기의 지속 시간입니다. (출처: bipm.org)
해당 기간은 일정하며 초에서 파생된 다른 모든 단위를 알려줍니다. 1분은 60초, 1시간은 60분(3,600초) 등으로 구성됩니다.
그러나 지구의 속도가 느려지고 낮이 길어지면 단위로 측정된 시간이 현실과 일치하도록 어떻게든 속도 저하를 수용해야 합니다. 이는 추가 초(윤초)를 삽입하여 수행됩니다.
연중 윤초를 사용할 수 있는 시간은 6월 말과 12월 2번뿐입니다. 맨 끝은 UTC 23:59:59 직후의 마지막 날(각각 30일 또는 31일)을 의미합니다(따라서 현지 시간은 다를 수 있음).
일반적으로 마지막 초가 지나면 다음 날로 이동하기 전에 추가 윤초가 삽입됩니다. 따라서 23:59:60(1분에 61초 — 0부터 계산)을 가질 수 있습니다.
감속이 일정하지 않기 때문에 윤초가 불규칙하게 삽입됩니다. 가장 최근의 사건(2021년 4월 이 글을 쓰는 시점)은 2016년 12월에 발생했는데, 이는 4년여 전의 일이다. 그러나 두 번째는 2015년 6월로, 둘 사이의 차이는 1.5년에 불과했습니다.
실제 벽시계나 일부 프레임워크 API와 같이 시간을 설정할 수 있는 일부 장소에서는 윤초를 사용하여 시간을 관찰하거나 설정하는 기능이 없을 수도 있습니다.
예를 들어 구식 Date Java 클래스는 이중 윤초도 지원합니다. 범위는 0에서 61까지이므로 62초가 가능합니다!
그러나 java.time 패키지의 최신 Instant 클래스는 프로그래머에게 윤초를 노출하지 않습니다. 윤초는 하루 중 마지막 1,000초 동안 동일하게 늘어납니다(이 초는 더 깁니다).
이론적으로 윤초는 음수일 수도 있습니다. 하지만 지금까지는 그런 일이 일어나지 않았습니다.
1분이 59초로 구성되어 큰 문제가 발생할 수 있다는 의미입니다. 예를 들어, 어떤 작업이 23:59:59.001에 발생하도록 예약되어 있는데 원하는 시간이 존재하지 않는 것으로 판명되면...
다행히 눈에 보이는 초를 퍼뜨리는 것과 유사하게 프로그래머에게 완전히 투명하게 축소될 수도 있습니다.
61초가 존재할 수 있다는 것은 알고 있지만 62초는 어떨까요? 문서에는 다음과 같이 나와 있습니다.
동일한 분에 2개의 윤초가 발생할 가능성은 극히 낮지만 이 사양은 ISO C의 날짜 및 시간 규칙을 따릅니다.
실제로 C 사양에는 [0, 61] 범위가 있습니다. 그런데 왜? 같은 질문이 1992년 tzdata(시간대 데이터베이스)의 저자인 Paul Eggert를 괴롭혔습니다. 아카이브에서 읽을 수 있듯이:
새로운 구현은 이러한 표준과 역호환되어야 하기 때문에 수십 년 전에 발생한 약간의 해석 오류가 지금도 눈에 띕니다.앱 및 백엔드 개발의 다른 극단적인 사례를 살펴보겠습니다.
일
하루란 무엇인가요? 상황에 따라 다릅니다! 00:00:00부터 23:59:60까지 24시간 동안 지속된다고 할 수 있습니다(윤초도 포함?). 글쎄, 후자가 일반적으로 사실이든, 하루는 항상 24시간 동안 지속되지 않습니다. 23일, 25일 또는 심지어 21일과 27일이 될 수도 있기 때문입니다! 왜 그런 가치가 있습니까? 정답은…
일광 절약 시간제
DST의 문제점은 무엇인가요? 어디 보자!
벽시계의 수동 조정을 잊어버린 경우나 대중교통 지연과 같은 내용은 다루지 않고 대신 백엔드 및 앱 개발과 관련된 측면에 중점을 두겠습니다.
여름철에는 시계를 1시간 앞당긴다고 생각할 수도 있습니다. 항상 그런 것은 아닙니다! 세상에는 3시간(케이시처럼)이나 30분(로드하우섬처럼) 차이가 나는 곳이 있습니다.
여름철은 이름에서 알 수 있듯이 정확하게 봄이나 가을의 일부를 덮을 수도 있지만 일반적으로 따뜻한 계절과 관련이 있습니다. 이는 결국 반구에 따라 다릅니다.
유럽에는 여름이 있고, 호주에는 겨울이 있으며, 그 반대도 마찬가지입니다. 따라서 이를 관점에서 보면 호주의 서머타임은 유럽의 겨울 동안 발생합니다!
게다가 여름의 정의는 관할권에 따라 다릅니다. 모든 유럽 연합 국가에서는 시간이 동시에 변경됩니다. 예를 들어 베를린과 런던의 시차는 여름이든 겨울이든 항상 1시간입니다.
그런데 시드니와 런던의 시차를 생각해 보겠습니다. 이런 경우는 날짜에 따라 다릅니다! 그 이유는 시드니가 런던과 다른 날짜에 DST 관찰을 시작하고 중지하기 때문입니다.
영감: timeanddate.com
예를 들어 1월부터 10시간의 시차가 있습니다. 시드니는 당시 DST를 관찰하지만 런던은 그렇지 않습니다. 3월 말에 런던은 DST를 관찰하기 시작하지만 시드니의 주는 변경되지 않으므로 차이가 9시간으로 줄어듭니다. 그러다가 4월이 되면 시드니는 DST 관찰을 중단하므로 8시간이 남습니다. 3가지 다른 오프셋이 있습니다. 그래서 시드니와 런던의 시차에 대한 질문에는 3가지 답변이 있습니다!
미국, EU 회원국, 호주와 같은 국가는 DST와 관련하여 안정적인 관할권을 갖고 있습니다. 전환이 발생하는 시점은 미리 잘 알려져 있습니다. 그러나 일부 국가에서는 예기치 않게 법률이 변경될 수 있으므로 항상 그런 것은 아닙니다. 예를 들어 피지에서는 2020년에 규칙이 변경되어 불과 몇 달 전에 발표가 도착했습니다.
라마단을 공식적으로 준수하는 일부 이슬람 국가에서는 훨씬 더 복잡한 상황이 발생합니다. DST도 준수하는 경우 후자는… 중단될 수 있습니다(라마단 기간 동안).
즉, DST 전환이 두 번 이상(앞뒤로) 발생할 수 있으며 심지어 1년에 몇 번이라도 발생할 수 있음을 의미합니다. 또한 라마단은 이슬람력(음력)에 따라 계산됩니다. 예측은 라마단 경계 날짜를 계산하는 데 사용되며 항상 정확하지는 않습니다. 예를 들어 2020년 팔레스타인에서는 예측이 일주일 정도 짧은 것으로 드러났다. 불과 며칠 전에 변경사항이 적용되었습니다.
대부분의 시스템은 DST 전환 순간의 소스로 IANA 시간대 데이터베이스를 사용합니다. 해당 데이터베이스에는 일반적으로 매년 몇 가지 업데이트가 있습니다.
DST를 처리하면 알고리즘에 영향을 미칠 수 있습니다. 몇 가지 일반적인 시나리오를 고려해 보겠습니다.
특정 벽 시간(예: 02:30)으로 예약된 알람이 있는 경우 해당 순간이 존재하지 않는 것으로 판명될 수 있습니다(DST 전환 중에 시간이 02:00에서 03:00로 변경되는 경우). 시간을 거꾸로 바꾸면 두 번 존재합니다. 예를 들어, 백업이 이루어지지 않거나 약이 제공되지 않거나 두 번 제공된다면 결과는 끔찍할 수 있습니다.
또 다른 일반적인 효과는 사용자가 DST를 준수하는 시간대에 있는 경우 주기적으로 시간 변경을 트리거하는 것으로 구성됩니다. 예를 들어, bitrise.io에서 빌드가 10:00에 실행되도록 예약하면 갑자기 11:00에 실행이 시작될 수 있습니다.
이는 내부 로직이 DST를 인식하지 못하고 사용자에게 표시되는 시간은 UI를 렌더링할 때만 계산되기 때문에 발생합니다. 절대적인 순간은 항상 동일하지만 사용자에게 표시되는 시간은 DST에 따라 달라집니다. 일반적으로 고객이 기대하는 것은 아닙니다.
주일의 첫날은 언제인가요? 유럽에 산다면 아마도 Monday라고 말할 것입니다. 미국에서는 일요일이고 아랍 국가에서는 토요일입니다. 이러한 사실은 알고리즘 구성 및/또는 달력 렌더링에 영향을 미칠 수 있습니다.
연도의 몇 번째 주는 어떻게 되나요? 모든 것이 1월 1일부터 시작된다고 생각하시면 됩니다.
짐작하셨겠지만, 항상 그런 것은 아닙니다!
ISO 표준에 따르면 첫 번째 주는 목요일을 포함해야 합니다. 예를 들어 2021년에는 1월 1일이 금요일이므로 전년도 마지막 주에 속합니다. 2021년의 첫 주가 1월 4일부터 시작되었습니다! 그러나 모든 로캘이 ISO 표준과 동일한 규칙을 사용하는 것은 아닙니다.
세계 대부분의 지역에서 사용하는 그레고리력은 1월부터 12월까지 12개월입니다. 그러나 다른 종류의 달력에는 더 많은 월이 있을 수 있습니다. 예를 들어 히브리 달력에는 13개월이 있을 수 있습니다. 13번째(IT계)는 Undecimber라고 합니다.
보통 1년은 365일입니다. 그러나 4로 나누어지는 각 해(예: 2020, 2024 등)는 366일(2월의 일반적인 28일이 아닌 29일)을 갖는 윤년입니다. 그러나 100으로 나누어지는 해(2100, 2200 등)는 윤년이 아닙니다. 하지만 ? 400으로 나누어 떨어지면(2000, 2400 등) 윤년입니다.
다행히도 이러한 구분을 직접 구현하려고 할 필요는 없습니다(하지 않아도 됩니다!). 해당 프로그래밍 언어의 잘 알려진 날짜 클래스/함수 라이브러리를 사용해야 합니다.
잘 알려진 서비스에는 잘못된 윤년 계산과 관련된 엄청난 버그가 많이 있었습니다. 윤년 문제라는 용어도 있습니다.
현지 시간은 경도에 따라 다릅니다. 특정 지역에서는 정오이지만, 지구 반대편에서는 자정이기도 합니다.
여행 중에 시계를 계속해서 조정하는 것은 불가능하므로 지구본은 동일한 현지 공식 시간을 갖는 지역을 포함하는 구역으로 나누어졌습니다.
소규모 국가나 가장 큰 국가(예: 호주, 미국, 러시아)의 일부 지리적 지역의 경우 영역 경계는 일반적으로 국가 경계와 동일합니다.
출처: timeanddate.com
일부 지역에서는 정치적, 경제적 이유로 공식 시간이 '적법한' 시간과 크게 다릅니다(경도/태양시만 고려). 예를 들어 스페인의 경우 경도에 따라 더 가까운 영국이 아닌 대부분의 중부 유럽 대륙과 동일한 지역입니다.
대부분의 시간대에는 정수 오프셋(UTC와의 차이 - 표준 시간)이 있습니다. 베를린에서는 +1시간(DST에서는 +2) 또는 부에노스아이레스(아르헨티나, DST 없음)에서는 -3시간입니다. 그러나 오프셋에는 절반의 시간이 포함될 수 있습니다. 뭄바이(인도)에서는 +5:30h(DST 없음)가 적용됩니다.
그것이 충분하지 않다면 카트만두(네팔)에서는 +5:45시간이 있으므로 쿼터도 가능합니다!
다행히도 이 글을 쓰는 시점에는 더 이상 세분화된 오프셋이 없습니다. 하지만 네덜란드의 +0:20 등 과거에도 존재했었죠.
우리는 24시간을 근무하고 있기 때문에 상쇄가 가능한 범위라고 생각할 수도 있습니다. +12:00과 -12:00을 합치면 24가 됩니다.
그러나 시간대 간 가장 먼 시거리는 26시간!
+14:00 오프셋이 있기 때문에 가능합니다. 오세아니아의 여러 국가에서는 호주와 오히려 연결되어 있기 때문에 채택되었으므로 20시간 이상 차이가 나는 것보다 몇 시간 차이가 나는 것이 더 실용적이며, 이는 대부분의 경우 다른 날짜로 이어집니다.
항공편 연결 찾기를 생각해 보겠습니다. 왕복 항공편의 경우 출발 및 귀국 날짜/시간을 모두 선택할 수 있습니다. 당연히 귀국시간은 출발시간 이후가 되어야 할 것 같습니다.
물론 이는 사실과 다를 수 없습니다!
해당 시간은 현지 시간대임을 명심하세요.
다음 예를 살펴보세요.
도쿄 00:30(오프셋 +09:00)에서 샌프란시스코(오프셋 -07:00)로 출발
현지시간 전날 18:00 샌프란시스코 도착!
샌프란시스코에서 19:50에 돌아옵니다(최초 출발에 비해 여전히 이전)
그래서 어디든 갔다가 어제 돌아오셔도 됩니다. 다음 예를 참조하세요.
앱/백엔드 개발에서 직면할 수 있는 또 다른 잠재적인 상황은 시간대 이름 지정과 관련이 있습니다.
예를 들어 오프셋으로 시간대를 호출할 수 있다는 것을 알 수 있습니다. +01:00 또는 식별자로 예: 유럽/베를린. 후자의 표기법은 여러 가지 이점을 제공합니다. DST 전환에 대한 정보를 전달하고(베를린은 여름에 오프셋 +02:00을 가짐) 과거 데이터도 보유합니다.
예를 들어 유럽/바르샤바 지역과 유럽/베를린 지역은 요즘에는 모두 동일한 것 같습니다. 둘 다 일년 내내 동일한 오프셋을 가지며 DST 전환도 항상 같은 순간에 발생합니다.
그러나 과거에는 항상 그랬던 것은 아닙니다! 예를 들어, 1977년 베를린에는 DST가 전혀 없었지만 바르샤바에서는 4월 3일부터 9월 25일까지 DST를 관측했습니다(현재와는 완전히 다릅니다).
시간대 식별자는 도시 이름을 기반으로 만들어졌습니다. 이는 국가 경계가 도시 이름보다 훨씬 더 자주 변경될 수 있다는 사실 때문에 의도적인 것입니다.
예를 들어 크리미아는 실제로 우크라이나에서 러시아로 변경되었지만 도시 이름은 변경되지 않았습니다. 유럽/심페로폴 지역은 현재 데이터나 과거 데이터가 필요하더라도 사용할 수 있습니다.
어떤 것이 하루만 지속된다고 가정해 보겠습니다. 따라서 오전 9시 15분에 시작하면 24시간을 추가하여 종료 시간을 얻을 수 있습니다(이 경우 다음 날 오전 9시 15분만).
글쎄, 항상 쉬운 일은 아닙니다! 시계가 인위적으로 앞이나 뒤로 조정될 때 DST 전환이 있을 수 있다는 점을 명심하세요. 이는 하루가 일반적으로 24시간 동안 지속되지만 때로는 23시간, 25시간 또는 27시간 또는 23시간 30분과 같은 더 이상한 값일 수도 있음을 의미합니다(케이시와 로드 하우를 기억하시나요?).
비즈니스 로직을 구현할 때 하루를 맹목적으로 24시간으로 취급하지 않는 것이 중요합니다. 이를 위해서는 적절한 날짜/시간 API를 사용해야 합니다. 예를 들어 Java에는 다음과 같은 고유한 클래스가 있습니다.
일수를 나타내는 기간 — 일, 월, 연 단위로 측정되는 구독 유효 기간 등에 적합합니다.
지속 시간을 나타내는 기간(예: 달력의 날짜와 상관없이 24시간입니다. 여행, 기기/프로그램 시작 이후 경과 시간 등 다양한 활동을 측정하는 데 적합합니다.
일부 언어에는 햇볕이 잘 드는 상태(영어 - day)와 연속 24시간(영어 - nychthemeron)을 나타내는 별도의 단어가 있지만 일반적으로 사용되지는 않습니다.
날짜/시간 표현:
출처 : xkcd.com
REST API 등을 통해 다른 시스템과 통신할 때 데이터 형식에 동의하는 것이 중요합니다. 날짜가 2021년 10월 12일로 표시되는 경우 미국 형식(10월 12일) 또는 영국 형식(12월 10일)일 수 있습니다.
모호하지 않은 표현을 사용하는 것이 좋습니다!
날짜와 시간은 ISO 8601 표준을 따릅니다. 절대 날짜/시간뿐만 아니라 기간도 표준화되어 있습니다. 또한 로컬(알람 시계 작동 시간 또는 생년월일과 같은 데이터를 나타내는 경우) 또는 구역(이벤트 시작 또는 사진 촬영과 같은 순간을 나타내는 경우) 등 적절한 유형을 사용하는 것도 중요합니다.
맞춤형, 비표준 형식은 일반적으로 혼란과 버그를 초래하므로 사용하지 마세요.
UI에 날짜와 시간을 표시할 때 사용자의 로케일을 고려해야 합니다. 표시되는 형식은 언어 및 지역에 따라 다릅니다. 모두 동일한 날짜를 나타내는 다음 예를 살펴보세요.
21년 4월 18일 — 미국 영어
2021년 4월 18일 — 영국 영어
2021년 4월 18일 — 루마니아어
١٨/٤/٢٠٢١ — 사우디아라비아, 아랍어 및 동부 아라비아 숫자
java.time의 FormatStyle과 같이 날짜/시간 라이브러리에서 제공하는 사전 정의된 형식 유형이 있습니다. 가능하다면 사용하세요. 그렇지 않으면 표준화된 패턴 기호를 사용하여 더욱 사용자 정의된 형식을 구성할 수 있습니다. 자세한 내용은 CLDR 문서를 참조하세요.
월, 분기, 요일의 경우 독립형 변형도 있다는 점은 주목할 가치가 있습니다. 이는 일부 언어(영어 제외)에서만 의미가 있습니다. 예를 들어 폴란드어에서는 4월을 kwiecień이라고 합니다. 이는 독립형 버전입니다. 그러나 날짜(예: 4월 18일)와 연결되면 텍스트는 18 kwietnia가 됩니다.
날짜와 시간을 처리하는 것은 간단하지 않습니다. 하지만 위에서 설명한 기준을 따르고 적절한 유형을 사용한다면 큰 실수를 피할 수 있습니다.
날짜와 시간에 민감한 사례에 대한 저의 통찰력이 귀하의 프로젝트에 도움이 되기를 바랍니다. 행운을 빕니다!
원본은 2021년 4월 26일 https://www.thedroidsonroids.com에 게시되었습니다.
위 내용은 앱 및 백엔드 개발의 극단적 사례 — 날짜 및 시간의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!