-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathdate.html
3574 lines (2960 loc) · 90.5 KB
/
date.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>chrono::date</title>
<style type="text/css">
p {text-align:justify;}
li {text-align:justify;}
blockquote.note
{
background-color:#E0E0E0;
padding-left: 15px;
padding-right: 15px;
padding-top: 1px;
padding-bottom: 1px;
}
ins {background-color:#A0FFA0;}
del {background-color:#FFA0A0;}
address {text-align:right;}
h1 {text-align:center;}
span.comment {color:#C80000;}
table {border-width:1px; border-color:black; border-style:solid;}
td {border-width:1px; border-color:black; border-style:solid; padding:5px;}
</style>
</head>
<body>
<address>
Document number: Dxxxx=xx-xxxx<br>
<br>
<a href="mailto:howard.hinnant@gmail.com">Howard Hinnant</a><br>
2011-08-07
</address>
<hr>
<h1><code>chrono::date</code></h1>
<p>
This work has been superseded. It is left here so that people can read the
history if they really want to. But the latest word on this subject is found
here:
</p>
<blockquote>
<a href="date_v2.html"><code>date</code></a>
</blockquote>
<h2>Introduction</h2>
<p>
The subject of this paper is a date class which has been strongly influenced by
<code>boost::date</code>, but is not proposing exactly what is currently
<code>boost::date</code>. What is proposed herein is a relatively small
library. The implementation is currently composed of 3 different performance
design alternatives, all conforming to a single specification, and the total
number of semicolons in the union of all three designs is only 523. Suffice it
to say that we are talking approximately 400 lines of C++ code to implement any
one of the 3 implementation design alternativs prototyped in the example code,
not 5000 lines of C++ code (the approximate size of boost date time). The API
proposed herein is correspondingly small, and yet powerful enough to enable the
client to express everything needed in this area.
</p>
<p>
The three example implementations can be found in <a
href="date"><code><date></code></a> and <a
href="date.cpp"><code>date.cpp</code></a>.
</p>
<p>
The <code>boost::date</code> API is considerably larger. Additionally the
<code>boost::date</code> API allows for a few situations which can be visually
ambiguous. For example, here is an example using boost with the intent of
forming January 2, 2011:
</p>
<blockquote><pre>
#include "boost/date_time/gregorian/gregorian.hpp"
#include <iostream>
int main()
{
using namespace boost::gregorian;
<span class="comment">// I'm from the US and am used to month/day/year</span>
std::cout << date(1, 2, 2011) << '\n';
}
</pre></blockquote>
<p>
The above results in a run time error because the <code>boost::date</code>
constructor expects the order of arguments to be year, month, day. This
proposal allows the entry of day, month and year to vary. Additionally the first
two entries must be a type that unambiguously gives the units (day,
month or year). For example:
</p>
<blockquote><pre>
std::cout << month(1)/day(2)/2011 << '\n'; <span class="comment">// 2011-01-02</span>
</pre></blockquote>
<p>
If you prefer the year/month/day order, that is just as easily accomplished:
</p>
<blockquote><pre>
std::cout << year(2011)/month(1)/2 << '\n'; <span class="comment">// 2011-01-02</span>
</pre></blockquote>
<p>
This library is placed in namespace <code>std::chrono</code>. This is just
another time-keeping library, but it counts sunrises instead of seconds. And it
interoperates with the existing <code>chrono</code> library.
</p>
<!--
<h2><code>date</code> Construction</h2>
-->
<h2><code>date</code> Construction</h2>
<p>
Significant effort has been put into designing a library that makes it easy to
correctly construct a date, unlikely to construct an unexpected date, and
impossible to construct an invalid date.
</p>
<p>
In all a <code>chrono::date</code> can be entered with three variations of the
order of years, months and days. The first two units must be specified, and
disambiguates which order you are using. Use of explicit units for the third
unit is optional. These three orders were chosen out of the six possible orders
because these are the only orders that people actually use worldwide.
</p>
<blockquote><pre>
int m, d, y;
<span class="comment">// give values to m, d and y ...</span>
date d1 = year(y) / month(m) / day(d); <span class="comment">// ymd ok</span>
date d2 = month(m) / day(d) / year(y); <span class="comment">// mdy ok</span>
date d3 = day(d) / month(m) / year(y); <span class="comment">// dmy ok</span>
</pre></blockquote>
<p>
The other three orderings of <code>year</code>, <code>month</code>, and
<code>day</code> are rejected <b>at compile time.</b>
</p>
<blockquote><pre>
int m, d, y;
<span class="comment">// give values to m, d and y ...</span>
date d1 = year(y) / day(d) / month(m); <span class="comment">// error: use of overloaded operator '/' is ambiguous</span>
date d2 = month(m) / year(y) / day(d); <span class="comment">// error: invalid operands to operator '/'</span>
date d3 = day(d) / year(y) / month(m); <span class="comment">// error: invalid operands to operator '/'</span>
</pre></blockquote>
<p>
The rationale for this is that there exists <a
href="http://en.wikipedia.org/wiki/Date_format_by_country">no country on the
planet</a> which uses any other ordering. This is in contradiction to
[locale.time.get] which specifies the above three orderings plus a
<code>ydm</code> ordering. There is no inconsistency in not offering a
<code>year/day/month</code> ordering. No current code will be invalidated. This
is simply a lack of support for the <code>ydm</code> ordering.
</p>
<p>
Furthermore, there exists <code>const</code> objects of type <code>month</code>
named <code>jan</code>, <code>feb</code>, ... <code>dec</code>. These can be
used exactly as <code>month(m)</code> is used:
</p>
<blockquote><pre>
date d1 = jan/day(2)/2011; <span class="comment">// jan/2/2011</span>
date d2 = year(2011)/jan/2; <span class="comment">// jan/2/2011</span>
date d3 = day(2)/jan/2011; <span class="comment">// jan/2/2011</span>
</pre></blockquote>
<p>
This makes creating date literals very easy, not prone to error due to ordering
issues, and unambiguous to read. Also in this proposal it is not possible to
create an invalid date. As dates are constructed or modified by date
arithmetic, range checking is done, and an exception (<code>bad_date</code>) is
thrown if any field of the <code>date</code> goes out of range.
</p>
<p>
Additionally there are const objects with the following names that can be used
to specify the day of the month:
</p>
<blockquote><pre>
_1st
_2nd
_3rd
_4th
_5th
last
</pre></blockquote>
<p>
Now you can write (for example):
</p>
<blockquote><pre>
date d1 = jan/_2nd/2011; <span class="comment">// jan/2/2011</span>
date d2 = year(2011)/jan/_2nd; <span class="comment">// jan/2/2011</span>
date d3 = _2nd/jan/2011; <span class="comment">// jan/2/2011</span>
</pre></blockquote>
<p>
Note the constant <code>last</code>. This provides a very easy way to specify
the last day of the month:
</p>
<blockquote><pre>
date d1 = last/jan/2011; <span class="comment">// jan/31/2011</span>
</pre></blockquote>
<p>
Sometimes you don't know exactly which day of the month you want, but instead
know what weekday of the month you want. For example Mother's Day is celebrated
on the second Sunday in May in the US:
</p>
<blockquote><pre>
date MothersDay = sun[2]/may/2011; <span class="comment">// may/8/2011</span>
date MothersDay = sun[_2nd]/may/2011; <span class="comment">// may/8/2011</span>
</pre></blockquote>
<p>
If you ask for a non-existent date (e.g. the fifth Friday of May in 2011) a
<code>bad_date</code> exception will be thrown. If what you really want is the
fifth Friday in May only if it exists, else the fourth Friday, you can use
<code>last</code>.
</p>
<blockquote><pre>
date d1 = fri[last]/may/2011; <span class="comment">// may/27/2011</span>
</pre></blockquote>
<p>
If you want to find out how many Fridays there are in May 2011, it is easy to
code with:
</p>
<blockquote><pre>
int num_fri_in_may = (fri[last]/may/2011).day() > 28 ? 5 : 4; <span class="comment">// 4</span>
</pre></blockquote>
<p>
If you don't know which day of the week you're looking for until run time, you
can use <code>weekday</code> in the exact same way you use the const objects
<code>sun</code> through <code>sat</code>:
</p>
<blockquote><pre>
int wd = ...
date d1 = weekday(wd)[_1st]/may/2011;
</pre></blockquote>
<p>
Creating <code>date</code>s is safe, easy, intuitive, and efficient.
</p>
<!--
<h2><code>date</code> Arithmetic</h2>
-->
<h2><code>date</code> Arithmetic</h2>
<p>
Date arithmetic is supported for the units of days, months and years. In the
chrono calendar, the length of months and years is not a constant number of days.
This complicates the meaning of arithmetic with months and years. This library
takes a pragmatic approach to this problem which gives clients choices on how
they want to perform the arithmetic.
</p>
<!--
<h3>Year-oriented Arithmetic</h3>
-->
<h3>Year-oriented Arithmetic</h3>
<p>
For every day of the year but one, adding a year to the date has a straight
forward meaning: it modifies the year of the date, but not the day of the month
or month of the year. For example <code>oct/day(15)/2011 + years(1) ==
oct/day(15)/2012</code>. But what about Feb. 29?
</p>
<p>
The boost library addresses this issue by "snapping to the end of the month."
That is Feb. 29, 2012 + 1 year is Feb. 28, 2013. And Feb. 28, 2011 + 1 year is
Feb. 29, 2012. But consider this example: John's birthday is Feb. 28. And he
wants to print out his birthday for the decade. Using boost this looks like:
</p>
<blockquote><pre>
<span class="comment">// Print Feb. 28 for each year in the decade</span>
for (date d(2010, Feb, 28), e(2020, Feb, 28); d <= e; d += years(1))
std::cout << d << '\n';
2010-Feb-28
2011-Feb-28
2012-Feb-29
2013-Feb-28
2014-Feb-28
2015-Feb-28
2016-Feb-29
2017-Feb-28
2018-Feb-28
2019-Feb-28
</pre></blockquote>
<p>
Using the boost design, every leap year the output prints out Feb. 29
instead of Feb. 28. But John wasn't born on Feb. 29! That isn't the behavior
he wants. He finds this behavior surprising.
</p>
<p>
This library prints out Feb. 28 for each year using the syntax below.
</p>
<blockquote><pre>
<span class="comment">// Print Feb. 28 for each year in the decade</span>
for (date d = feb/day(28)/2010, e = feb/day(28)/2020; d != e; d += years(1))
std::cout << d << '\n';
2010-02-28
2011-02-28
2012-02-28
2013-02-28
2014-02-28
2015-02-28
2016-02-28
2017-02-28
2018-02-28
2019-02-28
</pre></blockquote>
<p>
And if you add 1 year to <code>feb/day(29)/2012</code>, a <code>bad_date</code>
exception is thrown because there is no <code>feb/day(29)/2013</code>. You get
exactly the same result as if you had attempted to construct
<code>feb/day(29)/2013</code> directly.
</p>
<p>
But now Sue comes along, and she happens to have been born on Feb. 29. And she
doesn't want to have to wait 4 years to celebrate her birthday. She decides
that she wants to celebrate her birthday on the last day of Feb. every year.
She can print out her birthdays just as easily as John did:
</p>
<blockquote><pre>
<span class="comment">// Print the last day in Feb. for each year in the decade</span>
for (date d = feb/last/2010, e = feb/last/2020; d != e; d += years(1))
std::cout << d << '\n';
2010-02-28
2011-02-28
2012-02-29
2013-02-28
2014-02-28
2015-02-28
2016-02-29
2017-02-28
2018-02-28
2019-02-28
</pre></blockquote>
<p>
When year-oriented arithmetic is applied to a date that has been constructed
with <code>last</code>, the library knows that although <code>feb/last/2011 ==
feb/day(28)/2011</code>, <code>feb/last/2011 + years(1) ==
feb/day(29)/2012</code>. And the result of this computation behaves as if it had
been constructed with <code>feb/last/2012</code>, not
<code>feb/day(29)/2012</code>. Thus throughout the for-loop above, the variable
<code>d</code> always represents the last day of Feb., no matter what the year
is.
</p>
<p>
So this library enables both desired behaviors (do not "snap-to" and "snap-to"),
and also delivers results that are not surprising to the casual reader of the
code. And the arithmetic is reversible. If you add a year to a date (and this
results in a valid date), and then subtract a year from that result, then you
<i>always</i> get back your original date. This is <b>not</b> true of the boost
date library. For example with boost, if you add a year to Feb. 28, 2012, you
get Feb. 28, 2013. If you then subtract a year, you get Feb. 29, 2012, not the
original date Feb. 28, 2012.
</p>
<p>
To complete the birthday example, Sam, like Sue, was born on Feb. 29. But unlike
Sue, he wants to always celebrate his birthday on the day after Feb. 28. This
is also easy to accomplish:
</p>
<blockquote><pre>
<span class="comment">// Print the day after Feb. 28 for each year in the decade</span>
for (date d = feb/day(28)/2010, e = feb/day(28)/2020; d != e; d += years(1))
std::cout << d + days(1) << '\n';
2010-03-01
2011-03-01
2012-02-29
2013-03-01
2014-03-01
2015-03-01
2016-02-29
2017-03-01
2018-03-01
2019-03-01
</pre></blockquote>
<!--
<h3>How expensive is all this?!</h3>
-->
<h3>How expensive is all this?!</h3>
<p>
The implementation cost of this behavior is very inexpensive. It need not
impact the <code>sizeof(date)</code> at all, no matter what strategy the
vendor is using to store the <code>date</code>. The run time cost is minimal,
involving simply checking a few bits of the representation to choose the exact
arithmetic algorithm. These assertions are backed by an example implementation
for each of the three obvious storage strategies:
</p>
<ol>
<li>Store year, month, day, and a count of days since the epoch. (Storage space is 64 bits)</li>
<li>Store a count of days since the epoch. (Storage space is 32 bits)</li>
<li>Store year, month, and day. (Storage space is 32 bits)</li>
</ol>
<p>
In each example implementation above, 6 bits of storage are used to store
information such as: this date represents the last day of February. These
6 bits do not participate in the date comparison operations. The remaining bits
(58 or 26) are more than sufficient to store the indicated data.
</p>
<p>
In an attempt to quantify the cost of this library, I compared it with boost
for the case that they both produce the same results: computing the last day
of Feb. for years 2010 thru 2020.:
</p>
<blockquote><pre>
for (date d(2010, Feb, 28), e(2020, Feb, 28); d <= e; d += years(1))
</pre></blockquote>
<p>
vs.
</p>
<blockquote><pre>
for (date d = feb/last/2010, e = feb/last/2020; d != e; d += years(1))
</pre></blockquote>
<p>
Because I/O is rather expensive I set the body of the for loop to just:
</p>
<blockquote><pre>
;
</pre></blockquote>
<p>
I wrapped the for loop with calls to
<tt>std::chrono::high_resolution_clock::now()</tt> and printed out the
results in units of microseconds (represented as a double). I also printed
out the <tt>sizeof date</tt>. I ran each case 3 times and am reporting here
the average of those 3 runs. I also ran this test for each of the 3
<code>std::chrono</code> implementations described above. All tests were run
with clang -O3. The variance of the 3 runs for each implementation is
considerably less than the difference between the averages reported here.
</p>
<blockquote>
<p><b>boost:</b></p>
<blockquote><pre>
5.08 microseconds
sizeof(date) = 4
</pre></blockquote>
<p><b>chrono, implementation 1:</b></p>
<blockquote><pre>
1.80 microseconds
sizeof(date) = 8
</pre></blockquote>
<p><b>chrono, implementation 2:</b></p>
<blockquote><pre>
2.64 microseconds
sizeof(date) = 4
</pre></blockquote>
<p><b>chrono, implementation 3:</b></p>
<blockquote><pre>
1.75 microseconds
sizeof(date) = 4
</pre></blockquote>
</blockquote>
<p>
While this is clearly not a comprehensive performance test, it does at least
establish that this proposal is in the same ballpark, performance wise, as
the boost date library. The boost storage design is most analogous to what
this paper calls "implementation 2". This storage strategy will excel at
adding days to a date as this operation has no need to convert between the
count of days since the epoch, and the year, month, day triple.
</p>
<p>
Again, this proposal is neutral on the storage strategy used (1, 2 or 3). Each
has advantages and disadvantages.
</p>
<p>
Each of the above example implementations support a range of
<code>year(numeric_limits<short>::min())/jan/1</code> through
<code>year(numeric_limits<short>::max())/dec/31</code> inclusive (range
governed by representing the year by a <code>short</code>). Thus this is a
<a href="http://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar">proleptic
Gregorian calendar</a>. The year preceding year 1 is year 0, and year 0 is a
leap year. This is consistent with the definition of an <i>expanded
calendar</i> given in ISO 8601:2004: <i>Data elements and interchange formats
— Information interchange — Representation of dates and times</i>.
</p>
<!--
<h3>Month-oriented Arithmetic</h3>
-->
<h3>Month-oriented Arithmetic</h3>
<p>
One can add and subtract <code>months</code> to a <code>date</code> with the
same ease and semantics as is done with <code>years</code>. For example you can
add a month to <code>jul/day(31)/2011</code> which results in
<code>aug/day(31)/2011</code>. But if you add a month to
<code>aug/day(31)/2011</code> a <code>bad_date</code> is thrown since
<code>sep/day(31)/2011</code> does not exist. However you can <i>always</i> add
a month to the <code>_1st</code> day of the month, or to <i>any</i> day of the
month <= 28, or to the <code>last</code> day of the month. For example:
</p>
<blockquote><pre>
<span class="comment">// Print last day of every month in 2011</span>
for (date d = jan/last/2011, e = dec/last/2011; d <= e; d += months(1))
std::cout << d << '\n';
2011-01-31
2011-02-28
2011-03-31
2011-04-30
2011-05-31
2011-06-30
2011-07-31
2011-08-31
2011-09-30
2011-10-31
2011-11-30
2011-12-31
<span class="comment">// Print the 28th of every month in 2011</span>
for (date d = jan/day(28)/2011, e = dec/day(28)/2011; d <= e; d += months(1))
std::cout << d << '\n';
2011-01-28
2011-02-28
2011-03-28
2011-04-28
2011-05-28
2011-06-28
2011-07-28
2011-08-28
2011-09-28
2011-10-28
2011-11-28
2011-12-28
</pre></blockquote>
<p>
It is also easy to print out the 29th of every month. However one needs to
explicitly decide what you want to do for months with less than 29 days. One
obvious choice is to simply skip such months:
</p>
<blockquote><pre>
<span class="comment">// Print the 29th of every month in 2011</span>
for (date d = last/jan/2011, e = last/dec/2011; d <= e; d += months(1))
if (d.day() >= 29)
std::cout << d.year()/d.month()/29 << '\n';
2011-01-29
2011-03-29
2011-04-29
2011-05-29
2011-06-29
2011-07-29
2011-08-29
2011-09-29
2011-10-29
2011-11-29
2011-12-29
</pre></blockquote>
<p>
Year and month-oriented arithmetic also respects nth-day-of-the-week dates.
For example if you want to print out the 2nd Tuesday of every odd month that
is easily done with:
</p>
<blockquote><pre>
<span class="comment">// Print the 2nd Tuesday of every odd month in 2011</span>
for (date d = tue[2]/jan/2011, e = dec/day(31)/2011; d <= e; d += months(2))
std::cout << d << '\n';
2011-01-11
2011-03-08
2011-05-10
2011-07-12
2011-09-13
2011-11-08
</pre></blockquote>
<p>
This final example should be emphasized. Imagine you've just met an extended
family at a restaurant, one whom you have not met before. They're celebrating.
There's children, parents and grandparents present. They enjoy your company so
much they invite you back for the same celebration, same place, "next year." You
check the current date and it is May 8, 2011.
</p>
<p>
On what date should you plan to attend? If they were celebrating a birthday,
then you should come back on May 8, 2012. But if they were celebrating Mother's
Day then you should come back on May 13, 2012 (the second Sunday of May in 2012)
— five whole days later!
</p>
<blockquote><i>
Adding a year (or a month) to a date is intrinsically context sensitive.
</i></blockquote>
<p>
The expressions <code>d + years(1)</code> and <code>d + months(1)</code> are
only unambiguous when you know the context within which <code>d</code> was
constructed. This library stores that context as part of <code>d</code>'s
state.
</p>
<h3>Day-oriented Arithmetic</h3>
<p>
Day-oriented arithmetic is intrinsically less complicated than month and
year-oriented arithmetic. The chrono calendar is nothing but a count of
sunrises, and a distinct name for each sunrise. You can add any number of
<code>days</code> to any <code>date</code> and the result is <i>always</i> a
valid date (unless one exceeds the valid range for years). For example:
</p>
<blockquote><pre>
<span class="comment">// Print out every monday between jan/1/2011 and mar/1/2011;</span>
for (date d = mon[_1st]/jan/2011, e = mar/_1st/2011; d <= e; d += days(7))
std::cout << d << '\n';
2011-01-03
2011-01-10
2011-01-17
2011-01-24
2011-01-31
2011-02-07
2011-02-14
2011-02-21
2011-02-28
</pre></blockquote>
<p>
In the above example, the first Monday of the year is found, and then to get
each Monday after that, one simply adds 7 days. There is <i>always</i> another
Monday coming up!
</p>
<p>
Additionally, if you subtract two dates, the result is a
<code>chrono::duration</code> with the name <code>days</code>.
</p>
<blockquote><pre>
<span class="comment">// How many days between may/1/2011 and jan/1/2011?</span>
int x = (may/_1st/2011 - jan/_1st/2011).count(); <span class="comment">// x == 120</span>
</pre></blockquote>
<p>
<i>Question:</i> So what happens when you subtract a day from
<code>aug/last/2011</code> and then add a day back? The resultant day will be
equal to <code>aug/day(31)/2011</code>. But will it still represent the last day
of the month as far as month and year arithmetic is concerned?
</p>
<p>
<i>Answer:</i> No. <code>date</code>s tagged with "last" information become
untagged with that information as soon as they have <code>days</code> added to
or subtracted from them. So while <code>aug/last/2011 + months(1)</code> is
equal to <code>sep/day(30)/2011</code>, <code>aug/last/2011 - days(1) + days(1)
+ months(1)</code> results in a <code>bad_date</code> exception because
<code>sep/day(31)/2011</code> does not exist. This is the same behavior you
would get if you added <code>months(1)</code> to <code>aug/day(31)/2011</code>.
</p>
<h2><code>date</code> Observers</h2>
<p>
Given a date <code>d</code> you can ask for its <code>day()</code>, <code>month()</code>
and <code>year()</code>. These each return a <code>day</code>, <code>month</code> and
<code>year</code> respectively. Note that these returned types are not the
<i>durations</i> <code>days</code>, <code>months</code> and <code>years</code>. Rather they
are the <i>unit-specifiers</i> used to specify a day, month and year which you
used to construct the <code>date</code>. Each unit-specifier is implicitly
convertible to an <code>int</code>. Example uses:
</p>
<blockquote><pre>
date dt = aug/day(16)/2011;
int d = dt.day(); <span class="comment">// d == 16</span>
int m = dt.month(); <span class="comment">// m == 8</span>
int y = dt.year(); <span class="comment">// y == 2011</span>
</pre></blockquote>
<p>
And:
</p>
<blockquote><pre>
date dt = aug/day(16)/2011;
<span class="comment">// ...</span>
<span class="comment">// Create date with the same month and year but on the 5th</span>
date dt2 = dt.year()/dt.month()/5; <span class="comment">// aug/5/2011</span>
</pre></blockquote>
<p>
Note that in the latter example if <code>year()</code> returned a simple
<code>int</code> instead of a <code>year</code> then the construction of
<code>dt2</code> would start with an <code>int</code> instead of a
<code>year</code> and thus not be a well-formed <code>date</code>.
</p>
<p>
To get the <code>weekday</code> (day of the week) of a date use the
<code>weekday()</code> member function. This returns a <code>weekday</code> type
which is implicitly convertible to <code>int</code>. One can use this to
print out an <code>int</code> which represents the day of the week:
</p>
<blockquote><pre>
date dt = aug/day(16)/2011;
<span class="comment">// What day of the week is this?</span>
int wd = dt.weekday(); <span class="comment">// 2 (Tuesday)</span>
</pre></blockquote>
<p>
Or one can find the first same weekday of the month of the following year:
</p>
<blockquote><pre>
date dt = aug/day(16)/2011;
<span class="comment">// ...</span>
<span class="comment">// Get the date that is the first occurrence of the same day of the week</span>
<span class="comment">// in the same month of the next year</span>
date dt2 = dt.weekday()[_1st]/dt.month()/(dt.year() + 1); <span class="comment">// aug/7/2012, first Tuesday of Aug 2012</span>
</pre></blockquote>
<p>
<i>This syntax <b>has</b> power.</i> There are nearly infinitely many
applications for a date class which we can not imagine. This library creates a
small, simple, efficient and consistent language of dates and date arithmetic
which is widely applicable to all of the date applications which we have yet to
imagine. And because the API for this library is small, it is easy to teach,
and easy to learn.
</p>
<h2>Finding the next or previous <code>weekday</code></h2>
<p>
Sometimes, given a date, one needs to find the previous Monday, or the following
Sunday. For example the ISO week-based year starts on the Monday that falls on
or before Jan 4 of each year. With this library you can code that date for
year <code>y</code> as:
</p>
<blockquote><pre>
date ISO_week_start = mon <= jan/day(4)/y;
</pre></blockquote>
<p>
That is, the expression <code>wd <= x</code> returns the <code>date y</code>
such that <code>y</code> is the first <code>date</code> equal to or prior to
<code>x</code> such that <code>y.weekday() == wd</code>. There are four such
operations summarized here. Let <code>wd</code> be a <code>weekday</code>
expression and <code>d</code> be a <code>date</code> expression:
</p>
<blockquote>
<table>
<tr>
<td><code>wd < d</code></td>
<td>Returns the first <code>date</code> prior to <code>d</code> that has
<code>wd</code> as its <code>weekday</code>.</td>
</tr>
<tr>
<td><code>wd <= d</code></td>
<td>Returns the first <code>date</code> on or prior to <code>d</code> that has
<code>wd</code> as its <code>weekday</code>.</td>
</tr>
<tr>
<td><code>wd > d</code></td>
<td>Returns the first <code>date</code> after <code>d</code> that has
<code>wd</code> as its <code>weekday</code>.</td>
</tr>
<tr>
<td><code>wd >= d</code></td>
<td>Returns the first <code>date</code> on or after <code>d</code> that has
<code>wd</code> as its <code>weekday</code>.</td>
</tr>
</table>
</blockquote>
<h2><code>date</code> I/O</h2>
<p>
<code>date</code>s are obviously streamable. The default formatting is
consistent with ISO 8601: yyyy-mm-dd, as has been alluded to in previous
examples. Additionally there is a <code>datepunct facet</code> and a
<code>date_fmt</code> manipulator. These are basically C++ wrappers around C's
<code>strftime</code>. And they also serve as wrappers around the
non-standard-but-popular <code>strptime</code> for parsing <code>date</code>s
from an <code>istream</code>.
</p>
<p>
To demonstrate the ease with which <code>date</code>s can be formatted, I'm
taking a real-world example from my wife: She once set up a recurring meeting
for the odd Fridays of every month. That is, they met on the first, third, and
if it existed, the fifth Friday of every month. When I asked her why, she said:
"Every week was too often, and every other week wasn't often enough."
<shrug>
</p>
<p>
Here is how you print out the odd Fridays of every month in 2011, using
<code>date_fmt</code> to format the <code>date</code> however you want:
</p>
<blockquote><pre>
std::cout << date_fmt("%a %b %e, %Y");
<span class="comment">// Print the odd fridays of every month in 2011</span>
for (date d = fri[_1st]/jan/2011, e = dec/last/2011; d <= e; d += months(1))
{
std::cout << d << '\n'; <span class="comment">// first Friday of the month</span>
std::cout << d + days(14) << '\n'; <span class="comment">// third Friday of the month</span>
date last_fri = fri[last]/d.month()/d.year();
if (last_fri.day() >= 29)
std::cout << last_fri << '\n'; <span class="comment">// fifth Friday of the month if it exists</span>
}
Fri Jan 7, 2011
Fri Jan 21, 2011
Fri Feb 4, 2011
Fri Feb 18, 2011
Fri Mar 4, 2011
Fri Mar 18, 2011
Fri Apr 1, 2011
Fri Apr 15, 2011
Fri Apr 29, 2011
Fri May 6, 2011
Fri May 20, 2011
Fri Jun 3, 2011
Fri Jun 17, 2011
Fri Jul 1, 2011
Fri Jul 15, 2011
Fri Jul 29, 2011
Fri Aug 5, 2011
Fri Aug 19, 2011
Fri Sep 2, 2011
Fri Sep 16, 2011
Fri Sep 30, 2011
Fri Oct 7, 2011
Fri Oct 21, 2011
Fri Nov 4, 2011
Fri Nov 18, 2011
Fri Dec 2, 2011
Fri Dec 16, 2011
Fri Dec 30, 2011
</pre></blockquote>
<h2>Interoperability with other calendar systems</h2>
<p>
There are other calendar systems besides the <code>chrono</code> (Gregorian)
calendar. For example just to name a few:
</p>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Islamic_calendar">Islamic calendar</a></li>
<li><a href="http://en.wikipedia.org/wiki/Hebrew_calendar">Hebrew calendar</a></li>
<li><a href="http://en.wikipedia.org/wiki/Hindu_calendar">Hindu calendar</a></li>
<li><a href="http://en.wikipedia.org/wiki/Chinese_calendar">Chinese calendar</a></li>
<li><a href="http://en.wikipedia.org/wiki/Buddhist_calendar">Buddhist calendar</a></li>
<li><a href="http://en.wikipedia.org/wiki/Julian_Calendar">Julian calendar</a></li>
</ul>
<p>
This paper proposes only the
<a href="http://en.wikipedia.org/wiki/Gregorian_calendar">Gregorian calendar</a>
because this represents existing practice for C, C++, POSIX, and ISO 8601.
However one has to wonder: shouldn't we design a framework which can support
any of the world's calendars?
</p>
<p>
I claim that such a general framework is unnecessary, and we would likely get it
wrong. The reason it is unnecessary is that clients can easily write their own
calendars which convert to and from the <code>chrono</code> calendar using only
the public API proposed herein. Their calendar may or may not use an API
similar to the <code>chrono</code> API. Aside from the Julian calendar, the
I/O facets <code>time_get</code>, <code>time_put</code>, and
<code>datepunct</code> are not reusable by these other calendars. Indeed,
there is very little opportunity for code reuse by making a "calendar
framework".
</p>
<p>
To demonstrate, I coded a Julian calendar which converts to and from the
<code>chrono</code> calendar (using no knowledge whatsoever of the implementation
of <code>chrono</code>). This calendar is <em>not</em> proposed but shown here
only for demonstration purposes.
</p>
<ul>
<li><a href="julian.h">julian.h</a></li>
<li><a href="julian.cpp">julian.cpp</a></li>
</ul>
<p>
And here is example use showing how the two calendars can be converted to one
another:
</p>
<blockquote><pre>
#include <iostream>
#include "date"
#include "julian.h"
int main()
{
std::cout << std::chrono::date_fmt("%a %b %e, %Y");
std::chrono::date cd = std::chrono::date::today();
<b>julian::date jd(cd);</b>
std::cout << "Today is:\n"
<< cd << " in the std::chrono calendar and\n"
<< jd << " in the Julian calendar\n";
jd -= julian::years(1800);
<b>cd = std::chrono::date(jd);</b>
std::cout << "1800 years ago the two calendars were aligned:\n"
<< cd << " in the std::chrono calendar and\n"
<< jd << " in the Julian calendar\n";
}
Today is:
Fri May 6, 2011 in the std::chrono calendar and
Fri Apr 23, 2011 in the Julian calendar
1800 years ago the two calendars were aligned:
Tue Apr 23, 0211 in the std::chrono calendar and
Tue Apr 23, 0211 in the Julian calendar
</pre></blockquote>
<p>
I firmly believe that any other calendar can be converted to and from the
<code>chrono</code> calendar using the techniques shown here for the Julian
calendar, and that we need to do nothing to enable clients to do so. Furthermore
actually providing these other calendars is far outside of our scope.
</p>
<h2>A non-trivial example</h2>
<p>
There's nothing like real-world use to test an interface. This is when you
find out if using a given API is like flying with the wind, or wading through
waist-deep water. In this spirit we would like to present two <i>user-written</i>
functions drawn from real-world usage. These two functions are not proposed,
though they certainly could be.
</p>
<blockquote><pre>
std::tuple<int, std::chrono::weekday, std::chrono::year>
date_to_week(std::chrono::date d)