{"id":2066,"date":"2022-01-19T09:14:27","date_gmt":"2022-01-19T08:14:27","guid":{"rendered":"https:\/\/www.nuonsoft.com\/blog\/?p=2066"},"modified":"2022-01-19T09:14:30","modified_gmt":"2022-01-19T08:14:30","slug":"c20-seemingly-unexpected-behavior-with-date-arithmetic","status":"publish","type":"post","link":"https:\/\/www.nuonsoft.com\/blog\/2022\/01\/19\/c20-seemingly-unexpected-behavior-with-date-arithmetic\/","title":{"rendered":"C++20: Seemingly Unexpected Behavior with Date Arithmetic"},"content":{"rendered":"\n<p>C++20 has added support for calendars, dates, and time zones to the C++ Standard Library. This also allows you to perform arithmetic with dates. However, certain arithmetic might give seemingly the wrong results.<br>Let&#8217;s look at a simple example that works as expected:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; quick-code: false; notranslate\" title=\"\">\nusing namespace std::chrono;\nauto timestamp1 = sys_days{ 2022y \/ January \/ 19d } + 8h + 39min + 42s;\nauto timestamp2 = timestamp1 + days{ 3 }; \/\/ Add 3 days\nstd::cout &lt;&lt; timestamp1 &lt;&lt; &#039;\\n&#039; &lt;&lt; timestamp2;\n<\/pre><\/div>\n\n\n<p>The output is as expected:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; quick-code: false; notranslate\" title=\"\">\n2022-01-19 08:39:42\n2022-01-22 08:39:42\n<\/pre><\/div>\n\n\n<p>Now let&#8217;s try to add 1 year to timestamp1:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; quick-code: false; notranslate\" title=\"\">\nauto timestamp3 = timestamp1 + years{ 1 }; \/\/ Add 1 year\nstd::cout &lt;&lt; timestamp1 &lt;&lt; &#039;\\n&#039; &lt;&lt; timestamp3;\n<\/pre><\/div>\n\n\n<p>The output now is:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; quick-code: false; notranslate\" title=\"\">\n2022-01-19 08:39:42\n2023-01-19 14:28:54\n<\/pre><\/div>\n\n\n<p>The date part is correctly incremented with 1 year, but the timestamp looks wrong on first sight. However, this is correct according to the C++ standard. The reason why it is seemingly off has to do with support for leap years. The C++ standard states that adding 1 year to a date must add 1 average year to keep leap years into account. So, while you could expect adding 1 year adds 86,400 * 365 = 31,536,000 seconds (86,400 = number of seconds in a day), it doesn&#8217;t. Instead, it adds 86,400 * ((365 * 400) + 97) \/ 400) = 31,556,952 seconds.<\/p>\n\n\n\n<p>The reason why it is behaving like this is that the type of timestamp1, 2, and 3 is std::chrono::time_point which is a so-called serial type, i.e., it represents a date as a single number relative to a certain epoch (= clock origin). If you don&#8217;t want this behavior you can perform the arithmetic with a field-based type. The serial-based types above can be converted to a field-based representation as follows:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; quick-code: false; notranslate\" title=\"\">\n\/\/ Split timestamp1 into &quot;days&quot; and &quot;remaining seconds&quot;.\nsys_days timestamp1_days = time_point_cast&lt;days&gt;(timestamp1);\nseconds timestamp1_seconds = timestamp1 - timestamp1_days;\n\n\/\/ Convert the timestamp1_days serial type to a field-based year_month_day.\nyear_month_day ymd2 = timestamp1_days;\n\n\/\/ Add 1 year.\nyear_month_day ymd3 = ymd2 + years{ 1 };\n\n\/\/ Convert the result back to a serial type.\nauto timestamp4 = sys_days{ ymd3 } + timestamp1_seconds;\nstd::cout &lt;&lt; timestamp1 &lt;&lt; &#039;\\n&#039; &lt;&lt; timestamp4;\n<\/pre><\/div>\n\n\n<p>The output now is:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; quick-code: false; notranslate\" title=\"\">\n2022-01-19 08:39:42\n2023-01-19 08:39:42\n<\/pre><\/div>","protected":false},"excerpt":{"rendered":"<p>C++20 has added support for calendars, dates, and time zones to the C++ Standard Library. This also allows you to perform arithmetic with dates. However, certain arithmetic might give seemingly the wrong results.Let&#8217;s look at a simple example that works as expected: The output is as expected: Now let&#8217;s try to add 1 year to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[242,244],"class_list":["post-2066","post","type-post","status-publish","format-standard","hentry","category-c","tag-c20","tag-chrono"],"_links":{"self":[{"href":"https:\/\/www.nuonsoft.com\/blog\/wp-json\/wp\/v2\/posts\/2066","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.nuonsoft.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.nuonsoft.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.nuonsoft.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.nuonsoft.com\/blog\/wp-json\/wp\/v2\/comments?post=2066"}],"version-history":[{"count":22,"href":"https:\/\/www.nuonsoft.com\/blog\/wp-json\/wp\/v2\/posts\/2066\/revisions"}],"predecessor-version":[{"id":2088,"href":"https:\/\/www.nuonsoft.com\/blog\/wp-json\/wp\/v2\/posts\/2066\/revisions\/2088"}],"wp:attachment":[{"href":"https:\/\/www.nuonsoft.com\/blog\/wp-json\/wp\/v2\/media?parent=2066"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.nuonsoft.com\/blog\/wp-json\/wp\/v2\/categories?post=2066"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.nuonsoft.com\/blog\/wp-json\/wp\/v2\/tags?post=2066"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}