Skip to content

Instantly share code, notes, and snippets.

@hadrienblanc
Last active June 25, 2020 19:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hadrienblanc/b985f9c591379b194494e6bc394e6b4c to your computer and use it in GitHub Desktop.
Save hadrienblanc/b985f9c591379b194494e6bc394e6b4c to your computer and use it in GitHub Desktop.

TimeZone (and Rails)

UTC : What is that ?

https://en.wikipedia.org/wiki/ISO_8601 ISO 8601 describes representation of dates and times as an international standard

Time zones in ISO 8601 are represented as local time (with the location unspecified), as UTC, or as an offset from UTC.

Z ?

The Z suffix in the ISO 8601 time representation is sometimes referred to as "Zulu time" because the same letter is used to designate the Zulu time zone. "14:45:15Z"

Where is Zulu ? in South Africa.

UTC ? Coordinated Universal Time

Let's be coordinated in this world 🌎 🌍 🌏. UTC is the offest from the Zulu time, the Greenwich Mean Time (GMT).

Give me examples

"−05:00" for New York on standard time (UTC-05:00)
"−04:00" for New York on daylight saving time (UTC-04:00)
"+00:00" (but not "-00:00") for London (UTC±00:00)
"+02:00" for Cairo (UTC+02:00) or paris :yellow_heart:
"+05:30" for Mumbai (UTC+05:30)
"+14:00" for Kiribati (UTC+14:00)

Paris (UTC+02:00)

All the list from wikipedia : List of UTC time offsets https://en.wikipedia.org/wiki/List_of_UTC_time_offsets

Rails and dates

I want a UTC date

By default the date are stored in UTC. So you are in Zulu time. Why ? conveniant for worldwide application. But it can be changed with this kind of configuration :

config.time_zone = 'Eastern Time (US & Canada)'

New york has a "−05:00" UTC offset. So the time is configured as -5 hours.

Rails

Rails Time on a new project

Note bellow the +0 this server is configured as UTC. It's 22h in France. So 22h with 2 hours offset.

irb(main):019:0> Time.now
=> 2020-06-24 20:21:56.2667183 +0000

%T : to display the time %T %z : to display the UTC offset (the difference with the Zulu time.)

irb(main):020:0> Time.now.strftime("%T")
=> "20:23:16"
irb(main):021:0> Time.now
=> 2020-06-24 20:23:18.3238636 +0000
irb(main):022:0> Time.now.strftime("%T %z")
=> "20:23:27 +0000"

Rails Time

When was created the album ?

irb(main):003:0> album.created_at
=> 2020-06-24 11:00:05 -0500

What we should do to display on a different timezone, for exemple in Paris ? --> the same if we had stored the date in UTC.

irb(main):004:0> album.created_at.in_time_zone("Europe/Paris")
=> Wed, 24 Jun 2020 18:00:05 CEST +02:00`

CEST ? Central European Summer Time (CEST) is 2 hours ahead of Coordinated Universal Time (UTC). This time zone is a Daylight Saving Time time zone and is used in: Europe, Antarctica.

This time zone is often called Central European Summer Time.

How you can find trouble with life and time ?

If you do not take the UTC offset into consideration. For exemple, let's be in NY and share the time :

irb(main):007:0> album.created_at.strftime("%T")
=> "11:00:05"
irb(main):008:0> album.created_at
=> 2020-06-24 11:00:05 -0500

On the first line we lost the time offset with our friend Zulu. On the second we know the time coordinated with the world.

What's the answer ?

(1) Use ISO 8601 and add the UTC

(2) If you do not want to have the Zulu time and say on which timezone you want to be

irb(main):003:0> album.created_at
=> 2020-06-24 11:00:05 -0500
irb(main):004:0> album.created_at.in_time_zone("Europe/Paris")
=> Wed, 24 Jun 2020 18:00:05 CEST +02:00

more stuff :

Ruby source code

https://github.com/ruby/ruby/blob/995923b7f9178c234f8c685f434407eb4f0eeb5c/ext/date/date_core.c#L249

struct SimpleDateData
{
    unsigned flags;
    int jd;	/* as utc */
    VALUE nth;	/* not always canonicalized */
    date_sg_t sg;  /* 2298874..2426355 or -/+oo -- at most 22 bits */
    /* decoded as utc=local */
    int year;	/* truncated */
#ifndef USE_PACK
    int mon;
    int mday;
    /* hour is zero */
    /* min is zero */
    /* sec is zero */
#else
    /* packed civil */
    unsigned pc;
#endif
};

struct ComplexDateData
{
    unsigned flags;
    int jd; 	/* as utc */
    VALUE nth;	/* not always canonicalized */
    date_sg_t sg;  /* 2298874..2426355 or -/+oo -- at most 22 bits */
    /* decoded as local */
    int year;	/* truncated */
#ifndef USE_PACK
    int mon;
    int mday;
    int hour;
    int min;
    int sec;
#else
    /* packed civil */
    unsigned pc;
#endif
    int df;	/* as utc, in secs */
    int of;	/* in secs */
    VALUE sf;	/* in nano secs */
};

union DateData {
    unsigned flags;
    struct SimpleDateData s;
    struct ComplexDateData c;
};

Note that USE_PACK is defined line 22. https://github.com/ruby/ruby/blob/995923b7f9178c234f8c685f434407eb4f0eeb5c/ext/date/date_core.c#L22 the integer of is the offset. Used in df_local_to_utc coppied bellow for exemple.

https://github.com/ruby/ruby/blob/995923b7f9178c234f8c685f434407eb4f0eeb5c/ext/date/date_core.c#L882

inline static int
df_local_to_utc(int df, int of)
{
    df -= of;
    if (df < 0)
	df += DAY_IN_SECONDS;
    else if (df >= DAY_IN_SECONDS)
	df -= DAY_IN_SECONDS;
    return df;
}

Commands lines examples

irb(main):010:0> Time.zone
=> #<ActiveSupport::TimeZone:0x00005595e9818948 @name="UTC", @utc_offset=nil, @tzinfo=#<TZInfo::DataTimezone: Etc/UTC>>
irb(main):011:0> Time.zone = 'Europe/Paris'
irb(main):012:0> Time.zone.now
=> Wed, 24 Jun 2020 21:55:07 CEST +02:00
irb(main):013:0> Time.now
=> 2020-06-24 19:55:18.8506586 +0000
irb(main):014:0> Time.zone
=> #<ActiveSupport::TimeZone:0x00005595e97cc8e0 @name="Europe/Paris", @utc_offset=nil, @tzinfo=#<TZInfo::DataTimezone: Europe/Paris>>

thoughtbot : https://thoughtbot.com/blog/its-about-time-zones

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