रूबी / रेल्स - समय के समय को बदलें, बिना मूल्य को बदले


108

मैं एक रिकॉर्ड है fooडेटाबेस जो है में :start_timeऔर :timezoneजिम्मेदार बताते हैं।

:start_timeयूटीसी ए टाइम इन है - 2001-01-01 14:20:00उदाहरण के लिए। :timezone- एक स्ट्रिंग है America/New_Yorkउदाहरण के लिए,।

मैं मूल्य के साथ एक नया टाइम ऑब्जेक्ट बनाना चाहता हूं, :start_timeलेकिन जिसका टाइमज़ोन द्वारा निर्दिष्ट किया गया है :timezone। मैं लोड नहीं करना चाहता हूं :start_timeऔर फिर कन्वर्ट करना चाहता हूं :timezone, क्योंकि रेल चालाक होंगे और यूटीसी से उस समय के अनुरूप होने का समय अपडेट करेंगे।

वर्तमान में,

t = foo.start_time
=> 2000-01-01 14:20:00 UTC
t.zone
=> "UTC"
t.in_time_zone("America/New_York")
=> Sat, 01 Jan 2000 09:20:00 EST -05:00

इसके बजाय, मैं देखना चाहता हूं

=> Sat, 01 Jan 2000 14:20:00 EST -05:00

अर्थात। मैं करना चाहता हूँ:

t
=> 2000-01-01 14:20:00 UTC
t.zone = "America/New_York"
=> "America/New_York"
t
=> 2000-01-01 14:20:00 EST

1
शायद इससे मदद मिलेगी: api.rubyonrails.org/classes/Time.html#method-c-use_zone
MrYoshiji

3
मुझे नहीं लगता कि आप टाइमज़ोन का सही उपयोग कर रहे हैं। यदि आप इसे स्थानीय से UTC के रूप में अपने db में सहेजते हैं, तो इसे अपने स्थानीय समय के माध्यम से पार्स करने और अपने रिश्तेदार utc के माध्यम से सहेजने में क्या गलत है?
ट्रिप

1
हां ... मुझे लगता है कि आपको सर्वश्रेष्ठ सहायता प्राप्त करने की आवश्यकता है जो आपको यह समझाने की आवश्यकता हो सकती है कि आपको ऐसा करने की आवश्यकता क्यों होगी? आप पहली बार डेटाबेस में गलत समय क्यों स्टोर करेंगे?
नजीफनाब

@MrYoshiji सहमत हुए। मेरे लिए YAGNI या समय से पहले अनुकूलन जैसा लगता है।
इंजीनियरडिव

1
अगर उन डॉक्स ने मदद की, तो हमें StackOverflow की आवश्यकता नहीं होगी :-) एक उदाहरण, यह नहीं दिखाता है कि कुछ भी कैसे सेट किया गया था - विशिष्ट। मुझे यह भी करने की आवश्यकता है कि एपल्स-टू-एपल्स की तुलना करने के लिए मजबूर किया जाए, जब डेलाइट सेविंग अंदर या बाहर निकलती है तो वह टूटती नहीं है।
जोसेफ

जवाबों:


72

लगता है कि आप की तर्ज पर कुछ चाहते हैं

ActiveSupport::TimeZone.new('America/New_York').local_to_utc(t)

यह कहता है कि इस स्थानीय समय (क्षेत्र का उपयोग करके) को utc में बदलें। यदि आपने Time.zoneसेट किया है तो आप निश्चित रूप से कर सकते हैं

Time.zone.local_to_utc(t)

यह टी से जुड़ी टाइमज़ोन का उपयोग नहीं करेगा - यह मानता है कि यह उस समय क्षेत्र के लिए स्थानीय है जिसे आप परिवर्तित कर रहे हैं।

यहां सुरक्षा के लिए एक किनारे का मामला डीएसटी संक्रमण है: आपके द्वारा निर्दिष्ट स्थानीय समय मौजूद नहीं हो सकता है या अस्पष्ट हो सकता है।


17
Local_to_utc और Time.use_zone का एक संयोजन जो मुझे चाहिए:Time.use_zone(self.timezone) { Time.zone.local_to_utc(t) }.localtime
rwb

आप डीएसटी बदलाव के खिलाफ क्या सुझाव देते हैं? मान लीजिए कि परिवर्तित करने के लिए लक्ष्य समय डी / एसटी संक्रमण के बाद है जबकि टाइम.वन परिवर्तन से पहले है। क्या इससे काम हो जायेगा ?
सिरिल डचोन-डोरिस

28

मैं बस एक ही समस्या का सामना कर रहा हूँ और यहाँ मैं क्या करने जा रहा हूँ:

t = t.asctime.in_time_zone("America/New_York")

यहाँ asctime पर प्रलेखन है


2
यह सिर्फ वही करता है जो मैंने उम्मीद की थी
कज़

यह बहुत अच्छा है, धन्यवाद! इसके आधार पर मेरे उत्तर को सरल बनाया । इसका एक नकारात्मक पहलू asctimeयह है कि यह किसी भी उप-मूल्य (जो मेरा उत्तर रखता है) को गिरा देता है।
हेनरिक एन

22

यदि आप रेल का उपयोग कर रहे हैं, तो एरिक वाल्श के उत्तर की तर्ज पर एक और तरीका है:

def set_in_timezone(time, zone)
  Time.use_zone(zone) { time.to_datetime.change(offset: Time.zone.now.strftime("%z")) }
end

1
और DateTime ऑब्जेक्ट को TimeWithZone ऑब्जेक्ट में वापस कन्वर्ट करने के लिए, बस .in_time_zoneअंत में डील करें ।
ब्रायन

@ ब्रायन मर्फी-डाई मुझे इस फ़ंक्शन का उपयोग करके डेलाइट सेविंग टाइम के साथ समस्याएं हैं। क्या आप DST के साथ काम करने वाले समाधान प्रदान करने के लिए अपने प्रश्न को संपादित कर सकते हैं? शायद Time.zone.nowउस चीज़ की जगह जो उस समय के सबसे करीब है जिसे आप बदलना चाहते हैं?
सिरिल डचोन-डोरिस

6

इसे परिवर्तित करने के बाद आपको अपने समय के ऑफसेट को जोड़ने की आवश्यकता है।

ऐसा करने का सबसे आसान तरीका है:

t = Foo.start_time.in_time_zone("America/New_York")
t -= t.utc_offset

मुझे यकीन नहीं है कि आप ऐसा क्यों करना चाहेंगे, हालांकि यह वास्तव में सबसे अच्छा है कि वे जिस तरह से निर्मित होते हैं, उस समय के साथ काम करें। मैं कुछ पृष्ठभूमि का अनुमान लगाता हूं कि आपको समय और शिफ्ट करने की आवश्यकता क्यों है।


इसने मेरे लिए काम किया। यह दिन के उजाले-बचत पाली के दौरान सही रहना चाहिए, बशर्ते कि ऑफसेट मूल्य का उपयोग किया जाता है। यदि डेटटाइम ऑब्जेक्ट्स का उपयोग किया जाता है, तो कोई "ऑफसेट.सेकंड" को इससे जोड़ या घटा सकता है।
जोसेफ

यदि आप रेल के बाहर हैं, तो आप Time.in_time_zoneactive_support के सही भागों की आवश्यकता के द्वारा उपयोग कर सकते हैं :require 'active_support/core_ext/time'
jevon

यह गलत काम करने के लिए लगता है कि आप किस दिशा में जाते हैं, और हमेशा विश्वसनीय नहीं लगता है। उदाहरण के लिए, अगर मैं स्टॉकहोम समय लेता हूं और लंदन के समय में परिवर्तित हो जाता है, तो यह काम करता है अगर ऑफसेट को जोड़ (घटाना) नहीं है। लेकिन अगर मैं स्टॉकहोम को हेलसिंकी में परिवर्तित करता हूं, तो यह गलत है कि मैं जोड़ूं या घटाऊं।
हेनरिक एन

5

वास्तव में, मुझे लगता है कि आपको इसे बदलने के बाद ऑफ़सेट को घटाना होगा, जैसे कि:

1.9.3p194 :042 > utc_time = Time.now.utc
=> 2013-05-29 16:37:36 UTC
1.9.3p194 :043 > local_time = utc_time.in_time_zone('America/New_York')
 => Wed, 29 May 2013 12:37:36 EDT -04:00
1.9.3p194 :044 > desired_time = local_time-local_time.utc_offset
 => Wed, 29 May 2013 16:37:36 EDT -04:00 

3

इस पर निर्भर करता है कि आप इस समय का उपयोग कहां करने जा रहे हैं।

जब आपका समय एक विशेषता है

यदि समय को एक विशेषता के रूप में उपयोग किया जाता है, तो आप एक ही date_time_attribute रत्न का उपयोग कर सकते हैं :

class Task
  include DateTimeAttribute
  date_time_attribute :due_at
end

task = Task.new
task.due_at_time_zone = 'Moscow'
task.due_at                      # => Mon, 03 Feb 2013 22:00:00 MSK +04:00
task.due_at_time_zone = 'London'
task.due_at                      # => Mon, 03 Feb 2013 22:00:00 GMT +00:00

जब आप एक अलग चर सेट करते हैं

एक ही date_time_attribute रत्न का उपयोग करें :

my_date_time = DateTimeAttribute::Container.new(Time.zone.now)
my_date_time.date_time           # => 2001-02-03 22:00:00 KRAT +0700
my_date_time.time_zone = 'Moscow'
my_date_time.date_time           # => 2001-02-03 22:00:00 MSK +0400

1
def relative_time_in_time_zone(time, zone)
   DateTime.parse(time.strftime("%d %b %Y %H:%M:%S #{time.in_time_zone(zone).formatted_offset}"))
end

क्विक लिटिल फंक्शन मैं काम को हल करने के लिए आया था। यदि किसी के पास ऐसा करने का अधिक कुशल तरीका है तो कृपया इसे पोस्ट करें!


1
t.change(zone: 'America/New_York')

ओपी का आधार गलत है: "मैं: load_time को लोड नहीं करना चाहता हूं और फिर इसे: टाइमज़ोन में परिवर्तित कर दूंगा, क्योंकि रेल चालाक होगी और UTC से उस समय के अनुरूप होने का समय अपडेट करेगी।" यह आवश्यक रूप से सही नहीं है, जैसा कि यहां दिए गए उत्तर द्वारा प्रदर्शित किया गया है।


1
यह प्रश्न का उत्तर प्रदान नहीं करता है। किसी लेखक से स्पष्टीकरण मांगने या उसका अनुरोध करने के लिए, उनके पोस्ट के नीचे एक टिप्पणी छोड़ दें। - रिव्यू से
एकसेन पी

यह स्पष्ट करने के लिए मेरे उत्तर को अपडेट करें कि यह प्रश्न का वैध उत्तर क्यों है, और प्रश्न त्रुटिपूर्ण धारणा पर आधारित क्यों है।
कुकुरिन

यह कुछ भी करने के लिए प्रतीत नहीं होता है। मेरे परीक्षण यह दिखाते हैं कि यह एक शून्य है।
इत्ते ग्रुदेव

@ ItayGrudev जब आप देखते हैं तो t.zoneइससे पहले t.changeकि आप क्या करते हैं? और क्या है जब आप चलाने के बारे में t.zoneके बाद t.change? और आप t.changeवास्तव में किन मापदंडों से गुजर रहे हैं?
ककूरिन

0

मैंने कुछ सहायक विधियों का निर्माण किया है, जिसमें से एक ही काम करता है जैसा कि रूबी / रेल्स में पोस्ट के मूल लेखक द्वारा पूछा गया है - समय के समय को बदलें, बिना मूल्य को बदले

इसके अलावा मैंने कुछ विशिष्टताओं को भी देखा है, जिन्हें मैंने देखा है और इन सहायकों में स्वचालित रूप से दिन-प्रतिदिन की बचत को लागू करने के तरीके भी शामिल हैं जबकि समय-रूपांतरण जो रेल ढांचे में उपलब्ध नहीं है:

  def utc_offset_of_given_time(time, ignore_dst: false)
    # Correcting the utc_offset below
    utc_offset = time.utc_offset

    if !!ignore_dst && time.dst?
      utc_offset_ignoring_dst = utc_offset - 3600 # 3600 seconds = 1 hour
      utc_offset = utc_offset_ignoring_dst
    end

    utc_offset
  end

  def utc_offset_of_given_time_ignoring_dst(time)
    utc_offset_of_given_time(time, ignore_dst: true)
  end

  def change_offset_in_given_time_to_given_utc_offset(time, utc_offset)
    formatted_utc_offset = ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, false)

    # change method accepts :offset option only on DateTime instances.
    # and also offset option works only when given formatted utc_offset
    # like -0500. If giving it number of seconds like -18000 it is not
    # taken into account. This is not mentioned clearly in the documentation
    # , though.
    # Hence the conversion to DateTime instance first using to_datetime.
    datetime_with_changed_offset = time.to_datetime.change(offset: formatted_utc_offset)

    Time.parse(datetime_with_changed_offset.to_s)
  end

  def ignore_dst_in_given_time(time)
    return time unless time.dst?

    utc_offset = time.utc_offset

    if utc_offset < 0
      dst_ignored_time = time - 1.hour
    elsif utc_offset > 0
      dst_ignored_time = time + 1.hour
    end

    utc_offset_ignoring_dst = utc_offset_of_given_time_ignoring_dst(time)

    dst_ignored_time_with_corrected_offset =
      change_offset_in_given_time_to_given_utc_offset(dst_ignored_time, utc_offset_ignoring_dst)

    # A special case for time in timezones observing DST and which are
    # ahead of UTC. For e.g. Tehran city whose timezone is Iran Standard Time
    # and which observes DST and which is UTC +03:30. But when DST is active
    # it becomes UTC +04:30. Thus when a IRDT (Iran Daylight Saving Time)
    # is given to this method say '05-04-2016 4:00pm' then this will convert
    # it to '05-04-2016 5:00pm' and update its offset to +0330 which is incorrect.
    # The updated UTC offset is correct but the hour should retain as 4.
    if utc_offset > 0
      dst_ignored_time_with_corrected_offset -= 1.hour
    end

    dst_ignored_time_with_corrected_offset
  end

उपरोक्त विधियों को कक्षा या मॉड्यूल में लपेटने के बाद रेल कंसोल या रूबी स्क्रिप्ट पर जिन उदाहरणों को आजमाया जा सकता है:

dd1 = '05-04-2016 4:00pm'
dd2 = '07-11-2016 4:00pm'

utc_zone = ActiveSupport::TimeZone['UTC']
est_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
tehran_zone = ActiveSupport::TimeZone['Tehran']

utc_dd1 = utc_zone.parse(dd1)
est_dd1 = est_zone.parse(dd1)
tehran_dd1 = tehran_zone.parse(dd1)

utc_dd1.dst?
est_dd1.dst?
tehran_dd1.dst?

ignore_dst = true
utc_to_est_time = utc_dd1.in_time_zone(est_zone.name)
if utc_to_est_time.dst? && !!ignore_dst
  utc_to_est_time = ignore_dst_in_given_time(utc_to_est_time)
end

puts utc_to_est_time

उम्मीद है की यह मदद करेगा।


0

यहाँ एक और संस्करण है जो वर्तमान उत्तरों की तुलना में मेरे लिए बेहतर काम करता है:

now = Time.now
# => 2020-04-15 12:07:10 +0200
now.strftime("%F %T.%N").in_time_zone("Europe/London")
# => Wed, 15 Apr 2020 12:07:10 BST +01:00

यह "% N" का उपयोग करके नैनोसेकंड पर ले जाता है। यदि आप एक और परिशुद्धता की इच्छा रखते हैं , तो इस स्ट्रैटनटाइम संदर्भ को देखें


-1

मैंने महत्वपूर्ण समय TimeZones के साथ संघर्ष करने में बिताया, और रूबी के साथ छेड़छाड़ करने के बाद 1.9.3 एहसास हुआ कि आपको नाम बदलने से पहले नामांकित प्रतीक को बदलने की आवश्यकता नहीं है:

my_time = Time.now
west_coast_time = my_time.in_time_zone(-8) # Pacific Standard Time
east_coast_time = my_time.in_time_zone(-5) # Eastern Standard Time

इसका तात्पर्य यह है कि आप अपने इच्छित क्षेत्र में पहले उपयुक्त समय प्राप्त करने पर ध्यान केंद्रित कर सकते हैं, जिस तरह से आप इसके बारे में सोचेंगे (कम से कम मेरे सिर में मैं इसे इस तरह से विभाजित करता हूं), और फिर क्षेत्र के अंत में परिवर्तित करें आप अपने व्यावसायिक तर्क को सत्यापित करना चाहते हैं।

यह रूबी 2.3.1 के लिए भी काम करता है।


1
कभी-कभी पूर्वी ऑफसेट -4 होता है, मुझे लगता है कि वे इसे दोनों मामलों में स्वचालित रूप से संभालना चाहते हैं।
OpenCoderX
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.