রুবি / রেলস - মান পরিবর্তন না করে কোনও সময়ের সময় অঞ্চল পরিবর্তন করুন


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/class/Time.html#method-c-use_zone
মিঃ যোশিজি

3
আমি মনে করি না আপনি টাইমজোনগুলি সঠিকভাবে ব্যবহার করছেন। আপনি যদি এটি স্থানীয় থেকে আপনার ডিবিতে ইউটিসি হিসাবে সংরক্ষণ করেন তবে এর স্থানীয় সময়ের মাধ্যমে এটি পার্স করে এবং এটি সম্পর্কিত আপটিকের মাধ্যমে সংরক্ষণ করে কোন সমস্যা?
ট্রিপ

1
হ্যাঁ ... আমি আপনাকে কেন এটি করতে হবে তা বোঝাতে আপনার প্রয়োজন হতে পারে সেরা সহায়তা পেতে বলে মনে করি ? আপনি কেন প্রথম স্থানটিতে ডাটাবেজে ভুল সময় সঞ্চয় করবেন?
এনজিফনাব

@ মিঃ জ্যোশিজি রাজি হয়েছেন। আমার কাছে YAGNI বা অকাল অপ্টিমাইজেশনের মতো শোনাচ্ছে।
ইঞ্জিনিয়ার

1
যদি এই দস্তাবেজগুলি সহায়তা করে, আমাদের স্ট্যাকওভারফ্লো প্রয়োজন হবে না :-) সেখানে একটি উদাহরণ, এটি কীভাবে কীভাবে সেট করা হয়েছিল তা দেখায় না - সাধারণ। অ্যাপল-টু-আপেল তুলনা জোর করার জন্য আমারও এটি করা দরকার যা ডাইটলাইট সেভিংসে বা বাইরে বেরোনোর ​​সময় ভেঙে যায় না।
জোসেফকে

উত্তর:


72

আপনি যেমন লাইনের পাশাপাশি কিছু চান মনে হচ্ছে

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

এটি বলছে এই স্থানীয় সময়কে (জোন ব্যবহার করে) ইউটিসি তে রূপান্তর করুন। আপনি যদি Time.zoneসেট করে থাকেন তবে অবশ্যই করতে পারেন

Time.zone.local_to_utc(t)

এটি টি-এর সাথে সংযুক্ত টাইমজোনটি ব্যবহার করবে না - এটি ধরে নেওয়া হয় যে আপনি যে সময় অঞ্চল থেকে রূপান্তর করছেন তা এটি স্থানীয়।

এখানে রক্ষা করার জন্য একটি প্রান্তের কেসটি হ'ল ডিএসটি ট্রানজিশন: আপনি যে স্থানীয় সময় নির্দিষ্ট করেছেন তা অস্তিত্ব থাকতে পারে বা দ্বিধাগ্রস্ত হতে পারে।


17
লোকাল_ট_ টট এবং টাইম.উস_জোন এর সংমিশ্রণটি আমার যা প্রয়োজন:Time.use_zone(self.timezone) { Time.zone.local_to_utc(t) }.localtime
rwb

আপনি ডিএসটি স্থানান্তরের বিরুদ্ধে কী পরামর্শ দিচ্ছেন? ধরুন রূপান্তর করার লক্ষ্য সময়টি ডি / এসটি রূপান্তরের পরে যখন সময়.নু পরিবর্তনের আগে। কাজ করবে?
সিরিল ডুচন-ডরিস

28

আমি ঠিক একই সমস্যার মুখোমুখি হয়েছি এবং আমি এখানে যা করতে চলেছি তা এখানে:

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

এখানে আস্কটাইমের ডকুমেন্টেশন রয়েছে


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
এবং ডেটটাইম অবজেক্টটিকে একটি টাইম উইথজোন অবজেক্টে ফিরে রূপান্তর .in_time_zoneকরতে, শেষের দিকে তাকান।
ব্রায়ান

@ ব্রায়ান মারফি-ডাই এই ফাংশনটি ব্যবহার করে দিবালোক সংরক্ষণের সময় নিয়ে আমার সমস্যা হয়েছিল। আপনি কী ডিএসটি-র সাথে কাজ করে এমন কোনও সমাধান দেওয়ার জন্য আপনার প্রশ্নটি সম্পাদনা করতে পারেন? সম্ভবত Time.zone.nowআপনি যে পরিবর্তনটি পরিবর্তন করতে চান তার সবচেয়ে কাছাকাছি কিছু প্রতিস্থাপন কাজ করবে?
সিরিল ডুচন-ডরিস

6

আপনি রূপান্তর করার পরে আপনার সময় অফসেট সময় যোগ করা প্রয়োজন।

এটি করার সবচেয়ে সহজ উপায় হ'ল:

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

আপনি কেন এটি করতে চাইবেন তা আমি নিশ্চিত নই, যদিও বাস্তবে তারা যেভাবে নির্মিত সেভাবে কাজ করা ভাল। আপনার সময় এবং সময় অঞ্চলগুলি কেন সহায়ক হবে তা সম্পর্কে কিছুটা ব্যাকগ্রাউন্ড অনুমান করি।


এটি আমার পক্ষে কাজ করেছে। দিবালোক-সঞ্চয় শিফ্টের সময় এটি সঠিক থাকা উচিত, যখন অফসেট মান ব্যবহার করা হয় তখন সেট করা থাকে। ডেটটাইম অবজেক্ট ব্যবহার করা হলে, কেউ এ থেকে "অফসেট.সেকেন্ডস" যুক্ত বা বিয়োগ করতে পারেন।
জোসেফকে

আপনি যদি Time.in_time_zoneকারাগারের বাইরে থাকেন তবে অ্যাক্টিভ_সপোর্টের সঠিক অংশগুলি ব্যবহার করে আপনি ব্যবহার করতে পারেন :require 'active_support/core_ext/time'
জেভন

আপনি কোন দিকে যাচ্ছেন তার উপর নির্ভর করে এটি ভুল কাজ করছে বলে মনে হয় এবং সর্বদা নির্ভরযোগ্য বলে মনে হয় না। উদাহরণস্বরূপ, যদি আমি এখন স্টকহোম সময় নিই এবং লন্ডনের সময়ে রূপান্তর করি তবে অফসেট যুক্ত (বিয়োগ) না করা হলে এটি কাজ করে। তবে আমি যদি স্টকহোমকে হেলসিঙ্কিতে রূপান্তর করি তবে আমি যুক্ত করব বা বিয়োগ করব তা ভুল।
হেনরিক এন

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

আপনি এই সময়টি কোথায় ব্যবহার করছেন তা নির্ভর করে।

যখন আপনার সময় একটি বৈশিষ্ট্য হয়

সময়টি যদি কোনও অ্যাট্রিবিউট হিসাবে ব্যবহৃত হয় তবে আপনি একই তারিখ_টাইম_সত্তর রত্ন ব্যবহার করতে পারেন :

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

আপনি যখন আলাদা ভেরিয়েবল সেট করবেন set

একই তারিখের_টাম_অ্যাট্রিবিউট রত্ন ব্যবহার করুন :

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')

ওপিটির ভিত্তিটি ভুল: "আমি এই: স্টার্টটাইমটি লোড করতে চাই না এবং তারপরে: টাইমজোনকে রূপান্তর করতে চাই না, কারণ রেলগুলি চতুর হবে এবং ইউটিসি থেকে সময়টিকে সেই সময়সীমার সাথে সামঞ্জস্য রাখতে আপডেট করবে” " এটি সরবরাহ করা উত্তর দ্বারা প্রদর্শিত হিসাবে এটি অগত্যা সত্য নয়।


1
এটি প্রশ্নের উত্তর দেয় না। কোনও লেখকের কাছ থেকে সমালোচনা বা স্পষ্টতার জন্য অনুরোধ জানাতে, তাদের পোস্টের নীচে একটি মন্তব্য দিন। - পর্যালোচনা থেকে
আকসেন পি

এটি কেন এই প্রশ্নের বৈধ উত্তর এবং তা কেন ত্রুটিযুক্ত অনুমানের ভিত্তিতে করা হয়েছে তা পরিষ্কার করতে আমার উত্তর আপডেট করেছিলাম।
ক্কুরিয়ান

এটি কিছুই করতে পারে বলে মনে হচ্ছে না। আমার পরীক্ষাগুলি এটিকে নূপুর হিসাবে দেখায়।
Itay গ্রুদেব

@ আইটিগ্রুদেব আপনি যখন 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

আমি টাইমজোনগুলির সাথে লড়াই করেও উল্লেখযোগ্য সময় ব্যয় করেছি এবং রুবি ১.৯.৩ এর সাথে কথা বলার পরে বুঝতে পেরেছিলাম যে রূপান্তর করার আগে আপনাকে কোনও নামকৃত সময় অঞ্চল প্রতীক রূপান্তর করতে হবে না:

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

এর দ্বারা বোঝা যায় যে আপনি যে অঞ্চলে চান তার উপযুক্ত সময় সেটআপ পাওয়ার বিষয়ে আপনি মনোনিবেশ করতে পারেন, আপনি যেভাবে এটি সম্পর্কে ভাববেন (অন্তত আমার মাথায় আমি এটি এভাবে বিভাজন করব) এবং তারপরে জোনে রূপান্তর করতে পারি আপনি আপনার ব্যবসায়িক যুক্তি দিয়ে যাচাই করতে চান।

এটি রুবি ২.৩.১ এর জন্যও কাজ করে।


1
কখনও কখনও পূর্ব অফসেট -4 হয়, আমার ধারণা তারা উভয় ক্ষেত্রে এটি স্বয়ংক্রিয়ভাবে পরিচালনা করতে চায়।
ওপেন কোডারএক্স
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.