Handling dates

There are different ways to represent dates. No one way is satisfactory for all purposes. DateTime objects work well for most, if you do not have too many of them. When you do, you will want other representations, according to what you need to get done.

DateTime objects

A .Net DateTime object displays in readable form, and supports the comparison functions and also addition and subtraction. You can include them in arrays with other kinds of data. They have to be serialised to be stored, but that is little trouble when they can represent themselves as readable strings and be as easily reinstantiated from them.

Here are some examples of handling dates as DateTime objects.

      ⎕USING←'System'
      +dt←DateTime.Now             ⍝ displays readably
07/01/2008 16:56:50

      ⍴⍴dt                         ⍝ but is scalar
0

      ⍴dt.⎕NL-2 3                  ⍝ has many properties and methods
59

      ⍴⎕←dt.ToShortDateString      ⍝ represent as character vector
07/01/2008
10

The class offers many useful methods.

      dt.IsDaylightSavingTime
0
      DateTime.Parse∘⊂¨'15/9/52' '15SEP52' '15 September 1952'
 15/09/1952 00:00:00  15/09/1952 00:00:00  15/09/1952 00:00:00
      DateTime.IsLeapYear 2007
0
      DateTime.DaysInMonth¨(2007 2)(2008 2)
28 29

Comparison functions extend to the objects.

      +bd←DateTime.New 1952 9 15   ⍝ SJT’s birthday
15/09/1952 00:00:00

      ⍝ and now his whole family
      ∆←('mum' 1930 5 24)('dad' 1926 9 3)
      ∆,←('caro' 1960 11 16)('jo' 1954 7 5)('me' 1952 9 15)
      +fmly←↑{⍵[1],DateTime.New 1↓⍵}¨∆
 mum   24/05/1930 00:00:00
 dad   03/09/1926 00:00:00
 caro  16/11/1960 00:00:00
 jo    05/07/1954 00:00:00
 me    15/09/1952 00:00:00

      ⊃∘⊃∘⎕CLASS¨fmly[;2]          ⍝ 2nd column contains DateTime objects
 System.DateTime  System.DateTime  System.DateTime  System.DateTime  …

      {⍎⍺,'←⍵'}/fmly               ⍝ associate names and dates

      mum=dad                      ⍝ equivalence
0
      fmly[;2]⍳jo caro             ⍝ lookup
4 3
      mum⌊dad                      ⍝ comparison
03/09/1926 00:00:00
      fmly[⍋fmly[;2];1]            ⍝ names in birth order
 dad  mum  me  jo  caro

Subtraction extends to the objects, returning TimeSpan objects.

      mum-dad                      ⍝ days between my parents' births
1359.00:00:00
      ⊃⊃⎕CLASS mum-dad
System.TimeSpan
      (2-/caro jo me mum dad).Days ⍝ days between all our births
2326 658 8150 1359

For modest amounts of data it is hard to improve on the versatility of DateTime objects.

The obvious limit to this strategy is the number of DateTime objects you create and destroy. When handling large tables and vectors you might need to find lighter, faster ways to handle dates.

That is what this section is about.

Other formats

Switching formats

If you need a lighter representation than DateTime items, you will need some ways of converting between them. We will look first at converting between DateTime objects and other forms, then at converting between the other forms.

Finally, we shall look at algorithms that take advantage of particular representations.

DateTime and character

DateTime objects, being objects, are scalars. They display readably in the session. Applying the format function is enough to secure the default display as a character vector.

      +now←DateTime.Now
10/01/2008 18:30:40
      ⍴⍴now
0
      ⍴⎕←⍕now
10/01/2008 18:30:40
19

      ⍴⎕←fmly
 mum   24/05/1930 00:00:00
 dad   03/09/1926 00:00:00
 caro  16/11/1960 00:00:00
 jo    05/07/1954 00:00:00
 me    15/09/1952 00:00:00
5 2
      ⍴⎕←⍕fmly
 mum   24/05/1930 00:00:00
 dad   03/09/1926 00:00:00
 caro  16/11/1960 00:00:00
 jo    05/07/1954 00:00:00
 me    15/09/1952 00:00:00
5 27
      ⍴⎕←⍕¨fmly
 mum   24/05/1930 00:00:00
 dad   03/09/1926 00:00:00
 caro  16/11/1960 00:00:00
 jo    05/07/1954 00:00:00
 me    15/09/1952 00:00:00
5 2

Doesn’t get much easier than that.

DateTime objects also offer methods for specific date formats, such as:

      ⍴¨⎕←(me jo).ToShortDateString
 15/09/1952  05/07/1954
 10  10

These have the usual .Net advantage of honouring the internationalisation settings on the target machine. In similar manner, DateTime can construct instances from character vectors containing a wide range of date formats.

      DateTime.Parse∘⊂¨'15SEP52' '1952-09-15'
 15/09/1952 00:00:00  15/09/1952 00:00:00

Numeric vectors

This could hardly be easier.

      ⍴⎕←me.(Year Month Day)
1952 9 15
3
      ⍴⎕←me.(Year Month Day Hour Minute Second)
1952 9 15 0 0 0
6
      DateTime.New 1952 9 15
15/09/1952 00:00:00
      DateTime.New 1952 9 15 6 30 0
15/09/1952 06:30:00
      me=DateTime.New 1952 9 15
1
      fmly[;,1],(fmly[;2]).(Year Month Day)
 mum   1930 5 24
 dad   1926 9 3
 caro  1960 11 16
 jo    1954 7 5
 me    1952 9 15

Numeric scalars

DateTime objects don’t offer this representation so we convert to and from numeric vectors.

      100⊥1952 9 15
19520915
      100⊥1952 9 15 6 30 0
1.952091506E13
      0 100 100⊤19520915
1952 9 15
      0 100 100 100 100 100⊤19520915063000
1952 9 15 6 30 0

Note that while we need only a scalar left argument to the encode ⊥ function, we need a vector for its inverse, the decode function. That left argument might become a little unwieldy. It could be more convenient to define ed to encode dates and dd as its inverse.

      ed←0 100 100∘⊥
      dd←ed⍣¯1
      ed 1952 9 15 6 30 0
19520915
      dd 19520915063000
1952 9 15
      fmly[;,1],ed¨(fmly[;2]).(Year Month Day)
 mum   19300524
 dad   19260903
 caro  19601116
 jo    19540705
 me    19520915

Similarly et and dt for timestamps.

      et←÷∘1e6∘(0 100 100 100 100 100∘⊥)
      dt←et⍣¯1
      et 1952 9 15 6 30 0
19520915.06
      6⍕et 1952 9 15 6 30 0
 19520915.063000

OA Dates and IDNs

DateTime objects provide methods to convert to and from OA dates.

      me.ToOADate
19252
      DateTime.FromOADate 19252
15/09/1952 00:00:00
      fmly[;,1],(fmly[;2]).ToOADate
 mum   11102
 dad    9743
 caro  22236
 jo    19910
 me    19252

Watch out: these look like but differ from IDNs. Microsoft’s day count starts one day earlier than the international standard.

      (DateTime.New 1952 9 15).ToOADate
19252
      #.DateToIDN 1952 9 15
19251
      DateTime.FromOADate 19252.5
15/09/1952 12:00:00
      #.IDNToDate 19251.5
1952 9 15 0

OA Dates are exactly what you want for Microsoft applications such as Excel. They even handle fractional dates correctly. Otherwise you can use the light and fast Dyalog root methods #.DateToIDN and #.IDNToDate.

      fmly[;,1],#.DateToIDN¨(fmly[;2]).(Year Month Day)
 mum   11101
 dad    9742
 caro  22235
 jo    19909
 me    19251

Converting between other formats

Where speed matters you will avoid creating large arrays of DateTime objects. But different representations suit different purposes.

dateformats.gif

To summarise:

Here is an example of switching between representations to simplify calculation.

Calculating the next anniversary of a date is easily done in vector format.

      ⍝ next anniv of ⍵[2] after ⍵[1] (y m d)
      nxtann←{(3↑>/10000⊤100⊥⍉⍵)+⊃↓0 1 1⊖⍵}∘{↑¯3↑¨⍵}
      nxtann (2008 12 23)(12 25)
2008 12 25
      nxtann (2008 12 27)(12 25)
2009 12 25

Differences are best calculated between IDNs.

      onIDNs←{⊃⍺⍺/#.DateToIDN¨⍺ ⍵}     ⍝ ⍺⍺ on args as IDNs
      2008 12 25 -onIDNS 2008 1 11     ⍝ days to Xmas
349

For shopping days, we count the days that are not Sundays.

      wnb←{⍺+⍳⍵-⍺+1}                   ⍝ whole numbers between
      3 wnb 9
4 5 6 7 8
      sd←{×7|⍵}                        ⍝ shopping days (IDNs)

      sd2x←{+/sd ⍺ wnb onIDNs nxtann ⍺ ⍵}∘12 25
      sd2x 2008 1 11
298


Case study: shopping for Jesus

      100⊥1954 7 5
19540705
      0 100 100⊤19540705
1954 7 5
      (100⊥⍣¯1)19540705
1954 7 5
      dual←{⍺(⍺⍺⍣¯1)⍵⍵ ⍺ ⍺⍺ ⍵}
      0 100 100⊤dual {⎕←⍵} 19540705                           ⍝ display before recoding
1954 7 5
19540705
      0 100 100⊤dual {⎕←⍵} 19540705 19520915                  ⍝ my and my sister's birthdates
1954 1952
   7    9
   5   15
19540705 19520915
      0 100 100⊤dual {⎕←⍵} 20070714 19520915                  ⍝ today and my birthdate
2007 1952
   7    9
  14   15
20070714 19520915
      0 10000⊤dual {⎕←⍵} 20070714 19520915                    ⍝ split ccyy mmdd instead of ccyy mm dd
2007 1952
 714  915
20070714 19520915
      0 10000⊤dual {1 1⍉⎕←⍵} 20070714 19520915                ⍝ my birthday this year
2007 1952
 714  915
20070915
      0 10000⊤dual {1 1⍉⎕←⍵} 20071014 19520915                ⍝ but perhaps already passed
2007 1952
1014  915
20070915
      0 10000⊤dual {(2↑2⊃>/⍵)+1 1⍉⎕←⍵} 20071014 19520915      ⍝ my next birthday
2007 1952
1014  915
20080915
      nextbirthday←{0 10000⊤dual {(2↑2⊃>/⍵)+1 1⍉⍵} ⍺ ⍵}       ⍝ as a dyadic fn

      20070714 nextbirthday¨19520915 19540705                 ⍝ my and my sister's next birthdays
20070915 20080705
      Jo SJT KEI Jesus←0705 0915 1217 1225                    ⍝ birth years optional
      20070714 nextbirthday¨Jo SJT KEI Jesus
20080705 20070915 20071217 20071225

      YMDToIDN←{DateToIDN 0 100 100∘⊤⍵}                       ⍝ convert ccyymmdd to IDN
      asIDN←{⊃⍺⍺/YMDToIDN¨⍺ ⍵}                                ⍝ perform ⍺⍺ on args as IDNs
      20070915 -asIDN 20070714                                ⍝ days to my next birthday
63
      20070714{⍺ -⍨asIDN ⍺ nextbirthday ⍵}1225                ⍝ days to Christmas
164

      nb←{⍺+⍳⍵-⍺+1}                                           ⍝ whole numbers between args
      wkday←{(7|⍵)∊⍳5}                                        ⍝ is arg a weekday?

      20070714{+/wkday ⍺ nb asIDN ⍺ nextbirthday ⍵}Jesus      ⍝ weekdays to Christmas
116

StephenTaylor xxv.ii.MMVII


CategoryDyalogDotNet

Studio/EncodeDecodeDates (last edited 2015-04-05 01:47:49 by PierreGilbert)