जावा का उपयोग करके कैलेंडर टाइमज़ोन को कैसे संभालना है?


92

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

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

उपयोगकर्ता प्रवेश करता है: 5/1/2008 6:12 PM (ईएसटी)
WS के लिए पैरामीटर होना चाहिए : 5/1/2008 6:12 PM (GMT)

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

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 PM]


2
आप केवल समय-क्षेत्र क्यों बदल रहे हैं और इसके साथ दिनांक / समय परिवर्तित नहीं कर रहे हैं?
स्पेंसर कोरम्स

1
जावा तिथि लेने के लिए एक वास्तविक दर्द जो एक समयक्षेत्र में है, और उस तिथि को किसी अन्य समयक्षेत्र में प्राप्त करें। IE, 5PM EDT लें और 5PM PDT प्राप्त करें।
21:12 पर mtyson

जवाबों:


62
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" Calendar.getInstance()) से पास करता हूँ तो यहाँ यह आउटपुट है:

DEBUG - इनपुट कैलेंडर की तिथि [Thu Oct 23 12:09:05 EDT 2008]
DEBUG - ऑफसेट है -14400000
DEBUG - तिथि के साथ बनाया गया GMT कैल [Thu Oct 23 08:09:05 EDT 2008]

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

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


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

29

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

// 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()));

कोड का आउटपुट है: (उपयोगकर्ता 5/1/2008 6:12 PM (ईएसटी) में प्रवेश किया

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


20

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

यदि यह स्थिति है, तो आपको DateFormat वर्ग के सेटटाइमजोन विधि पर एक नज़र डालनी चाहिए । यह तय करता है कि टाइम स्टैम्प को प्रिंट करते समय कौन से समय क्षेत्र का उपयोग किया जाएगा।

एक सरल उदाहरण:

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
SDF से संबंधित नहीं था, यह अपना समय क्षेत्र था, सोच रहा था कि कैलेंडर TimeZone को बदलने का कोई प्रभाव क्यों नहीं है!
क्रिस। जेनकिंस

TimeZone.getTimeZone ("UTC") मान्य नहीं है क्योंकि UTC उपलब्ध नहीं है () ... भगवान को पता है क्यों
childno knows.de

TimeZone.getTimeZone ("UTC") मेरी मशीन पर उपलब्ध है। क्या यह जेवीएम पर निर्भर है?
कोडकालींबर

2
SetTimeZone () विधि के साथ बहुत बढ़िया विचार। आपने मुझे बहुत सारे सिरदर्द से बचा लिया, बहुत बहुत धन्यवाद!
बोगदान जुरैक

1
मुझे जिस चीज की जरूरत थी! आप सर्वर टाइम ज़ोन को फॉर्मेटर में सेट कर सकते हैं और फिर जब आप इसे कैलेंडर फॉर्मेट में बदलेंगे तो आपको चिंता करने की कोई बात नहीं है। अब तक का सबसे अच्छा तरीका! GetTimeZone के लिए आपको प्रारूप का उपयोग करने की आवश्यकता हो सकती है Ex: "GMT-4: 00" ETD के लिए
माइकल केर्न

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"));

इस बात का ध्यान रखें यदि आप हाइबरनेट 4 का उपयोग कर रहे हैं, जो कि साइड डिपेंडेंसी और अतिरिक्त कॉन्फिगरेशन के बिना सीधे संगत नहीं है। हालाँकि यह तीसरे संस्करण के लिए सबसे तेज़ और आसान तरीका था।
ऑबर्जिन सेप

8

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

यह पदावनत है, लेकिन यह काम करना चाहिए:

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

गैर-वंचित तरीका उपयोग करना है

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

लेकिन क्लाइंट की तरफ से इसे करने की आवश्यकता होगी, क्योंकि यह सिस्टम जानता है कि यह किस समय में है।


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;
}

6

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

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

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

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

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

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


4

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

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

timezoneId उपयोगकर्ता के टाइमज़ोन (जैसे ईएसटी) की आईडी है।


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

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

1

java.time

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

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

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

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

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

Instant instant = myTimestamp.toInstant() ;

किसी अन्य समय क्षेत्र में समायोजित करने के लिए, समय क्षेत्र को एक ZoneIdवस्तु के रूप में निर्दिष्ट करें । के रूप में , या , के प्रारूप में एक उचित समय क्षेत्र नाम निर्दिष्ट करें । कभी इस तरह के रूप में 3-4 पत्र छद्म ज़ोन का उपयोग या के रूप में वे कर रहे हैं नहीं सच समय क्षेत्र, नहीं मानकीकृत, और यहां तक कि अद्वितीय नहीं (!)।continent/regionAmerica/MontrealAfrica/CasablancaPacific/AucklandESTIST

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

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

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

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 ढांचे जावा 8 और बाद में बनाया गया है। इन कक्षाओं परेशानी वर्ष प्रतिस्थापित विरासत जैसे तिथि-समय कक्षाएं java.util.Date, Calendar, औरSimpleDateFormat

Joda समय परियोजना, अब में रखरखाव मोड , प्रवास करने की सलाह देता java.time कक्षाएं।

अधिक जानने के लिए, Oracle ट्यूटोरियल देखें । और कई उदाहरणों और स्पष्टीकरणों के लिए ढेर अतिप्रवाह खोजें। विशिष्टता JSR 310 है

Java.time कक्षाएं कहाँ से प्राप्त करें?

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

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