https://redmine.ruby-lang.org/https://redmine.ruby-lang.org/favicon.ico?17113305112019-01-13T09:53:39ZRuby Issue Tracking SystemRuby master - Feature #15527: Redesign of timezone object requirementshttps://redmine.ruby-lang.org/issues/15527?journal_id=762832019-01-13T09:53:39Znaruse (Yui NARUSE)naruse@airemix.jp
<ul></ul><p>Sounds interesting, but <code>zone.utc_offset(time)</code> can only be a partial alternative of <code>utc_to_local</code> with using `gmtime(3).</p>
<p>Note that a timezone system requires two API.<br>
One is an API which converts from [year, month, day, hour, minute, second] to epoch.<br>
And another is an API which converts from epoch to [year, month, day, hour, minute, second, isdst, zonestr].</p> Ruby master - Feature #15527: Redesign of timezone object requirementshttps://redmine.ruby-lang.org/issues/15527?journal_id=763142019-01-14T18:40:42Zzverok (Victor Shepelev)zverok.offline@gmail.com
<ul></ul><blockquote>
<p>Note that a timezone system requires two API.</p>
</blockquote>
<p>Sorry for my arrogance, but can you please explain this a bit?..<br>
From what I can understand from code, <code>local_to_utc</code> is used only from <code>Time.new</code>, but I am not quite sure why exactly.<br>
From "logical" point of view, we need just to understand what exact UTC offset it should have (so it covered with <code>Zone#utc_offset</code>), the only uncertainty here is <em>what exact moment</em> we want UTC offset for -- this can fire in the foot at DST change moment, but I am not sure how a pair of APIs could help here.</p> Ruby master - Feature #15527: Redesign of timezone object requirementshttps://redmine.ruby-lang.org/issues/15527?journal_id=766282019-02-01T09:41:54Znobu (Nobuyoshi Nakada)nobu@ruby-lang.org
<ul></ul><p><code>zone.utc_offset(time)</code> seems to consider the timezone of the argument, or the UTC offset.<br>
When constructing a time from each components (year, month, day, hour...), we don't know the offset.<br>
That means we have to know the offset to get it by <code>utc_offset</code> method.<br>
It's the key in the locked box, isn't it?</p> Ruby master - Feature #15527: Redesign of timezone object requirementshttps://redmine.ruby-lang.org/issues/15527?journal_id=766332019-02-01T18:06:15Zzverok (Victor Shepelev)zverok.offline@gmail.com
<ul></ul><p>That's interesting problem indeed.<br>
I'll look at it on particular example: my own timezone :)</p>
<p>We have GMT+2 at winter and GMT+3 at summer, transitions for 2018 were Mar 25 03:00 and Oct 28 04:00.</p>
<p>So....</p>
<p>For mid-period, everything is obvious, checking at UTC is enough:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">tz</span> <span class="o">=</span> <span class="no">TZInfo</span><span class="o">::</span><span class="no">Timezone</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="s1">'Europe/Kiev'</span><span class="p">)</span>
<span class="n">tz</span><span class="p">.</span><span class="nf">utc_offset</span><span class="p">(</span><span class="no">Time</span><span class="p">.</span><span class="nf">utc</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span>
<span class="c1"># => 7200 </span>
<span class="n">tz</span><span class="p">.</span><span class="nf">utc_offset</span><span class="p">(</span><span class="no">Time</span><span class="p">.</span><span class="nf">utc</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span>
<span class="c1"># => 10800 </span>
</code></pre>
<p>But how do we decide for the 2018-10-28 4:00 for example? I believe it could be done this way (with the only method <code>#utc_offset(at)</code> required):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">tz</span><span class="p">.</span><span class="nf">utc_offset</span><span class="p">(</span><span class="no">Time</span><span class="p">.</span><span class="nf">utc</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span> <span class="c1"># step 1: take "approximate" offset from UTC time with given value</span>
<span class="c1"># => 7200 </span>
<span class="n">tz</span><span class="p">.</span><span class="nf">utc_offset</span><span class="p">(</span><span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">7200</span><span class="p">))</span> <span class="c1"># step 2: take real offset from time with approximate offset</span>
<span class="c1"># => 10800 </span>
<span class="n">tz</span><span class="p">.</span><span class="nf">utc_offset</span><span class="p">(</span><span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">10800</span><span class="p">))</span> <span class="c1"># step 3: check it: yes, it is right</span>
<span class="c1"># => 10800 </span>
</code></pre>
<p>So, the real answer is: Time at <code>2018-10-28 02:00 Europe/Kiev</code> has UTC offset <code>GMT+3</code>.</p>
<p>Same for transition in another direction:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">tz</span><span class="p">.</span><span class="nf">utc_offset</span><span class="p">(</span><span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span> <span class="c1"># "approximate" offset</span>
<span class="c1"># => 10800 </span>
<span class="n">tz</span><span class="p">.</span><span class="nf">utc_offset</span><span class="p">(</span><span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">10800</span><span class="p">))</span> <span class="c1"># real offset?</span>
<span class="c1"># => 7200 </span>
<span class="n">tz</span><span class="p">.</span><span class="nf">utc_offset</span><span class="p">(</span><span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">7200</span><span class="p">))</span> <span class="c1"># check it: no, it is transition point</span>
<span class="c1"># => 10800 </span>
</code></pre>
<p>The real answer is: it is lost hour (we have 04:00 after 02:59 at transition point), <code>2018-10-25 03:00 Europe/Kiev</code> can't exist, should be an exception.</p>
<p>Yes, 3 calls to <code>utc_offset</code> are kinda indirect, but the current implementation is also "indirect" in a sense it requires timezone library to calculate <code>Time</code> object but doesn't use it.</p>
<p>What is worse is: if modern (2.6-aware) timezone library will try to make <em>proper</em> Time object (using <code>Time.new</code> with its timezone object), there could be infinite recursion (because <code>Time</code> itself and timezone library would call each other). That's because current requirements were designed with exactly one implementation in mind -- which is a third-party library with a legacy interface.</p>
<p>In fact, it is funny paradox that exactly this "legacy" feature (library is able to work with non-offsetted time, considering it as just "tuple of time values"). Maybe more robust API to require would be something like:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">tz</span><span class="p">.</span><span class="nf">utc_offset_by_tuple</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span> <span class="c1"># => consider it as a components of local time, return seconds offset</span>
</code></pre>
<p>PS: In Ruby, currently, creating time on a border of transition is impossible</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Time</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mo">00</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">tz</span><span class="p">)</span>
<span class="c1"># TZInfo::AmbiguousTime (2018-10-28 03:00:00 is an ambiguous local time.)</span>
</code></pre>
<p>TZInfo itself solves it this way:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">tz</span><span class="p">.</span><span class="nf">local_time</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="c1"># TZInfo::AmbiguousTime (2018-10-28 03:00:00 is an ambiguous local time.)</span>
<span class="n">tz</span><span class="p">.</span><span class="nf">local_time</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mo">00</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="kp">true</span><span class="p">)</span> <span class="c1"># last param is dst=true</span>
<span class="c1"># => 2018-10-28 03:00:00 +0300 </span>
<span class="n">tz</span><span class="p">.</span><span class="nf">local_time</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mo">00</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="kp">false</span><span class="p">)</span> <span class="c1"># dst = false</span>
<span class="c1"># => 2018-10-28 03:00:00 +0200 </span>
</code></pre> Ruby master - Feature #15527: Redesign of timezone object requirementshttps://redmine.ruby-lang.org/issues/15527?journal_id=766422019-02-02T11:15:05Znobu (Nobuyoshi Nakada)nobu@ruby-lang.org
<ul></ul><p>zverok (Victor Shepelev) wrote:</p>
<blockquote>
<p>Yes, 3 calls to <code>utc_offset</code> are kinda indirect, but the current implementation is also "indirect" in a sense it requires timezone library to calculate <code>Time</code> object but doesn't use it.</p>
</blockquote>
<p>It is trivial, and is possible to change.<br>
3 calls to <code>utc_offset</code> seems slower than 1 call to <code>local_to_utc</code>.</p>
<blockquote>
<p>What is worse is: if modern (2.6-aware) timezone library will try to make <em>proper</em> Time object (using <code>Time.new</code> with its timezone object), there could be infinite recursion (because <code>Time</code> itself and timezone library would call each other).</p>
</blockquote>
<p>You mean the case a timezone library calls <code>Time.new</code> in <code>utc_to_local</code> method?<br>
<code>Time.new</code> with a timezone object would call <code>local_to_utc</code>, with a UTC time-like object, and the result should be UTC.</p>
<blockquote>
<p>That's because current requirements were designed with exactly one implementation in mind -- which is a third-party library with a legacy interface.</p>
</blockquote>
<p>It was designed with another implementation, timezone gem, too.</p>
<blockquote>
<p>In fact, it is funny paradox that exactly this "legacy" feature (library is able to work with non-offsetted time, considering it as just "tuple of time values"). Maybe more robust API to require would be something like:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">tz</span><span class="p">.</span><span class="nf">utc_offset_by_tuple</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span> <span class="c1"># => consider it as a components of local time, return seconds offset</span>
</code></pre>
</blockquote>
<p>Non-offsetted Time-like object is used now.</p>
<blockquote>
<p>TZInfo itself solves it this way:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">tz</span><span class="p">.</span><span class="nf">local_time</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="c1"># TZInfo::AmbiguousTime (2018-10-28 03:00:00 is an ambiguous local time.)</span>
<span class="n">tz</span><span class="p">.</span><span class="nf">local_time</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mo">00</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="kp">true</span><span class="p">)</span> <span class="c1"># last param is dst=true</span>
<span class="c1"># => 2018-10-28 03:00:00 +0300 </span>
<span class="n">tz</span><span class="p">.</span><span class="nf">local_time</span><span class="p">(</span><span class="mi">2018</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mo">00</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="kp">false</span><span class="p">)</span> <span class="c1"># dst = false</span>
<span class="c1"># => 2018-10-28 03:00:00 +0200 </span>
</code></pre>
</blockquote>
<p>Yes I know, but another implementation in my mind, timezone, doesn't support <code>dst</code> argument.</p> Ruby master - Feature #15527: Redesign of timezone object requirementshttps://redmine.ruby-lang.org/issues/15527?journal_id=1014032023-01-22T15:43:36Zzverok (Victor Shepelev)zverok.offline@gmail.com
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Closed</i></li></ul>