Skip to content

Instantly share code, notes, and snippets.

@pixeltrix
Last active August 2, 2024 15:13
Show Gist options
  • Save pixeltrix/e2298822dd89d854444b to your computer and use it in GitHub Desktop.
Save pixeltrix/e2298822dd89d854444b to your computer and use it in GitHub Desktop.
When should you use DateTime and when should you use Time?

When should you use DateTime and when should you use Time?

It's a common misconception that William Shakespeare and Miguel de Cervantes died on the same day in history - so much so that UNESCO named April 23 as World Book Day because of this fact. However because England hadn't yet adopted Gregorian Calendar Reform (and wouldn't until 1752) their deaths are actually 10 days apart. Since Ruby's Time class implements a proleptic Gregorian calendar and has no concept of calendar reform then there's no way to express this. This is where DateTime steps in:

>> shakespeare = DateTime.iso8601('1616-04-23', Date::ENGLAND)
=> Tue, 23 Apr 1616 00:00:00 +0000
>> cervantes = DateTime.iso8601('1616-04-23', Date::ITALY)
=> Sat, 23 Apr 1616 00:00:00 +0000

Already you can see something's weird - the days of the week are different, taking this further:

>> cervantes == shakespeare
=> false
>> (shakespeare - cervantes).to_i
=> 10

This shows that in fact they died 10 days apart (in reality 11 days since Cervantes died a day earlier but was buried on the 23rd). We can see the actual date of Shakespeare's death by using the gregorian method to convert it:

>> shakespeare.gregorian
=> Tue, 03 May 1616 00:00:00 +0000

So there's an argument that all the celebrations that take place on the 23rd April in Stratford-upon-Avon are actually the wrong date since England is now using the Gregorian calendar. You can see why when we transition across the reform date boundary:

# start off with the anniversary of Shakespeare's birth in 1751
>> shakespeare = DateTime.iso8601('1751-04-23', Date::ENGLAND)
=> Tue, 23 Apr 1751 00:00:00 +0000

# add 366 days since 1752 is a leap year and April 23 is after February 29
>> shakespeare + 366
=> Thu, 23 Apr 1752 00:00:00 +0000

# add another 365 days to take us to the anniversary in 1753
>> shakespeare + 366 + 365
=> Fri, 04 May 1753 00:00:00 +0000

As you can see, if we're accurately tracking the number of solar years since Shakespeare's birthday then the correct anniversary date would be the 4th May and not the 23rd April.

So when should use you use DateTime in Ruby and when should you use Time? Almost certainly you'll want to use Time since your app is probably dealing with current dates and times and it has support for timezones (system/local and utc), whereas DateTime just has offsets from UTC. However, if you need to deal with dates and times in a historical context you'll want to use DateTime to avoid making the same mistakes as UNESCO. If you also have to deal with timezones then best of luck - just bear in mind that you'll probably be dealing with local solar times, since it wasn't until the 19th century that the introduction of the railways necessitated the need for Standard Time and eventually timezones.

@neerajsingh0101
Copy link

This episode of 99 % invisible goes into the history of calendar if anyone is interested.

@g-pavlik
Copy link

g-pavlik commented Jun 6, 2015

👍 cool story :D thanx

@prestoncopeland
Copy link

This is very interesting. Thanks for taking the time to write it! I recently wrote a post for beginners about the basics of displaying a date with Ruby on my blog http://threemonthcoder.com; it's really neat to read about some advanced subtleties on the same topic!

@urkle
Copy link

urkle commented Jun 10, 2015

In your final example you state Shakespeare's birth, that should be death.

@pixeltrix
Copy link
Author

@urkle Shakespeare was born and died on the same day (probably - he was baptised on the 26th). The 23rd is accepted as the anniversary of his birth and I was illustrating the fact that the celebrations are on the wrong day, whether that's his birth or death 😄

@Rio517
Copy link

Rio517 commented Jun 11, 2015

One should also be mindful of preformance differences that might crop up.

> require 'date'
> require 'benchmark'
> Benchmark.bm do |bm|
>       bm.report('DateTime:') do
>           n1 = DateTime.now
>         n2 = DateTime.now
>         1_000_000.times{ n1 < n2 }
>       end
>     bm.report('Time:    ') do
>           n1 = Time.now
>         n2 = Time.now
>         1_000_000.times{ n1 < n2 }
>       end
>   end

# Using ruby 2.2.0 w/ IRB
       user     system      total        real
DateTime:  0.180000   0.000000   0.180000 (  0.181002)
Time:      0.190000   0.000000   0.190000 (  0.191819)

# Using ruby 2.2.0 w/ rails 4.2 console
       user     system      total        real
DateTime:  0.540000   0.000000   0.540000 (  0.603581)
Time:      2.190000   0.500000   2.690000 (  2.692820)

@callenb
Copy link

callenb commented Jun 12, 2015

Thank you. This has given me more understanding about Time being more appropriate for my Workpattern gem.

I have been writing Workpattern using DateTime. It defines working and non-working time so public holidays, weekends and vacations can be defined as non-working and calculations take that into account.

This is the kind of thing needed to calculate a project network like Microsoft Project or Primavera P6 does. I work in project management in Oil & Gas at the moment - coding is a hobby.

These calculations are about the future and very recent past, so I have been re-writing my gem to use Time. This prompted by a question on rogues parley. I chose DateTime through ignorance, thinking Time just did time!!

Time should also allows the gem to handle daylight saving time, though how i deal with two 1:00 am on the same day will be challenging.

@bobbrez
Copy link

bobbrez commented Aug 14, 2015

Thanks @pixeltrix

@neerajdotname I was just listening to that episode before reading this.

@fatuhoku
Copy link

Great write-up thanks. Really important!

@assoftrefinery
Copy link

assoftrefinery commented Jul 25, 2016

Cervantes was spanish, not italian. And now he is universal :P

@big-meel
Copy link

This is probably the greatest explanation of time ever

@akostadinov
Copy link

@callenb, I think issue is how you assign time. Otherwise time formatting should take care of what you need. e.g. if you print the UTC offset, it will be apparent whether time is before or after switchover.

@callenb
Copy link

callenb commented Oct 30, 2021

@akostadinov I convert all incoming dates into UTC, perform calculations and then convert back out to the Time Zone they were supplied in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment