Skip to content
Jacob Quinn edited this page Jul 27, 2013 · 15 revisions

From the beginning, Julia has had a problem with being greedy. This same greed has driven the development of the Datetime package. With roots in Joda/JS-310, numpy datetime64, lubridate, zoo, and runt, the Datetime package aims to greedily provide a DateTime implementation that is not only top in efficiency and performance, but that also contains a full range of types and methods for working with dates, times, periods, and time series of any flavor. Dates and times can sometimes be tricky to work with, so Datetime strives for accuracy, clarity, and simplicity.

We also realize we're far from perfect, so tire-kicking, feature requests, and bug reports are heartily welcomed and appreciated!


Manual Content

  • Calendars/Type Parameters
  • Period Types
  • TimeTypes
  • Date
  • DateTime
  • Time Series

Calendars/Type Parameters

Following Joda-time's idea of pluggable chronologies, Datetime defines Calendar, an abstract type that can be subtyped for specific calendar implementations and that all Period/TimeTypes take as a parameter. Datetime defines a default Calendar with abstract ISOCalendar <: Calendar which follows ISO 8601 standards. Using a Calendar type as a type and method parameter is a core principle of the Datetime package; it not only leverages Julia's multiple dispatch for Calendar-specific methods, but also allows for extremely efficient Date, DateTime, and Period types that don't need to store certain metadata like a Calendar value/pointer. Indeed, these types simply wrap 32 and 64-bit integers; yep, just 4 and 8 [bytes!][1]. The same style is used to specify an offset for DateTime; thus, PST (Pacific Standard Time) is an Offsets type that DateTime can take as a parameter upon creation and is used in calculating the appropriate offset when showing its value. Let's see some of these parameters in action:

julia> y = year(1)
1 year

julia> typeof(y)
Year{ISOCalendar}

julia> dt = today()
2013-07-26

julia> typeof(dt)
Date{ISOCalendar}

julia> calendar(dt)
ISOCalendar

julia> typeof(calendar(dt))
DataType

julia> super(calendar(dt))
Calendar

julia> dt2 = now(CST)
2013-07-25T23:08:12 CDT

julia> typeof(dt2)
DateTime{ISOCalendar,Zone382}

julia> super(CST)
TimeZone

julia> super(TimeZone)
Offsets

Zone382 represents the "America/Chicago" or Central Standard Time timezone (for those interested 382 comes directly from the universally used Olson timezone database zone IDs).

[1]: Compare to other languages: R = 256 bytes, Python date, datetime classes = 32, 48 bytes, Joda-time = 24-40 bytes


Period Types

Period types (i.e. Year, Month, Week, Day, etc) have always been a bit controversial in Datetime implementations. The core problem is that Periods represent a human view of time that aren't fixed durations of time. Consider 1 month; it could represent, in days, a value of 28, 29, 30, or 31 depending on the year and month context. Or 1 year could represent 365 or 366 days in the case of a leap year. In any case, the real trickiness comes out when Date/DateTime-Period arithmetic is considered (which is discussed for each type in its section below). Thanks to the power and expressiveness of Julia's type and dispatch systems, Datetime provides Period implementations that aim to be simple and predictable. Currently Year, Month, Week, Day, Hour, Minute, and Second are 32-bit Period types that can be used for Date/DateTime arithmetic, as well as some limited inter-Period arithmetic. We say "limited" because inter-Period operations aren't always clear (e.g. 1 month + 1 day = ? days). The current approach, which could possibly change, is to allow Year, Week, Day, Hour, Minute, and Second inter-ops, but for months, only Year-Month operations are allowed (it's the only conversion that's consistent). We also follow a "promote downwards" approach to not lose precision (e.g. 1 week + 7 days == 14 days). The motivation for inter-Period operations was to help Periods feel like real types that have their own behaviors and are not severely limited or awkward to work with. Constructing Period types is done through the lowercase single or plural versions of their names:

julia> y = year(1)
1 year

julia> m = months(1)
1 month

julia> w = weeks(52)
52 weeks

julia> d = days(10)
10 days

julia> h = hour(24)
24 hours

julia> mi = minute(1)
1 minute

julia> s = second(1)
1 second

Conversion between types occur with same functions applied to already created Periods:

julia> month(year(1))
12 months

julia> weeks(year(1))
52 weeks

julia> hours(day(1))
24 hours

Arithmetic and comparison operations follow the promotion rules discussed earlier.

julia> year(1) + month(1)
13 months

julia> year(1) + week(1)
53 weeks

julia> year(1) + day(1)
366 days

julia> month(1) + week(1)
ERROR: no method convert(Type{Week{ISOCalendar}},Month{ISOCalendar})
 in + at no file

julia> month(1) + day(1)
ERROR: no method convert(Type{Day{ISOCalendar}},Month{ISOCalendar})
 in + at no file

julia> week(1) == day(7)
true

julia> day(1) == seconds(86400)
true

julia> year(1) > second(1)
true

Period types also support PeriodRange, which allows a Range type to be constructed similar to the numeric Ranges in Julia.

julia> startyear = year(1)
1 year

julia> endyear = year(101)
101 years

julia> stepyear = year(25)
25 years

julia> r = startyear:stepyear:endyear
1 year:25 years:101 years

julia> first(r)
1 year

julia> last(r)
101 years

julia> length(r)
5

julia> step(r)
25 years

julia> [r]
5-element Period Array:
 1 year   
 26 years 
 51 years 
 76 years 
 101 years

TimeTypes

Datetime currently provides two TimeTypes: Date and DateTime, representing different levels of "precision". The motivation for distinct types is simple, some operations are much simpler, both in terms of code and mental reasoning, when the complexities of greater precision don't have to be dealt with. For example, the Date type has a "day-precision" (i.e. no hours, minutes, or seconds), which means that normal consideration for time zones, daylight savings/summer time, and leap seconds is unneccesary. Users should take care to think about the level precision they will actually require and choose a TimeType appropriately, remembering that the simpler the date type, the faster, more efficient, and less complex the code will be.

Date

As mentioned above, the Date implementation has "day" precision. It is represented in the format "YYYY-MM-DD". A Date type is 8 bytes (64 bits), is immutable, and takes a Calendar type as a parameter.

julia> dt = today()
2013-07-26

julia> typeof(dt)
Date{ISOCalendar}

julia> sizeof(dt)
8

julia> isbits(dt)
true

julia> isimmutable(dt)
true

The default ISOCalendar type used by the Datetime package follows ISO 8601 standards. This means dates follow the proleptic Gregorian calendar, which means that the normal calendar we think of toady, which was implemented as it currently stands in 1582, is applied retroactively back through time. So even though the folks in 1582 fast-forwarded 10 days and didn't experience October 5-14, these are valid dates for the ISOCalendar (non-ISO implementations usually switch to the Julian calendar before Oct 14, 1582).

The ISO 8601 standard is also particular about BC/BCE dates. In common use, the date 1-12-31 BC/BCE was followed by 1-1-1 AD/CE, thus no year 0 exists. The ISO standard however states that 1 BC/BCE is year 0, so 0000-12-31 is the day before -001-01-01, and year -0001 (yes, negative 1 for the year) is 2 BC/BCE, year -0002 is 3 BC/BCE, etc. Clear as mud? Just take note that if you happen to be working with BC/BCE dates and using the default ISOCalendar that the year might be off by 1 than what you initially think.

Ok, on to more important stuff.

Constructors

Dates can be constructed by parts or there is preliminary support for parsing.

julia> date(2013,7,1)
2013-07-01

julia> date(2013)
2013-01-01

julia> date(2013,7)
2013-07-01

julia> today()
2013-07-26

julia> a = "2005-01-31"
b = "2005/31/01"
c = "01.31.2005"
d = "31 01 2005"
aa = "2005,1,31"
bb = "2005-31-1"
cc = "1-31-2005"
dd = "31-1-2005"
aaa = "05-01-31"
bbb = "05/31/01"
ccc = "01.31.05"
ddd = "31 01 05"
e = "20050131"
ee = "2005131"

julia> for i in (a,b,c,d,aa,bb,cc,dd,aaa,bbb,ccc,ddd,ee,ee)
	try
		dt = date(i)
		println(i," ",dt)
	catch
		println(i," ","Can't parse")
		continue
	end
end
2005-01-31 2005-01-31
2005/31/01 2005-01-31
01.31.2005 2005-01-31
31 01 2005 2005-01-31
2005,1,31 2005-01-31
2005-31-1 2005-01-31
1-31-2005 2005-01-31
31-1-2005 2005-01-31
05-01-31 2031-05-01
05/31/01 2001-05-31
01.31.05 2005-01-31
31 01 05 2005-01-31
2005131 Can't parse
2005131 Can't parse

Note that parsing accepts a few separators (-,/,.,,,and space), in a few different formats, but care should be taken because the algorithm is really just guessing what it thinks you mean, while always defaulting to the standard ISO format YYYY-MM-DD. A planned parsedate function will take a format string as input to help specify the year, month, and day parts.

Accessors/Generic date functions

Accessor functions return the year, month, and day parts of a Date, and other generic date functions are provided for convenience in working with dates.

julia> dt = date(2013,7,1)
2013-07-01

julia> year(dt)
2013

julia> month(dt)
7

julia> day(dt)
1

julia> string(dt)
"2013-07-01"

julia> week(dt)
27

julia> isdate(dt)
true

julia> calendar(dt)
ISOCalendar

julia> typemax(Date)
252522163911149-12-31

julia> typemin(Date)
-252522163911150-01-01

julia> isleap(dt)
false

julia> lastdayofmonth(dt)
31

julia> dayofweek(dt)
1

julia> dayofweekinmonth(dt)
1

julia> daysofweekinmonth(dt)
5

julia> firstdayofweek(dt)
2013-07-01

julia> lastdayofweek(dt)
2013-07-07

julia> dayofyear(dt)
182
Duration/Comparison

As has been noted, the Date type has a day precision, so subtracting two dates results in the number of days between them.

julia> dt = date(2012,2,29)
2012-02-29

julia> dt2 = date(2000,2,1)
2000-02-01

julia> dt > dt2
true

julia> dt != dt2
true

julia> year(-dt) == -2012
true

julia> +dt == dt
true

julia> dt + dt2
ERROR: + not defined for Date/DateTime
 in error at error.jl:22

julia> dt * dt2
ERROR: * not defined for Date/DateTime
 in error at error.jl:22

julia> dt / dt2
ERROR: / not defined for Date/DateTime
 in error at error.jl:22

julia> dt - dt2
4411 days
Period Arithmetic

Datetime's approach to Period arithmetic tries to be very simple and clear while still giving the user flexibility. It's good practice when using any language/date framework to be familiar with how period arihtmetic is handled as there is no strong consensus and some tricky issues to deal with (though much less so for a day-precision type than a datetime type, more on that later). **A key thing to note is that Period arithmetic in Datetime is non-associative. As we explained earlier, inter-Period conversion is allowed, so year(1) + day(1) == days(366). That means if you try the following you may trip up trying to combine the periods before doing the date operations.

julia> dt2 = date(2000,2,1)
2000-02-01

julia> dt2 - year(4) + days(366)
1997-02-01

julia> dt2 - (year(4) - days(366))
1997-02-02

Fortunately, this is only something to remember with Years, as any operations with Months throw exceptions, and other inter-Period conversions are associative.

Also to note is that Datetime implements calendrical arithmetic, which is a slightly vague term for what we normally think of when doing date-period arithmetic. What this means is that the Periods will be added/subtracted from the date similar to a 7-7-7 slot machine, trying to only change the slot it needs to. Only when it needs to? Yes, because sometimes when you try to add 1 month to 2013-01-31, it's not apparently clear what you're expecting. 2013-02-28 because it's the last day of February? 2013-03-02 because you wanted to add 30 days? Datetime takes the approach of only changing when necessary, which means if you add/subtract a month and the new day of month happens to be "out of range" for the new month, then the new day of month becomes the last day of the new month. So for our example above date(2013,1,31) + month(1) == date(2013,2,28). Got it? It's not too difficult, but just be aware. Here's an example.

julia> dt = date(2013,1,31)
for i = 1:12
	println(dt)
	dt += month(1)
end
2013-01-31

julia> 2013-01-31
2013-02-28
2013-03-28
2013-04-28
2013-05-28
2013-06-28
2013-07-28
2013-08-28
2013-09-28
2013-10-28
2013-11-28
2013-12-28

Some more examples of general period arithmetic for your enjoyment.

julia> dt = date(2012,2,29)
2012-02-29

julia> dt2 = date(2000,2,1)
2000-02-01

julia> dt2 - year(3) == date(1997,2,1)
true

julia> dt2 - year(3)
1997-02-01

julia> dt + year(1)
2013-02-28

julia> year(3) - dt2
1997-02-01

julia> dt2 - months(3)
1999-11-01

julia> months(11) + dt
2013-01-29

julia> months(8) + dt
2012-10-29

julia> dt2 + days(4411)
2012-02-29

julia> dt2 + days(4412)
2012-03-01

julia> dt2 + weeks(52)
2001-01-30

julia> dt2 + weeks(104)
2002-01-29
DateTime

The DateTime type has a microsecond precision and is represented in the format "YYYY-MM-DDTHH:MM:SS UTC" where UTC may specify a specific timezone abbreviation or the +HH:MM/-HH:MM for a generic offset. The DateTime type is 8 bytes (64 bits), is immutable, takes a Calendar type as a parameter, and also takes a Offsets type as a parameter. There are two general types of offsets, Offset{n} for generic offsets where {n} is the number of minutes plus or minus UTC/GMT time (e.g. Offset{60} would be UTC+01:00). The other general set of offset types are TimeZone types which represent political/geographic areas around the world with rules for not only the offset from UTC/GMT, but also include daylight savings/summer time rules (e.g. CST is an alias for Zone382, which represents the "America/Chicago" timezone in the Olson timezone database).

Another important characteristic of the DateTime type is that it includes full support for leap seconds. What are leap seconds? Go read up, because every programmer should be aware, just ask LinkedIn, Reddit, or Amazon. In short, sometimes on June 30 or December 31, an extra second is added at midnight UTC to make up for the fact that the earth doesn't always spin at the same, predictable rate (due to volcanos, tectonics, etc. so I'm told). Obviously this causes problems for companies/databases who aren't prepared for an extra second. Imagine taking timestamps and a second repeats itself, or inserts get out of order, or even worse, your kernel panics and everything shuts down. While many datetime implementations choose to ignore leap seconds and allow the OS or npt to figure it out, Datetime realizes they're a fact of life and includes them at very little cost to performance. Observe:

#just a happy server churning out timestamps
julia> dt = datetime(2012,6,30,18,59,50,CST)
2012-06-30T18:59:50 CDT

julia> for i = 1:20
	println(dt)
	dt += second(1)
end
2012-06-30T18:59:50 CDT
2012-06-30T18:59:51 CDT
2012-06-30T18:59:52 CDT
2012-06-30T18:59:53 CDT
2012-06-30T18:59:54 CDT
2012-06-30T18:59:55 CDT
2012-06-30T18:59:56 CDT
2012-06-30T18:59:57 CDT
2012-06-30T18:59:58 CDT
2012-06-30T18:59:59 CDT
2012-06-30T18:59:60 CDT
2012-06-30T19:00:00 CDT
2012-06-30T19:00:01 CDT
2012-06-30T19:00:02 CDT
2012-06-30T19:00:03 CDT
2012-06-30T19:00:04 CDT
2012-06-30T19:00:05 CDT
2012-06-30T19:00:06 CDT
2012-06-30T19:00:07 CDT
2012-06-30T19:00:08 CDT

And it's as simple as that. No crashes, no hiccups, and no need for second-smearing. We'll talk a little more about leap seconds with DateTime-Period arithmetic.

Constructors

DateTimes can only be constructed by parts currently, but there is planned support for parsing.

julia> datetime(2013)
2013-01-01T00:00:00 UTC

julia> datetime(2013,7)
2013-07-01T00:00:00 UTC

julia> datetime(2013,7,1)
2013-07-01T00:00:00 UTC

julia> datetime(2013,7,1,12)
2013-07-01T12:00:00 UTC

julia> datetime(2013,7,1,12,0)
2013-07-01T12:00:00 UTC

julia> datetime(2013,7,1,12,0,0)
2013-07-01T12:00:00 UTC

julia> datetime(2013,7,1,12,0,0,UTC)
2013-07-01T12:00:00 UTC

julia> datetime(2013,7,1,12,0,0,PST)
2013-07-01T12:00:00 PDT

julia> datetime(2013,7,1,12,0,0,CST)
2013-07-01T12:00:00 CDT

julia> datetime(2013,7,1,12,0,0,"America/Chicago")
2013-07-01T12:00:00 CDT

julia> datetime(2013,7,1,12,0,0,Offset{-360})
2013-07-01T12:00:00 -06:00

julia> datetime(2013,7,1,12,0,0,offset(hours(-6)))
2013-07-01T12:00:00 -06:00

See the wikipedia page for the IANA timezone list here for supported timezone strings (thought we don't suppor all the various aliases yet).

Accessors/Generic date functions

Accessor functions return the year, month, day, hour, minute, second, and microsecond parts of a DateTime, and other generic date functions are provided for convenience in working with datetimes.

julia> dt = datetime(2013,7,1)
2013-07-01T00:00:00 UTC

julia> year(dt)
2013

julia> month(dt)
7

julia> day(dt)
1

julia> hour(dt)
0

julia> minute(dt)
0

julia> second(dt)
0

julia> string(dt)
"2013-07-01T00:00:00 UTC"

julia> week(dt)
27

julia> isdatetime(dt)
true

julia> calendar(dt)
ISOCalendar

julia> timezone(dt)
Zone0

julia> typemax(DateTime)
292277-12-31T23:59:59 UTC

julia> typemin(DateTime)
-292276-01-01T00:00:00 UTC

julia> isleap(dt)
false

julia> lastdayofmonth(dt)
31

julia> dayofweek(dt)
1

julia> dayofweekinmonth(dt)
1

julia> daysofweekinmonth(dt)
5

julia> firstdayofweek(dt)
2013-07-01T00:00:00 UTC

julia> lastdayofweek(dt)
2013-07-07T00:00:00 UTC

julia> dayofyear(dt)
182
Duration/Comparison

DateTime is microsecond precision, so the difference between two DateTimes is returned in microseconds.

julia> dt = datetime(2012,2,29)
2012-02-29T00:00:00 UTC

julia> dt2 = datetime(2000,2,1)
2000-02-01T00:00:00 UTC

julia> dt > dt2
true

julia> dt != dt2
true

julia> dt - dt2 == 381110402000000
true
Period Arithmetic

DateTime-Period arithmetic is feared by many. With all the same concerns as Date-Period arithmetic, DateTime types bring a whole new level of complexity due to TimeZones/Offsets, Daylight Savings/Summer Time, and leap seconds. Fortunately for the Datetime package, the same simple principle of trying to change as little as possible works as well for DateTime types as it did for Date types. What that means is that arithmetic works pretty much like you expect. Just be aware when dealing with Seconds, leap seconds are included, so datetime(2012,6,30,23,59,59) + second(1) == datetime(2012,6,30,23,59,60) (this is really only a concern if you're doing a calculation with seconds that span a leap second).

julia> dt = datetime(2012,2,29)
2012-02-29T00:00:00 UTC

julia> dt2 = datetime(2000,2,1)
2000-02-01T00:00:00 UTC

julia> dt2 - year(3) == datetime(1997,2,1)
true

julia> dt + year(1) == datetime(2013,2,28)
true

julia> year(3) - dt2 == dt2 - year(3)
true

julia> dt2 - months(3) == datetime(1999,11,1)
true

julia> months(11) + dt == datetime(2013,1,29)
true

julia> months(8) + dt == dt + months(8)
true

julia> dt2 + days(4411) == datetime(2012,2,29)
true

julia> dt2 + days(4412) == datetime(2012,3,1)
true

julia> dt2 + weeks(52) == datetime(2001,1,30)
true

julia> dt2 + weeks(104) == datetime(2002,1,29)
true

julia> dt2 - year(4) + days(366) == datetime(1997,2,1)
true

julia> dt2 - (year(4) - days(366)) == datetime(1997,2,2) #non-associative
true

julia> q = datetime(1972,6,30,23,59,58)
1972-06-30T23:59:58 UTC

julia> t = datetime(1972,6,30,23,59,59)
1972-06-30T23:59:59 UTC

julia> t - q == 1000000
true

julia> t == t
true

julia> r = datetime(1972,7,1,0,0,0)
1972-07-01T00:00:00 UTC

julia> r - t == 2000000 #Includes leap second
true

julia> n = datetime(1972,6,30,23,59,60)
1972-06-30T23:59:60 UTC

julia> n - t == 1000000
true

julia> r - n == 1000000
true

julia> a = datetime(1972,12,31,23,59,59)
1972-12-31T23:59:59 UTC

julia> b = datetime(1972,12,31,23,59,60)
1972-12-31T23:59:60 UTC

julia> c = datetime(1973,1,1,0,0,0)
1973-01-01T00:00:00 UTC

julia> d = datetime(1973,1,1,0,0,1)
1973-01-01T00:00:01 UTC

julia> c - b == 1000000
true

julia> b - a == 1000000
true

julia> a = datetime(1982,6,30,23,59,59)
1982-06-30T23:59:59 UTC

julia> b = datetime(1982,6,30,23,59,60)
1982-06-30T23:59:60 UTC

julia> c = datetime(1982,7,1,0,0,0)
1982-07-01T00:00:00 UTC

julia> c - b == 1000000
true

julia> b - a == 1000000
true

#daylight savings time
julia> t1 = datetime(1920,6,13,1,59,59,CST)
1920-06-13T01:59:59 CST

julia> t2 = datetime(1920,6,13,2,0,0,CST)
1920-06-13T03:00:00 CDT

julia> t3 = datetime(1920,6,13,3,0,0,CST)
1920-06-13T03:00:00 CDT

julia> t2 - t1 == 1000000
true

julia> t3 - t1 == 1000000
true

julia> t3 == t2
true

julia> t1 + second(1) == t2
true

julia> t1 + second(1) == t3
true

julia> t4 = datetime(1920,6,13,4,0,0,CST)
1920-06-13T04:00:00 CDT

julia> t4 - hour(1) == t3
true

Time Series

Time Series support is newborn in Datetime currently, but already some very useful functionality is available, particularly in quickly and painlessly creating regular frequency series. These are done through the DateRange and DateTimeRange types which inherit from Julia's traditional numeric ranges. These date ranges can be created either through a fixed Period length (i.e. day(1), weeks(2), years(4)) or also through providing an anonymous "inclusion" boolean function for irregular frequencies that still follow some kind of pattern (i.e. 3rd Thursday of every month, last day of the month, Monday and Wednesday during May-August and Tuesday/Thursday the rest of the year).

Fixed Period Frequency

When looking to create ranges like "the 1st of every month for 12 months" or "every other day from today to next month" or "this day for the next five years", fixed Period lengths can be used with a start and suggested end date (the actual end date will depend on the start-step combination). These are used with the colon (:) syntax similar to numeric ranges.

julia> dt1 = date(2000,1,1)
2000-01-01

julia> dt2 = date(2010,1,1)
2010-01-01

julia> y = year(1)
1 year

julia> r = dt1:y:dt2
2000-01-01:1 year:2010-01-01

julia> first(r)
2000-01-01

julia> last(r)
2010-01-01

julia> typeof(r.step)
Year{ISOCalendar}

julia> length(r)
11

julia> [r]
11-element Date{ISOCalendar} Array:
 2000-01-01
 2001-01-01
 2002-01-01
 2003-01-01
 2004-01-01
 2005-01-01
 2006-01-01
 2007-01-01
 2008-01-01
 2009-01-01
 2010-01-01

julia> dt2 = date(2001,1,1)
2001-01-01

julia> r = dt1:month(1):dt2
2000-01-01:1 month:2001-01-01

julia> length(r)
13

julia> [r]
13-element Date{ISOCalendar} Array:
 2000-01-01
 2000-02-01
 2000-03-01
 2000-04-01
 2000-05-01
 2000-06-01
 2000-07-01
 2000-08-01
 2000-09-01
 2000-10-01
 2000-11-01
 2000-12-01
 2001-01-01

julia> last(r)
2001-01-01

julia> typeof(r.step)
Month{ISOCalendar}

julia> r = dt1:weeks(2):dt2
2000-01-01:2 weeks:2000-12-30

julia> length(r)
27

julia> [r]
27-element Date{ISOCalendar} Array:
 2000-01-01
 2000-01-15
 2000-01-29
 2000-02-12
 2000-02-26
 2000-03-11
 2000-03-25
 2000-04-08
 2000-04-22
 2000-05-06
 â‹®         
 2000-08-26
 2000-09-09
 2000-09-23
 2000-10-07
 2000-10-21
 2000-11-04
 2000-11-18
 2000-12-02
 2000-12-16
 2000-12-30

julia> last(r)
2000-12-30

julia> typeof(r.step)
Week{ISOCalendar}

julia> dt2 = date(2000,3,1)
2000-03-01

julia> r = dt2:day(-1):dt1
2000-03-01:-1 days:2000-01-01

julia> length(r)
61

julia> [r]
61-element Date{ISOCalendar} Array:
 2000-03-01
 2000-02-29
 2000-02-28
 2000-02-27
 2000-02-26
 2000-02-25
 2000-02-24
 2000-02-23
 2000-02-22
 2000-02-21
 â‹®         
 2000-01-10
 2000-01-09
 2000-01-08
 2000-01-07
 2000-01-06
 2000-01-05
 2000-01-04
 2000-01-03
 2000-01-02
 2000-01-01

julia> last(r)
2000-01-01

julia> typeof(r.step)
Day{ISOCalendar}

julia> last(r + year(1))
2000-12-31
Irregular Frequency

Datetime provides simple, efficient syntax for creating date range patterns that follow rules other than fixed Periods. This is similar functionality to Ruby's runt and an expression of Martin Fowler's ideas on recurring calendar events. These date patterns are ideal for creating a series of holidays, business days in the year, or garbage pickups that occur on different days of the week for different months of the year. This functionality is used by providing an inclusion function rather than a Period for the range step. This can be done using the colon (:) syntax like regular date ranges, but the recur function is also provided which leverages Julia's do blocks for usage with higher-order/anonymous functions.

julia> dt = date(2009,1,1)
2009-01-01

julia> dt2 = date(2013,1,1)
2013-01-01

julia> MemorialDay = x->month(x)==May && dayofweek(x)==Monday && dayofweekinmonth(x)==daysofweekinmonth(x)
# function

julia> f = dt:MemorialDay:dt2
2009-05-25:# function:2012-05-28

julia> typeof(f)
DateRange1{ISOCalendar}  (use methods(DateRange1{ISOCalendar}) to see constructors)

julia> [f]
4-element Date{ISOCalendar} Array:
 2009-05-25
 2010-05-31
 2011-05-30
 2012-05-28

julia> length(f)
4

julia> last(f)
2012-05-28

julia> typeof(f.step)
Function

julia> Thanksgiving = x->dayofweek(x)==Thursday && month(x)==November && dayofweekinmonth(x)==4
# function

julia> f = dt:Thanksgiving:dt2
2009-11-26:# function:2012-11-22

julia> [f]
4-element Date{ISOCalendar} Array:
 2009-11-26
 2010-11-25
 2011-11-24
 2012-11-22

julia> length(f)
4

julia> last(f) == date(2012,11,22)
true

julia> ff = recur(dt:dt2) do x
	dayofweek(x)==4 && 
	month(x)==11 && 
	dayofweekinmonth(x)==4
end
2009-11-26:# function:2012-11-22

julia> [ff] == [f]
true

Clone this wiki locally