2010-11-05 15 views
5

हमारे पास एक ऐसा ऐप्लिकेशन है जहां समय महत्वपूर्ण है। हम समय परिवर्तन करने और यूटीसी समय में सभी डेटा संग्रहीत करने के लिए जोडा का उपयोग कर रहे हैं। हम थोड़ी देर के लिए उत्पादन में रहे हैं और हमेशा कुछ सही रहा है लेकिन ...जोडा समय समय रूपांतरण 'बहुत जल्द'

अब हम देखते हैं कि समय परिवर्तन से कुछ घंटों पहले होने वाली घटनाएं पहले ही बहुत जल्दी परिवर्तित हो चुकी हैं! वास्तव में, डेटाबेस में सहेजे गए यूटीसी समय एक घंटे से बंद होते हैं।

यहां एक उदाहरण है। मेरा कार्यक्रम 11/6/2010 @ 9 बजे पीडीटी पर होता है और आमतौर पर 11/7/2010 @ 4 बजे के रूप में सहेजा जाएगा। हालांकि, क्योंकि डेलाइट सेविंग्स टाइम 7 वें (संभवतः 2 बजे) पर समाप्त हुआ, इस बार 11/7/2010 @ 5 बजे के रूप में स्थानांतरित हो गया और संग्रहीत किया गया।

हमें डीएसटी परिवर्तन की आवश्यकता नहीं है जब तक यह वास्तव में पीएसटी क्षेत्र में 2am पीएसटी पर नहीं होता है। मुझे लगता है कि जोडा इसे संभालेगा, खासकर जब से जावा की डिफ़ॉल्ट कार्यक्षमता में काफी सुधार हुआ है।

आपके पास कोई भी प्रतिक्रिया उपयोगी होगी, खासकर यदि आप कल समय परिवर्तन से पहले इसे प्राप्त कर सकते हैं! इसके बाद यह अकादमिक होगा, लेकिन फिर भी एक उपयोगी चर्चा होगी।

यहां कुछ कोड है जो हम टाइमज़ोन परिवर्तन करने के लिए उपयोग करते हैं और परिणाम को नियमित जावा डेट ऑब्जेक्ट के रूप में प्राप्त करते हैं।

public Date convertToTimeZone(Date dt, TimeZone from, TimeZone to){ 
    DateTimeZone tzFrom = DateTimeZone.forTimeZone(from); 
    DateTimeZone tzTo = DateTimeZone.forTimeZone(to); 

    Date utc = new Date(tzFrom.convertLocalToUTC(dt.getTime(), false)); 
    Date convertedTime = new Date(tzTo.convertUTCToLocal(utc.getTime())); 
    return convertedTime; 
} 

संपादित करें: नीचे

public Date convert(Date dt, TimeZone from, TimeZone to) { 
    long fromOffset = from.getOffset(dt.getTime()); 
    long toOffset = to.getOffset(dt.getTime()); 

    long convertedTime = dt.getTime() - (fromOffset - toOffset); 
    return new Date(convertedTime); 
} 

पूरा यूनिट टेस्ट टिप्पणी के लिए कोड का नमूना उदाहरण

package com.test.time; 

import java.util.Calendar; 
import java.util.Date; 
import java.util.TimeZone; 

import org.joda.time.DateTime; 
import org.joda.time.DateTimeZone; 
import org.joda.time.Instant; 
import org.junit.Before; 
import org.junit.Test; 

public class TimeTest { 
Calendar nov6; 
Calendar nov1; 
Calendar nov12; 

@Before 
public void doBefore() { 
    // November 1st 2010, 9:00pm (DST is active) 
    nov1 = Calendar.getInstance(); 
    nov1.setTimeZone(TimeZone.getTimeZone("US/Arizona")); 
    nov1.set(Calendar.HOUR_OF_DAY, 21); 
    nov1.set(Calendar.MINUTE, 0); 
    nov1.set(Calendar.SECOND, 0); 
    nov1.set(Calendar.YEAR, 2010); 
    nov1.set(Calendar.MONTH, 10); // November 
    nov1.set(Calendar.DATE, 1); 

    // November 6st 2010, 9:00pm (DST is still active until early AM november 7th) 
    nov6 = Calendar.getInstance(); 
    nov6.setTimeZone(TimeZone.getTimeZone("US/Arizona")); 
    nov6.set(Calendar.HOUR_OF_DAY, 21); 
    nov6.set(Calendar.MINUTE, 0); 
    nov6.set(Calendar.SECOND, 0); 
    nov6.set(Calendar.YEAR, 2010); 
    nov6.set(Calendar.MONTH, 10); // November 
    nov6.set(Calendar.DATE, 6); 

    // November 12th 2010, 9:00pm (DST has ended) 
    nov12 = Calendar.getInstance(); 
    nov12.setTimeZone(TimeZone.getTimeZone("US/Arizona")); 
    nov12.set(Calendar.HOUR_OF_DAY, 21); 
    nov12.set(Calendar.MINUTE, 0); 
    nov12.set(Calendar.SECOND, 0); 
    nov12.set(Calendar.YEAR, 2010); 
    nov12.set(Calendar.MONTH, 10); // November 
    nov12.set(Calendar.DATE, 12); 
} 

@Test 
public void test1() { 
    //  System.out.println("test1"); 
    timeTestJava(nov1.getTime(), "equivalent", "US/Arizona", "US/Pacific"); 
    timeTestJodaOld(nov1.getTime(), "equivalent", "US/Arizona", "US/Pacific"); 
    timeTestJodaNew(nov1.getTime(), "equivalent", "US/Arizona", "US/Pacific"); 

    timeTestJava(nov6.getTime(), "equivalent", "US/Arizona", "US/Pacific"); 
    timeTestJodaOld(nov6.getTime(), "equivalent", "US/Arizona", "US/Pacific"); 
    timeTestJodaNew(nov6.getTime(), "equivalent", "US/Arizona", "US/Pacific"); 

    timeTestJava(nov12.getTime(), "minus1", "US/Arizona", "US/Pacific"); 
    timeTestJodaOld(nov12.getTime(), "minus1", "US/Arizona", "US/Pacific"); 
    timeTestJodaNew(nov12.getTime(), "minus1", "US/Arizona", "US/Pacific"); 
} 

private void timeTestJodaOld(Date startTime, String text, String from, String to) { 
    System.out.println("joda_old: " + startTime); 
    Date output = convertJodaOld(startTime, TimeZone.getTimeZone(from), TimeZone.getTimeZone(to)); 
    System.out.println(text + ": " + output + "\n"); 
} 

private void timeTestJodaNew(Date startTime, String text, String from, String to) { 
    System.out.println("joda_new: " + startTime); 
    Date output = convertJodaNew(startTime, TimeZone.getTimeZone(from), TimeZone.getTimeZone(to)); 
    System.out.println(text + ": " + output + "\n"); 
} 

private void timeTestJava(Date startTime, String text, String from, String to) { 
    System.out.println("java: " + startTime); 
    Date output = convertJava(startTime, TimeZone.getTimeZone(from), TimeZone.getTimeZone(to)); 
    System.out.println(text + ": " + output + "\n"); 
} 

// Initial Joda implementation, works before and after DST change, but not during the period from 2am-7am UTC on the day of the change 
public Date convertJodaOld(Date dt, TimeZone from, TimeZone to) { 
    DateTimeZone tzFrom = DateTimeZone.forTimeZone(from); 
    DateTimeZone tzTo = DateTimeZone.forTimeZone(to); 

    Date utc = new Date(tzFrom.convertLocalToUTC(dt.getTime(), false)); 
    Date convertedTime = new Date(tzTo.convertUTCToLocal(utc.getTime())); 
    return convertedTime; 
} 

// New attempt at joda implementation, doesn't work after DST (winter) 
public Date convertJodaNew(Date dt, TimeZone from, TimeZone to) { 
    Instant utcInstant = new Instant(dt.getTime()); 
    DateTime datetime = new DateTime(utcInstant); 

    datetime.withZone(DateTimeZone.forID(to.getID())); 
    return datetime.toDate(); 
} 

// Java implementation. Works. 
public Date convertJava(Date dt, TimeZone from, TimeZone to) { 
    long fromOffset = from.getOffset(dt.getTime()); 
    long toOffset = to.getOffset(dt.getTime()); 

    long convertedTime = dt.getTime() - (fromOffset - toOffset); 
    return new Date(convertedTime); 
} 

}

+0

एक विधि प्रदान करने के बजाय, क्या आप एक छोटा लेकिन पूरा कार्यक्रम प्रदान कर सकते हैं जो समस्या का प्रदर्शन करता है? –

उत्तर

10

आपका कोड मौलिक टूट गया है, क्योंकि एक Date वस्तु नहीं कर सकते समय क्षेत्रों के बीच "रूपांतरित" हो - यह समय पर एक तत्काल प्रतिनिधित्व करता है। getTime() यूटीसी यूनिक्स युग के बाद मिलीस में लौटाता है। एक Date एक समय क्षेत्र पर निर्भर नहीं है, इसलिए एक समय क्षेत्र से दूसरे समय Date के उदाहरण को परिवर्तित करने का विचार व्यर्थ है। यह "बेस 10" से "आधार 16" तक int को परिवर्तित करने जैसा है - मूलभूत मौलिक संख्या के बजाय अंक में प्रतिनिधित्व के बारे में सोचने पर आधार केवल तभी समझते हैं।

आप LocalDateTime का उपयोग कर किया जाना चाहिए एक निश्चित समय क्षेत्र के बिना तारीख/बार प्रतिनिधित्व करने के लिए, या DateTime एक विशिष्ट समय क्षेत्र, या Instant साथ दिनांक/बार प्रतिनिधित्व करने के लिए java.util.Date के रूप में अवधारणा के एक ही प्रकार का प्रतिनिधित्व करने के लिए।

एक बार जब आप उचित प्रकार का उपयोग करते हैं, तो मुझे यकीन है कि आपको कोई समस्या नहीं होगी।

संपादित करें: अपने वास्तविक कोड सही समय क्षेत्र के साथ एक Calendar उपयोग कर रहा है, तो आप यूटीसी के रूपांतरित होने की कुछ भी सब करने की ज़रूरत नहीं है। बस calendar.getTime() पर कॉल करें और यह आपको उपयुक्त Date देगा।

उदाहरण के लिए:

// Display all Date values as UTC for convenience 
    TimeZone.setDefault(TimeZone.getTimeZone("UTC")); 

    // November 6st 2010, 9:00pm (DST is still active until 
    // early AM november 7th) 
    Calendar nov6 = Calendar.getInstance(); 
    nov6.setTimeZone(TimeZone.getTimeZone("US/Arizona")); 
    nov6.set(Calendar.HOUR_OF_DAY, 21); 
    nov6.set(Calendar.MINUTE, 0); 
    nov6.set(Calendar.SECOND, 0); 
    nov6.set(Calendar.YEAR, 2010); 
    nov6.set(Calendar.MONTH, 10); // November 
    nov6.set(Calendar.DATE, 6); 

    // Prints Sun Nov 07 04:00:00 UTC 2010 which is correct 
    System.out.println("Local Nov6 = " + nov6.getTime()); 

मूल रूप से यह मेरे लिए स्पष्ट नहीं है तुम क्यों अमेरिका/एरिजोना से अमेरिका में बदलने के लिए/प्रशांत जब आप यूटीसी को बचाने की कोशिश के बारे में बात की कोशिश कर रहे ...

+0

हम java.util.Date के साथ फंस गए हैं क्योंकि अब हम डेटा को बनाए रखने के लिए हाइबरनेट का उपयोग कर रहे हैं। मेरी समझ यह है कि जोडा तारीख को ऑफ़सेट लागू कर रहा है, भले ही डेट ऑब्जेक्ट को टाइमज़ोन के बारे में वास्तव में पता न हो। यदि कोड मौलिक रूप से टूटा हुआ है, तो क्या यह साल के केवल 14 घंटे के बजाय हर समय टूट जाएगा? – samspot

+0

@ सैमस्पॉट: ऐसा नहीं है कि दिनांक वस्तु वास्तव में समय क्षेत्र के बारे में अवगत नहीं है - यह है कि तिथि हमेशा एक तत्काल का प्रतिनिधित्व करती है। समय क्षेत्र के बीच की तारीख "कनवर्ट करना" व्यर्थ है। क्यों यह हर समय टूट नहीं जाता है - एक छोटा लेकिन पूरा उदाहरण वास्तव में यहां मदद करेगा। सिर्फ इसलिए कि आपको * कुछ * बिंदु पर 'दिनांक' का उपयोग करना है, इसका मतलब यह नहीं है कि आपको इसका उपयोग * हर जगह * करना है, है ना? –

+0

अरे जॉन, मैंने एक गैर-जोडा नमूना जोड़ा जो महान काम करता है। यही वह है जो मैंने सोचा था कि जोडा मेरे लिए पहली जगह कर रहा था। क्या आप यह कह रहे हैं कि मुझे इसे प्राप्त करने के लिए जोडा के किसी अन्य पहलू का उपयोग करने की ज़रूरत है? – samspot

संबंधित मुद्दे