DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Data Engineering
  3. Data
  4. Time Is Funny in Ruby

Time Is Funny in Ruby

Have you ever tried to program dates using Ruby? It gives a whole other meaning to the expression 'time is relative.' Found out how to deal with these issues.

Swizec Teller user avatar by
Swizec Teller
·
Mar. 30, 17 · Tutorial
Like (3)
Save
Tweet
Share
4.58K Views

Join the DZone community and get the full member experience.

Join For Free

Ruby is great. Or maybe Rails is what's great… after 4 years, I still don't quite know where the line lies.

One of my favorite features is this:

> "next sunday".to_date
 => Sun, 26 Mar 2017 
```
```ruby
> 2.2.0 :003 > "last sunday".to_date
 => Sun, 26 Mar 2017

Wait a minute… next sunday and last sunday are the same date? 

By the way, it's Tuesday right now. Next Sunday is April 2nd, and last Sunday was March 26th.

Ok, that's weird. But what if we change Date.now to Monday? Let's try that.

2.2.0 :008 > Timecop.freeze("April 3rd, 2017".to_datetime) do
2.2.0 :009 >     Date.today
2.2.0 :010?>   end
 => Sun, 02 Apr 2017

PS: Timecop is a Ruby library meant for testing. It lets you specify the value of "now."

April 2nd is not April 3rd… timezones? Gotta be timezones.

2.2.0 :011 > Timecop.freeze("April 3rd, 2017".to_datetime) do
2.2.0 :012 >     DateTime.now
2.2.0 :013?>   end
 => Sun, 02 Apr 2017 17:00:00 -0700

Ah. to_datetime without a time of day sets the time to midnight in UTC. Then when you call DateTime.now, you get your local timezone. Completely intuitive!

2.2.0 :014 > Timecop.freeze("April 3rd, 2017 1pm".to_datetime) do
2.2.0 :015 >     DateTime.now
2.2.0 :016?>   end
 => Mon, 03 Apr 2017 07:00:00 -0700

Okay, that's better. Does "next Sunday" work now?

2.2.0 :020 > Timecop.freeze("Mar 27, 2017 1pm".to_datetime) do
2.2.0 :021 >     "next sunday".to_datetime
2.2.0 :022?>   end
 => Sun, 26 Mar 2017 00:00:00 +0000

Nope. Doesn't even react to the current time being mocked. That's intuitive.

Turns out, if you want to find next Sunday, you have to use Date.today.sunday. Because… I don't know why.

2.2.0 :023 > Date.today.sunday
 => Sun, 02 Apr 2017 

2.2.0 :024 > Timecop.freeze("April 3rd, 2017 1pm".to_datetime) do
2.2.0 :025 >     Date.today.sunday
2.2.0 :026?>   end
 => Sun, 09 Apr 2017

Excellent! That worked. Today, we get next Sunday. On Monday, we get next-next Sunday. Just what you'd expect!

Now watch this.

2.2.0 :027 > Timecop.freeze("April 3rd, 2017".to_datetime) do
2.2.0 :028 >     Date.today.sunday
2.2.0 :029?>   end
 => Sun, 02 Apr 2017

Timezones? Timezones.

Even though time freezing happens in UTC, time getting happens in your local timezone. So when they misalign, Monday becomes Sunday, and obviously, if today is Sunday and you want to find next Sunday, that's today. D'oh.

Oh, the fun I had figuring that one out! Almost pushed to production without realizing these quirks existed!

But Why? Let's Find Out.

Here is the source for to_datetime

# File activesupport/lib/active_support/core_ext/string/conversions.rb, line 53
  def to_datetime
    ::DateTime.parse(self, false) unless blank?
  end

Great, we know that to_datetime is a Rails method, not a Ruby method. It’s part of the ActiveSupport gem, which looks like a bag of all utility methods you might find useful in a Rails project.

Neat.

The to_datetime method looks like magic. My understanding is that it's part of the string class, which makes it defined on every string. Once called, it passes itself – self – into Datetime.parse.

So what is DateTime?

It looks like a part of core extensions. Its source is split into five files, so I have no idea what's what. Curiously, there is also DateAndTime. They do not seem to do the same things.

And I'm having trouble finding the parse method. It shows up in 107 rails source files and none of them is DateTime. How strange… maybe it's in Ruby after all?

It is!

Deep inside Ruby's C source code, the DateTime class is defined. You'd think Ruby used the bootstrapping compiler approach and it was written in Ruby, but nope. It looks like it's done in C. Who knew?

To the best of my understanding, this is the code that becomes .parse once Ruby is compiled and looks like Ruby.

static VALUE
datetime_s_parse(int argc, VALUE *argv, VALUE klass)
{
    VALUE str, comp, sg;

    rb_scan_args(argc, argv, "03", &str, &comp, &sg);

    switch (argc) {
      case 0:
    str = rb_str_new2("-4712-01-01T00:00:00+00:00");
      case 1:
    comp = Qtrue;
      case 2:
    sg = INT2FIX(DEFAULT_SG);
    }

    {
    VALUE argv2[2], hash;

    argv2[0] = str;
    argv2[1] = comp;
    hash = date_s__parse(2, argv2, klass);
    return dt_new_by_frags(klass, hash, sg);
    }
}

I don't know what all this code does, but it seems to eventually use good old strptime and guesses which format to use. That means it doesn't really care if we say this Sunday or next Sunday. It sees only Sunday.

And when strptime sees Sunday, it returns the current week's Sunday, which in Ruby land is the first day of the week. I don't know why Ruby thinks Sunday is the first day of the week, but it seems like large parts of the world think that way.

shrug

According to the Hebrew calendars and traditional Christian calendars, Sunday is the first day of the week. However, according to the International Organization for Standardization ISO 8601, Sunday is the seventh and last day of the week.

Wikipedia

So?

That was fun. I don't think we learned anything useful. But it's good to know that even though Ruby and Rails can handle fancy relative time strings, they actually ignore the most informative part.

Oops.

IT Strings Calendar (Apple) Data Types Pass (software) Production (computer science)

Published at DZone with permission of Swizec Teller, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • 5 Factors When Selecting a Database
  • OpenID Connect Flows
  • Distributed SQL: An Alternative to Database Sharding
  • Understanding gRPC Concepts, Use Cases, and Best Practices

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: