2015-01-21 4 views
11

मैं अपने आवेदन में JSR 310 दिनांक समय एपीआई * उपयोग कर रहा हूँ, और मैं पार्स करने के लिए और प्रारूप सैन्य तारीख बार (DTG या "दिनांक समय समूह" के रूप में जाना जाता है) की जरूरत है क्षेत्रों।सैन्य समय JSR 310 (DATETIME एपीआई) का उपयोग कर

प्रारूप मैं इस तरह दिखता है (DateTimeFormatter उपयोग करते हुए) को पार्स कर रहा हूँ:

"ddHHmm'Z' MMM yy" // (ie. "312359Z DEC 14", for new years eve 2014) 

यह स्वरूप जैसा कि ऊपर वर्णित पार्स करने के लिए काफी आसान है। समस्या तब उत्पन्न होती है जब तिथियों में 'Z' (ज़ुलू समय क्षेत्र, यूटीसी/जीएमटी के समान) की तुलना में एक अलग समय क्षेत्र होता है, उदाहरण के लिए 'ए' (अल्फा, यूटीसी + 1: 00) या 'बी' (ब्रावो, यूटीसी + 2:00)। पूरी सूची के लिए Military time zones देखें।

मैं इन समय क्षेत्रों कैसे पार्स कर सकते हैं? या दूसरे शब्दों में, मैं शाब्दिक 'ज़ेड' के अलावा उपरोक्त प्रारूप में क्या डाल सकता हूं ताकि यह सभी क्षेत्रों को सही ढंग से पार्स कर सके? मैं "ddHHmmX MMM yy", "ddHHmmZ MMM yy" और "ddHHmmVV MMM yy" उपयोग करने की कोशिश की है, लेकिन उनमें से कोई भी काम करते हैं (सब से ऊपर जब पार्स करने उदाहरण के लिए DateTimeParseException: Text '312359A DEC 14' could not be parsed at index 6 फेंक देंगे,)। (जब DateTimeFormatter का दृष्टांत करने की कोशिश कर IllegalArgumentException) एक एकल V प्रारूप में अनुमति नहीं है का उपयोग करना।

संपादित करें: ऐसा लगता है कि प्रतीक z, काम कर सकते थे अगर यह नीचे जारी करने के लिए नहीं था।

मैं भी कि मैं सभी नामित क्षेत्रों के साथ एक ZoneRulesProvider बनाया है और ऑफसेट सही उल्लेख करना चाहिए। मैंने सत्यापित किया है कि ये एसपीआई तंत्र का उपयोग करके सही तरीके से पंजीकृत हैं, और मेरी provideZoneIds() विधि अपेक्षित के रूप में लागू की जाती है। अभी भी पार्स नहीं होगा। एक साइड इश्यू के रूप में (संपादित करें: यह अब मुख्य मुद्दा प्रतीत होता है), एपीआई द्वारा 'जेड' के अलावा एकल वर्ण समय क्षेत्र आईडी (या "क्षेत्र") की अनुमति नहीं है।

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

ZoneId alpha = ZoneId.of("A"); // boom 

DateTimeException: Invalid zone: A (यहां तक ​​कि मेरे नियमों प्रदाता तक पहुँचने यदि वह मौजूद है को देखने के लिए बिना) फेंक देते हैं।

क्या यह एपीआई में एक निरीक्षण है? या मुझ से कुछ गलत हो रहा है?


*) वास्तव में, मैं जावा 7 और ThreeTen Backport उपयोग कर रहा हूँ, लेकिन मुझे नहीं लगता कि इस प्रश्न के लिए मायने रखती है है। (। यानी "ddHHmm'A' MMM yy", "ddHHmm'B' MMM yy", आदि), क्षेत्र आईडी निकालने, और सही क्षेत्र पर आधारित फ़ॉर्मेटर को सौंपने के लिए एक RegExp का उपयोग मेरे वर्तमान समाधान का शाब्दिक क्षेत्र आईडी के साथ 25 विभिन्न DateTimeFormatter रों उपयोग करने के लिए है:

पी एस। प्रदाता में जोन आईडी को ज़ोन खोजने के लिए ZoneId.of(...) की अनुमति देने के लिए "अल्फा", "ब्रावो" नाम दिया गया है। यह काम करता हैं। लेकिन यह बहुत ही सुरुचिपूर्ण नहीं है, और मुझे आशा है कि एक बेहतर समाधान होगा।

+1

क्या आपने प्रारूप स्ट्रिंग में क्षेत्र-आईडी के लिए 'V'' की कोशिश की है (यानी "ddHHmmV MMM yy" ')? AFAIK 'Z' का अर्थ केवल 'Z' वर्ण है। https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html –

+1

हां, मैं समझता हूं कि 'ज़ेड' एक शाब्दिक है और इसका विश्लेषण पार्सर द्वारा नहीं किया जाता है। 'Z' (unquoted) हालांकि एक प्रतीक है (लेकिन काम नहीं करता है)। और हाँ, मैंने 'वी' की कोशिश की। यह यद्यपि एक अलग त्रुटि देता है ... 'अवैध अर्ग्यूमेंट अपवाद: फ़ॉर्मर बनाने की कोशिश करते समय पैटर्न अक्षर गणना 2: V' होना चाहिए। 'वीवी' का उपयोग करके, मैं वापस पा रहा हूं "... को पार्सिंग के दौरान" इंडेक्स 6 पर पार्स नहीं किया जा सका "। फिर भी धन्यवाद! :-) – haraldK

+0

@PavelHoral वास्तव में, ऐसा लगता है कि "सही" प्रतीक 'z' (लोअरकेस) है ... ऐसा लगता है कि ज़ोन को सही ढंग से पार्स करना है, लेकिन निश्चित रूप से यह एक अक्षर अक्षर की अनुमति नहीं है ..: -पी "' ... पार्स नहीं किया जा सका: अवैध क्षेत्र: ए' "। मैं प्रश्न अपडेट करूँगा ... – haraldK

उत्तर

8

java.time में, ZoneId 2 या इससे अधिक वर्ण होने के लिए सीमित है। विडंबना यह है कि यह भविष्य में जेडीके रिलीज में सैन्य आईडी को जोड़ने की अनुमति देने के लिए अंतरिक्ष आरक्षित करना था, अगर यह मांग में भारी साबित हुआ। ऐसे में, दुख की बात है कि आपका प्रदाता काम नहीं करेगा, और उन नामों के साथ ZoneId उदाहरण बनाने का कोई तरीका नहीं है।

पार्स समस्या घुलनशील है एक बार आप ZoneOffset बजाय ZoneId के साथ काम करने पर विचार (और यह देखते हुए कि सैन्य क्षेत्रों ऑफसेट तय कर रहे हैं, कि एक अच्छा तरीका समस्या को देखने के लिए है)।

कुंजी DateTimeFormatterBuilder.appendText(TemporalField, Map) विधि है जो आपकी पसंद के पाठ का उपयोग करके संख्यात्मक फ़ील्ड को प्रारूपित और पाठ के रूप में पार्स करने की अनुमति देती है। और ZoneOffset एक संख्यात्मक फ़ील्ड है (ऑफसेट में सेकंड की कुल संख्या मान है)।

मैं यह उदाहरण, मैंने Z, A और B के लिए मैपिंग सेट अप किया है, लेकिन आपको उन्हें सभी जोड़ना होगा। अन्यथा, कोड बहुत सरल है, एक फॉर्मेटर स्थापित करना जो सैन्य समय को प्रिंट और पार्स कर सकता है (दिनांक और समय के लिए OffsetDateTime का उपयोग करें)।

Map<Long, String> map = ImmutableMap.of(0L, "Z", 3600L, "A", 7200L, "B"); 
DateTimeFormatter f = new DateTimeFormatterBuilder() 
    .appendPattern("HH:mm") 
    .appendText(ChronoField.OFFSET_SECONDS, map) 
    .toFormatter(); 
System.out.println(OffsetTime.now().format(f)); 
System.out.println(OffsetTime.parse("11:30A", f)); 
+0

धन्यवाद! निश्चित रूप से एक बहुत साफ और अधिक सुरुचिपूर्ण समाधान! परीक्षण और मेरे प्रारूप के साथ बहुत अच्छी तरह से काम करता है। – haraldK

+0

पीएस: मैं वर्तमान में सभी तारीख/समय को आंतरिक रूप से ज़ुलू में परिवर्तित कर रहा हूं और 'ऑफ़सेटडेट टाइम' की बजाय 'ज़ोनडेटडेट टाइम' का उपयोग कर रहा हूं। मुझे जोडा में उपयोग किया जाता है, लेकिन यह जेएसआर 310 का उपयोग कर मेरा पहला प्रोजेक्ट है। क्या ज़ेडडीटी पर ओडीटी का उपयोग करने का कोई अच्छा कारण है (मुझे एहसास है कि आप इन कक्षाओं को किसी बिंदु पर मर्ज करना चाहते हैं ... :-)? – haraldK

+2

ओडीटी सरल है और इसमें कोई डीएसटी समस्या नहीं हो सकती है। इसका मानक नेटवर्क ISO08601 प्रारूप भी है। अन्यथा कक्षाएं बहुत समान हैं। जो कुछ भी आपके लिए सबसे अच्छा काम करता है उसका प्रयोग करें। – JodaStephen

3

java.time-पैकेज के व्यवहार (JSR-310) क्षेत्र आईडी के समर्थन के संबंध में निर्दिष्ट के रूप में किया जाता है - javadoc देखते हैं। प्रासंगिक खंड का स्पष्ट उद्धरण (अन्य आईडी को प्रारूप "जेड", "+ एचएच: एमएम", "-एचएच: एमएम" या "यूटीसी + एचएच: एमएम" आदि में ऑफसेट-आईडी के रूप में माना जाता है।):

एक क्षेत्र आधारित आईडी दो या अधिक वर्ण

कम से कम दो पात्रों की आवश्यकता को भी समय-क्षेत्र डेटा के किसी भी लोड करने से पहले वर्ग ZoneRegion के स्रोत कोड में कार्यान्वित किया जाता है का होना चाहिए शुरू होता है:

/** 
* Checks that the given string is a legal ZondId name. 
* 
* @param zoneId the time-zone ID, not null 
* @throws DateTimeException if the ID format is invalid 
*/ 
private static void checkName(String zoneId) { 
    int n = zoneId.length(); 
    if (n < 2) { 
     throw new DateTimeException("Invalid ID for region-based ZoneId, invalid format: " + zoneId); 
    } 
    for (int i = 0; i < n; i++) { 
     char c = zoneId.charAt(i); 
     if (c >= 'a' && c <= 'z') continue; 
     if (c >= 'A' && c <= 'Z') continue; 
     if (c == '/' && i != 0) continue; 
     if (c >= '0' && c <= '9' && i != 0) continue; 
     if (c == '~' && i != 0) continue; 
     if (c == '.' && i != 0) continue; 
     if (c == '_' && i != 0) continue; 
     if (c == '+' && i != 0) continue; 
     if (c == '-' && i != 0) continue; 
     throw new DateTimeException("Invalid ID for region-based ZoneId, invalid format: " + zoneId); 
    } 
} 

यही कारण है कि यह संभव नहीं है JSR-310/Threeten ZoneId.of("A") की तरह एक अभिव्यक्ति लिखने के लिए। पत्र जेड काम करता है क्योंकि यह शून्य ऑफसेट का प्रतिनिधित्व करने के लिए जेएसआर -310 में आईएसओ -8601 में भी निर्दिष्ट है।

जो काम आपने पाया है वह जेएसआर -310 के दायरे में ठीक है जो सैन्य समय क्षेत्र का समर्थन नहीं करता है। इसके परिणामस्वरूप आपको इसके लिए कोई प्रारूप समर्थन नहीं मिलेगा (केवल कक्षा DateTimeFormatterBuilder का अध्ययन करें - प्रारूप पैटर्न प्रतीकों की प्रत्येक प्रसंस्करण बिल्डर को भेजी जाती है)। मुझे मिला एकमात्र अस्पष्ट विचार एक विशेष TemporalField को सैन्य टाइमज़ोन ऑफ़सेट का प्रतिनिधित्व करना था। लेकिन कार्यान्वयन (यदि संभव हो तो) निश्चित रूप से आपके कामकाज से अधिक जटिल है।

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

String input = "312359A Dec 14"; 
String offset = ""; 

switch (input.charAt(6)) { 
    case 'A': 
    offset = "+01:00"; 
    break; 
    case 'B': 
    offset = "+02:00"; 
    break; 
    //... 
    case 'Z': 
    offset = "Z"; 
    break; 
    default: 
    throw new ParseException("Wrong military timezone: " + input, 6); 
} 
input = input.substring(0, 6) + offset + input.substring(7); 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("ddHHmmVV MMM yy", Locale.ENGLISH); 
ZonedDateTime odt = ZonedDateTime.parse(input, formatter); 
System.out.println(odt); 
// output: 2014-12-31T23:59+01:00 

नोट्स:

  • मैं "दिसं" का इस्तेमाल किया है के बजाय "डीईसी", अन्यथा पार्सर शिकायत करेगा। यदि आपके इनपुट में वास्तव में पूंजी अक्षरों हैं तो आप बिल्डर विधि parseCaseInsensitive() का उपयोग कर सकते हैं।

  • अन्य जवाब क्षेत्र OffsetSeconds का उपयोग कर पार्स समस्या के संबंध में बेहतर जवाब है और यह भी मेरी वोट दें मिल गया है (इस सुविधा अनदेखा कर दिया)। यह बेहतर नहीं है क्योंकि यह उपयोगकर्ता को बोझ डालता है ताकि सैन्य क्षेत्र अक्षरों से ऑफसेट पर मैपिंग को परिभाषित किया जा सके - जैसे स्ट्रिंग प्रीप्रोकैसिंग के साथ मेरा सुझाव। लेकिन यह बेहतर है क्योंकि यह बिल्डर-विधियों optionalStart() और optionalEnd() का उपयोग करने में सक्षम बनाता है, इसलिए वैकल्पिक समय क्षेत्र अक्षर ए, बी, ... को संभाला जा सकता है। वैकल्पिक क्षेत्र आईडी के बारे में ओपी की टिप्पणी भी देखें।

+0

पुष्टि करने के लिए धन्यवाद, मुझे लगता है कि मैं इसे सही उत्तर के रूप में चिह्नित करूंगा, जैसा कि मैं करना चाहता हूं, वर्तमान स्पेक/कार्यान्वयन के बाद असंभव लगता है (मैंने threetenbp प्रोजेक्ट के साथ कोई समस्या दर्ज की है, प्रतिक्रिया का इंतजार कर रहा है)। – haraldK

+0

पुन। स्ट्रिंग प्रीप्रोकैसिंग और केस संवेदनशीलता, हाँ, मुझे इसके बारे में पता है। पार्सिंग के लिए उपयोग किए जाने वाले वास्तविक प्रारूप को मैंने जो दिखाया है उससे कहीं अधिक जटिल है। जोन आईडी वैकल्पिक है (यदि छोड़ा गया है, ज़ेड माना जाता है) और रिक्त स्थान भी वैकल्पिक हैं (जिसका अर्थ है 'charAt (6) 'अप्रैल में ए हो सकता है ...)। मैं अपने regexp आधारित समाधान से चिपके रहूंगा। मैं regexp प्रतिस्थापन का उपयोग करने में देखता हूँ हालांकि, यह काम कर सकता है! और मैंने जो 'जोनरूल्सप्रोवाइडर' बनाया है वह शायद अनावश्यक है, मैं अभी 'ज़ोनऑफसेट' का उपयोग कर सकता था। वैसे भी, एक अच्छे और गहन जवाब के लिए धन्यवाद! :-) – haraldK

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