2008-10-23 7 views
87

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

चूंकि इस दिनांक का उपयोग किसी वेब सेवा के लिए किया जाता है, जो मानता है कि दिया गया समय हमेशा जीएमटी में होता है, मुझे उपयोगकर्ता के पैरामीटर को (ईएसटी) से (जीएमटी) में बदलने की आवश्यकता होती है। यहां किकर है: उपयोगकर्ता अपने टीजेड के लिए अनजान है। उन्होंने कहा कि वह WS के लिए भेजना चाहती निर्माण तिथि में प्रवेश करती है, तो मैं क्या जरूरत है:

उपयोगकर्ता में प्रवेश करती है: 2008/05/01 6:12 बजे (ईएसटी)
पैरामीटर WS आवश्यकताओं के: 5/1/2008 6:12 अपराह्न (जीएमटी)

मुझे पता है कि टाइमस्टैम्प हमेशा डिफ़ॉल्ट रूप से जीएमटी में होना चाहिए, लेकिन पैरामीटर भेजते समय भी, मैंने टीएस से अपना कैलेंडर बनाया है (जो कि जीएमटी में माना जाता है), जब तक कि उपयोगकर्ता जीएमटी में नहीं होता तब तक घंटे हमेशा बंद होते हैं। मैं क्या खो रहा हूँ?

Timestamp issuedDate = (Timestamp) getACPValue(inputs_, "issuedDate"); 
Calendar issueDate = convertTimestampToJavaCalendar(issuedDate); 
... 
private static java.util.Calendar convertTimestampToJavaCalendar(Timestamp ts_) { 
    java.util.Calendar cal = java.util.Calendar.getInstance(
     GMT_TIMEZONE, EN_US_LOCALE); 
    cal.setTimeInMillis(ts_.getTime()); 
    return cal; 
} 
पिछले कोड के साथ

, यह क्या मैं एक परिणाम के रूप मिलता है (आसानी से पढ़ने के लिए लघु स्वरूप):

[1 मई, 2008 11:12]

+2

आप केवल टाइमज़ोन क्यों बदल रहे हैं और इसके साथ दिनांक/समय को परिवर्तित नहीं कर रहे हैं? –

+1

यह एक वास्तविक समय है जो जावा टाइम लेने के लिए एक टाइमज़ोन में है, और * उस * दिनांक को किसी अन्य टाइमज़ोन में प्राप्त करें। आईई, 5 पीएम ईडीटी लें और 5 पीएम पीडीटी प्राप्त करें। – mtyson

उत्तर

28

उत्तर देने के लिए सभी को धन्यवाद। एक और जांच के बाद मुझे सही जवाब मिल गया। जैसा कि Skip Head द्वारा उल्लिखित किया गया है, मेरे आवेदन से प्राप्त टाइमस्टैम्ड I को उपयोगकर्ता के टाइमज़ोन में समायोजित किया जा रहा था। तो यदि उपयोगकर्ता 6:12 अपराह्न (ईएसटी) में प्रवेश करता है तो मुझे 2:12 अपराह्न (जीएमटी) मिल जाएगा। मुझे जो चाहिए वह रूपांतरण पूर्ववत करने का एक तरीका था ताकि उपयोगकर्ता द्वारा दर्ज किया गया समय वह समय है जब मैंने वेबसेवर अनुरोध पर भेजा था। यहाँ कैसे मैं इस पूरा किया है:

// Get TimeZone of user 
TimeZone currentTimeZone = sc_.getTimeZone(); 
Calendar currentDt = new GregorianCalendar(currentTimeZone, EN_US_LOCALE); 
// Get the Offset from GMT taking DST into account 
int gmtOffset = currentTimeZone.getOffset(
    currentDt.get(Calendar.ERA), 
    currentDt.get(Calendar.YEAR), 
    currentDt.get(Calendar.MONTH), 
    currentDt.get(Calendar.DAY_OF_MONTH), 
    currentDt.get(Calendar.DAY_OF_WEEK), 
    currentDt.get(Calendar.MILLISECOND)); 
// convert to hours 
gmtOffset = gmtOffset/(60*60*1000); 
System.out.println("Current User's TimeZone: " + currentTimeZone.getID()); 
System.out.println("Current Offset from GMT (in hrs):" + gmtOffset); 
// Get TS from User Input 
Timestamp issuedDate = (Timestamp) getACPValue(inputs_, "issuedDate"); 
System.out.println("TS from ACP: " + issuedDate); 
// Set TS into Calendar 
Calendar issueDate = convertTimestampToJavaCalendar(issuedDate); 
// Adjust for GMT (note the offset negation) 
issueDate.add(Calendar.HOUR_OF_DAY, -gmtOffset); 
System.out.println("Calendar Date converted from TS using GMT and US_EN Locale: " 
    + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT) 
    .format(issueDate.getTime())); 

कोड का उत्पादन होता है: (उपयोगकर्ता द्वारा दर्ज 2008/05/01 6:12 बजे (ईएसटी)

वर्तमान उपयोगकर्ता की समयक्षेत्र: ईएसटी
वर्तमान (जीएमटी से ऑफसेट घंटे में): - 4 (सामान्य रूप से -5, छोड़कर डीएसटी समायोजित किया गया है)
एसीपी से टीएस: 2008-05-01 14: 12: 00.0
जीएमटी और यूएस_एन लोकेल का उपयोग कर टीएस से परिवर्तित कैलेंडर तिथि: 5/1/08 6: 12 अपराह्न (जीएमटी)

4

कुछ ऐसा काम किया है मेरे लिए अतीत में उपयोगकर्ता के टाइमज़ोन और जीएमटी के बीच ऑफसेट (मिलीसेकंड में) निर्धारित करना था। एक बार ऑफसेट होने के बाद, आप टाइमज़ोन में उचित समय प्राप्त करने के लिए बस जोड़/घटा सकते हैं (रूपांतरण किस तरह से चल रहा है)। मैं आमतौर पर कैलेंडर ऑब्जेक्ट के मिलीसेकंड फ़ील्ड को सेट करके इसे पूरा करता हूं, लेकिन मुझे यकीन है कि आप इसे टाइमस्टैम्प ऑब्जेक्ट पर आसानी से लागू कर सकते हैं। यहाँ कोड मैं ऑफसेट

int offset = TimeZone.getTimeZone(timezoneId).getRawOffset(); 

timezoneId प्राप्त करने के लिए प्रयोग करें (जैसे ईएसटी) के रूप में उपयोगकर्ता के समय क्षेत्र की आईडी है।

+8

कच्चे ऑफसेट का उपयोग करके आप डीएसटी को अनदेखा कर रहे हैं। –

+1

बिल्कुल, यह विधि आधे साल के लिए गलत परिणाम देगी। त्रुटि का सबसे खराब रूप। –

7

ऐसा लगता है कि आपका टाइमस्टैम्प मूल प्रणाली के टाइमज़ोन पर सेट किया जा रहा है।

यह अब मान्य नहीं है, लेकिन यह काम करना चाहिए:

cal.setTimeInMillis(ts_.getTime() - ts_.getTimezoneOffset()); 

गैर पदावनत रास्ता

Calendar.get(Calendar.ZONE_OFFSET) + Calendar.get(Calendar.DST_OFFSET))/(60 * 1000) 

उपयोग करने के लिए है, लेकिन है कि ग्राहक पक्ष पर किया जाना है, कि इस प्रणाली के बाद से की आवश्यकता होगी जानता है कि क्या समय-क्षेत्र में है।

58
public static Calendar convertToGmt(Calendar cal) { 

    Date date = cal.getTime(); 
    TimeZone tz = cal.getTimeZone(); 

    log.debug("input calendar has date [" + date + "]"); 

    //Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT 
    long msFromEpochGmt = date.getTime(); 

    //gives you the current offset in ms from GMT at the current date 
    int offsetFromUTC = tz.getOffset(msFromEpochGmt); 
    log.debug("offset is " + offsetFromUTC); 

    //create a new calendar in GMT timezone, set to this date and add the offset 
    Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); 
    gmtCal.setTime(date); 
    gmtCal.add(Calendar.MILLISECOND, offsetFromUTC); 

    log.debug("Created GMT cal with date [" + gmtCal.getTime() + "]"); 

    return gmtCal; 
} 

यहाँ उत्पादन अगर मैं ("12:09:05 EDT"से वर्तमान समय पारित) में:

डीबग - इनपुट कैलेंडर तिथि है [गुरु अक्टू 23 12:09:05 EDT 2008]
डीबग - ऑफसेट है -१४४०००००
डीबग - निर्मित की तारीख के साथ जीएमटी कैलोरी [गुरु अक्टू 23 08 : 09: 05 ईडीटी 2008]

12:09:05 GMT 8:09:05 EDT है।

यहां भ्रमित करने वाला हिस्सा यह है कि Calendar.getTime() आपको अपने वर्तमान टाइमज़ोन में Date देता है, और यह भी कि कैलेंडर के टाइमज़ोन को संशोधित करने के लिए कोई तरीका नहीं है और अंतर्निहित तारीख भी लुढ़क गई है।आपकी वेब सेवा किस प्रकार के पैरामीटर पर निर्भर करता है, इस पर निर्भर करता है कि आप युग से मिलीसेकंड के मामले में डब्लूएस डील करना चाहते हैं।

+10

क्या आप इसे जोड़ने के बजाय 'ऑफसेटफ्रूट्यूसी' घटा नहीं सकते हैं? अपने उदाहरण का प्रयोग करते हुए, यदि 12:09 जीएमटी 8:09 ईडीटी (जो सत्य है) है, और उपयोगकर्ता "12:09 ईडीटी" में प्रवेश करता है, तो एल्गोरिदम को मेरी राय में "16:09 जीएमटी" आउटपुट करना चाहिए। – DzinX

19

आप कहते हैं कि तारीख का उपयोग कनेक्शन वाई में किया जाता है वें वेब सेवाएं, इसलिए मुझे लगता है कि किसी बिंदु पर एक स्ट्रिंग में क्रमबद्ध है।

यदि ऐसा है, तो आपको डेटफॉर्मैट क्लास के setTimeZone method पर एक नज़र डालना चाहिए। यह निर्धारित करता है कि समय क्षेत्र मुद्रित करते समय किस समय क्षेत्र का उपयोग किया जाएगा।

एक साधारण उदाहरण: एक समय-क्षेत्र से दूसरे में परिवर्तित करने (शायद यह काम करता है :)) के लिए

SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); 
formatter.setTimeZone(TimeZone.getTimeZone("UTC")); 

Calendar cal = Calendar.getInstance(); 
String timestamp = formatter.format(cal.getTime()); 
+4

एसडीएफ से राहत नहीं मिली थी, यह समय क्षेत्र था, यह सोचकर कि कैलेंडर टाइमज़ोन को बदलने का कोई प्रभाव नहीं पड़ता है! –

+0

TimeZone.getTimeZone ("UTC") मान्य नहीं है क्योंकि यूटीसी उपलब्ध आईडी() में नहीं है ... भगवान जानता है कि –

+0

टाइमज़ोन.getटाइमज़ोन ("यूटीसी") मेरी मशीन पर उपलब्ध है। क्या यह जेवीएम निर्भर है? – CodeClimber

7

विधि।

/** 
* Adapt calendar to client time zone. 
* @param calendar - adapting calendar 
* @param timeZone - client time zone 
* @return adapt calendar to client time zone 
*/ 
public static Calendar convertCalendar(final Calendar calendar, final TimeZone timeZone) { 
    Calendar ret = new GregorianCalendar(timeZone); 
    ret.setTimeInMillis(calendar.getTimeInMillis() + 
      timeZone.getOffset(calendar.getTimeInMillis()) - 
      TimeZone.getDefault().getOffset(calendar.getTimeInMillis())); 
    ret.getTime(); 
    return ret; 
} 
12

आप Joda Time साथ इसे हल कर सकते हैं:

Date utcDate = new Date(timezoneFrom.convertLocalToUTC(date.getTime(), false)); 
Date localDate = new Date(timezoneTo.convertUTCToLocal(utcDate.getTime())); 

जावा 8:

LocalDateTime localDateTime = LocalDateTime.parse("2007-12-03T10:15:30"); 
ZonedDateTime fromDateTime = localDateTime.atZone(
    ZoneId.of("America/Toronto")); 
ZonedDateTime toDateTime = fromDateTime.withZoneSameInstant(
    ZoneId.of("Canada/Newfoundland")); 
+0

के लिए "जीएमटी -4: 00" इस पर ध्यान दें, हालांकि यदि आप हाइबरनेट 4 का उपयोग कर रहे हैं, जो साइड निर्भरता और अतिरिक्त कॉन्फ़िगरेशन के बिना सीधे संगत नहीं है। हालांकि यह तीसरे संस्करण के लिए दृष्टिकोण का उपयोग करने का सबसे तेज़ और आसान था। – Aubergine

6

दिनांक और टाइमस्टैम्प वस्तुओं समयक्षेत्र-अनजान हैं: वे की एक निश्चित संख्या का प्रतिनिधित्व करते हैं युग के बाद से सेकंड, उस इंस्टा की एक विशेष व्याख्या के बिना घंटे और दिन के रूप में एनटी। समय क्षेत्र है जो एक समय क्षेत्र अलग क्षेत्रों और तारीख (या लंबे) मूल्यों के बीच परिवर्तित करने के लिए ऑफसेट की जरूरत है चित्र केवल ग्रेगोरी कैलेंडर में (सीधे इस कार्य के लिए आवश्यक नहीं है) और SimpleDateFormat, दर्ज करें।

ओपी की समस्या उसकी प्रसंस्करण की शुरुआत में सही है: उपयोगकर्ता घंटों को इनपुट करता है, जो अस्पष्ट हैं, और इन्हें स्थानीय, गैर-जीएमटी टाइमज़ोन में व्याख्या किया जाता है; इस बिंदु पर मूल्य "6:12 ईएसटी", जो आसानी से के रूप में "11.12 जीएमटी" या किसी अन्य समय क्षेत्र मुद्रित किया जा सकता, लेकिन कभी परिवर्तनकरने के लिए "6.12 जीएमटी" जा रहा है है।

SimpleDateFormat कि पार्स "06:12" बनाने के लिए कोई तरीका नहीं है रूप "HH: MM" बजाय यूटीसी के लिए डिफ़ॉल्ट (स्थानीय समय क्षेत्र के लिए दोषी); SimpleDateFormat अपने स्वयं के अच्छे के लिए थोड़ा सा स्मार्ट है।

हालांकि, अगर आप किसी भी SimpleDateFormat सही समय क्षेत्र का उपयोग करने के लिए अगर आप इनपुट में स्पष्ट रूप से रख दिया उदाहरण समझाने कर सकते हैं: अभी-अभी प्राप्त करने के लिए एक निश्चित स्ट्रिंग संलग्न (और पर्याप्त रूप से मान्य) "06:12" को पार्स "06:12 GMT""एचएच: एमएम जेड"

ग्रेगोरियन कैलेंडर फ़ील्ड या टाइमज़ोन और डेलाइट सेविंग टाइम ऑफ़सेट का उपयोग करने और उपयोग करने की स्पष्ट सेटिंग की आवश्यकता नहीं है।

असली समस्या स्थानीय टाइमज़ोन के लिए डिफ़ॉल्ट इनपुट को अलग कर रही है, इनपुट जो यूटीसी के लिए डिफ़ॉल्ट है, और इनपुट जो वास्तव में एक स्पष्ट टाइमज़ोन संकेत की आवश्यकता है।

1

java.time

आधुनिक दृष्टिकोण java.time वर्गों है कि जावा के जल्द से जल्द संस्करणों के साथ बंडल परेशानी विरासत तिथि-समय वर्गों का स्थान ले लिया उपयोग करता है।

java.sql.Timestamp कक्षा उन विरासत कक्षाओं में से एक है। अब जरूरत नहीं। इसके बजाय जेडीबीसी 4.2 और बाद में अपने डेटाबेस के साथ सीधे Instant या अन्य जावा.टाइम कक्षाओं का उपयोग करें।

Instant कक्षा UTC में nanoseconds (दशमलव अंश के नौ (9) अंकों के संकल्प के साथ समयरेखा पर एक पल का प्रतिनिधित्व करती है)।

Instant instant = myResultSet.getObject(… , Instant.class) ; 

यदि आप किसी मौजूदा Timestamp साथ interoperate करना आवश्यक है, तुरंत java.time में तब्दील वर्ष कक्षाओं में जोड़े गए नए रूपांतरण विधियों के माध्यम से।

Instant instant = myTimestamp.toInstant() ; 

किसी अन्य समय क्षेत्र में समायोजित करने के लिए, एक ZoneId वस्तु के रूप में समय क्षेत्र निर्दिष्ट करें। continent/region के प्रारूप में निर्दिष्ट करें, जैसे America/Montreal, Africa/Casablanca, या Pacific/Auckland। 0-या IST जैसे 3-4 अक्षर छद्म-जोन का उपयोग न करें क्योंकि सही समय क्षेत्र, मानकीकृत नहीं, और यहां तक ​​कि अनूठा (!) भी नहीं है।

ZoneId z = ZoneId.of("America/Montreal") ; 

Instant के लिए आवेदन करें एक ZonedDateTime वस्तु के उत्पादन के लिए।

ZonedDateTime zdt = instant.atZone(z) ; 

उपयोगकर्ता के लिए प्रदर्शन के लिए एक स्ट्रिंग उत्पन्न करने के लिए कई चर्चाओं और उदाहरण खोजने के लिए DateTimeFormatter के लिए खोज स्टैक ओवरफ़्लो।

आपका प्रश्न वास्तव में दूसरी दिशा में जाने के बारे में है, उपयोगकर्ता डेटा प्रविष्टि से डेट-टाइम ऑब्जेक्ट्स तक। आमतौर पर अपने डेटा-एंट्री को दो हिस्सों, एक तिथि और समय-समय पर तोड़ने के लिए सबसे अच्छा है।

LocalDate ld = LocalDate.parse(dateInput , DateTimeFormatter.ofPattern("M/d/uuuu" , Locale.US)) ; 
LocalTime lt = LocalTime.parse(timeInput , DateTimeFormatter.ofPattern("H:m a" , Locale.US)) ; 

आपका प्रश्न स्पष्ट नहीं है। क्या आप उपयोगकर्ता द्वारा यूटीसी में होने वाली तारीख और समय की व्याख्या करना चाहते हैं? या किसी अन्य समय क्षेत्र में?

यदि आप यूटीसी का मतलब रखते हैं, तो यूटीसी, ZoneOffset.UTC के लिए निरंतर उपयोग करके ऑफ़सेट के साथ OffsetDateTime बनाएं।

OffsetDateTime odt = OffsetDateTime.of(ld , lt , ZoneOffset.UTC) ; 

यदि आप किसी अन्य समय क्षेत्र का मतलब है, एक समय क्षेत्र वस्तु के साथ गठबंधन, एक ZoneId। लेकिन कौन सा समय क्षेत्र? आप एक डिफ़ॉल्ट समय क्षेत्र का पता लगा सकते हैं। या, यदि महत्वपूर्ण हो, तो आपको उपयोगकर्ता के इरादे से निश्चित होने के लिए पुष्टि करनी होगी।

ZonedDateTime zdt = ZonedDateTime.of(ld , lt , z) ; 

एक सरल ऑब्जेक्ट परिभाषा से यूटीसी में हमेशा होता है कि प्राप्त करने के लिए, एक Instant निकालें।

Instant instant = odt.toInstant() ; 

... या ...

Instant instant = zdt.toInstant() ; 

अपने डेटाबेस को भेजें।

myPreparedStatement.setObject(… , instant) ; 

java.time

बारे java.time ढांचे जावा 8 और बाद में बनाया गया है। ये कक्षाएं परेशान पुराने legacy दिनांक-समय कक्षाएं जैसे java.util.Date, Calendar, & SimpleDateFormat प्रदान करती हैं।

Joda-Time प्रोजेक्ट, अब maintenance mode में, java.time कक्षाओं में माइग्रेशन की सलाह देता है।

और जानने के लिए, Oracle Tutorial देखें। और कई उदाहरणों और स्पष्टीकरणों के लिए स्टैक ओवरफ़्लो खोजें। विशिष्टता JSR 310 है।

जावा.टाइम कक्षाएं कहां प्राप्त करें?

  • Java SE 8, Java SE 9, और बाद में
    • में निर्मित।
    • एक बंडल कार्यान्वयन के साथ मानक जावा एपीआई का हिस्सा।
    • जावा 9 कुछ मामूली विशेषताओं और सुधारों को जोड़ता है।
  • Java SE 6 और Java SE 7
    • java.time कार्यक्षमता की ज्यादातर ThreeTen-Backport में जावा 6 & से 7 वापस भेजा गया है।
  • Android
    • java.time वर्गों के एंड्रॉयड बंडल कार्यान्वयन के बाद के संस्करण।
    • पहले एंड्रॉइड के लिए, ThreeTenABP प्रोजेक्ट थ्रीटेन-बैकपोर्ट (ऊपर उल्लिखित) का अनुकूलन करता है। How to use ThreeTenABP… देखें।

ThreeTen-Extra परियोजना अतिरिक्त कक्षाओं के साथ java.time फैली हुई है। यह परियोजना java.time के संभावित भविष्य के जोड़ों के लिए एक सिद्ध भूमि है। आपको यहां कुछ उपयोगी कक्षाएं मिल सकती हैं जैसे Interval, YearWeek, YearQuarter, और more