java - इन दो बार(1 9 27 में) क्यों अजीब परिणाम दे रहा है?




date timezone (6)

यदि मैं निम्न प्रोग्राम चलाता हूं, जो दो दिनांक तारों को संदर्भित करता है तो 1 सेकंड अलग-अलग संदर्भ देता है और उनकी तुलना करता है:

public static void main(String[] args) throws ParseException {
    SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    String str3 = "1927-12-31 23:54:07";  
    String str4 = "1927-12-31 23:54:08";  
    Date sDt3 = sf.parse(str3);  
    Date sDt4 = sf.parse(str4);  
    long ld3 = sDt3.getTime() /1000;  
    long ld4 = sDt4.getTime() /1000;
    System.out.println(ld4-ld3);
}

आउटपुट है:

353

ld4-ld3 1 क्यों नहीं है (जैसा कि मैं समय में एक-दूसरे अंतर से अपेक्षा करता हूं), लेकिन 353 ?

यदि मैं दिनांक 1 बार बाद में बदलता हूं:

String str3 = "1927-12-31 23:54:08";  
String str4 = "1927-12-31 23:54:09";  

फिर ld4-ld3 1 होगा।

जावा संस्करण:

java version "1.6.0_22"
Java(TM) SE Runtime Environment (build 1.6.0_22-b04)
Dynamic Code Evolution Client VM (build 0.2-b02-internal, 19.0-b04-internal, mixed mode)

Timezone(`TimeZone.getDefault()`):

sun.util.calendar.ZoneInfo[id="Asia/Shanghai",
offset=28800000,dstSavings=0,
useDaylight=false,
transitions=19,
lastRule=null]

Locale(Locale.getDefault()): zh_CN

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

  • निर्यात एलसी_ALL = सी टीजेड = यूटीसी
  • यूटीसी को अपनी सिस्टम घड़ी सेट करें
  • स्थानीयकृत कार्यान्वयन का कभी भी उपयोग न करें जब तक कि बिल्कुल जरूरी न हो (यानी केवल प्रदर्शन के लिए)

जावा सामुदायिक प्रक्रिया सदस्यों के लिए मैं अनुशंसा करता हूं:

  • स्थानीयकृत विधियों को डिफ़ॉल्ट नहीं बनाते हैं, लेकिन उपयोगकर्ता को स्थानीयकरण का स्पष्ट रूप से अनुरोध करने की आवश्यकता होती है।
  • यूटीएफ -8 / यूटीसी का उपयोग फिक्स्ड डिफॉल्ट के रूप में करें क्योंकि यह आज डिफ़ॉल्ट है। कुछ और करने का कोई कारण नहीं है, सिवाय इसके कि आप इस तरह के धागे का उत्पादन करना चाहते हैं।

मेरा मतलब है, चलो, वैश्विक स्थैतिक चर विरोधी एंटी-ओओ पैटर्न नहीं हैं? कुछ प्राथमिक पर्यावरण चर द्वारा दिए गए उन व्यापक दोषों को और कुछ नहीं .......


आपको स्थानीय समय की असंतोष का सामना करना पड़ा है:

जब स्थानीय मानक समय रविवार तक पहुंचने वाला था, 1. जनवरी 1 9 28, 00:00:00 घड़ियां पिछली बार 0:05:52 घंटे शनिवार, 31. दिसंबर 1 9 27, 23:54:08 स्थानीय मानक समय के बजाय

यह विशेष रूप से अजीब नहीं है और राजनीतिक या प्रशासनिक कार्यों के कारण समय-समय पर स्विच या बदल दिया गया है, क्योंकि एक समय या दूसरे में कहीं भी काफी कुछ हुआ है।


जैसा कि दूसरों द्वारा समझाया गया है, वहां एक समय विघटन होता है। Asia/Shanghai 1927-12-31 23:54:08 लिए दो संभावित 1927-12-31 23:54:08 ऑफ़सेट हैं, लेकिन 1 1927-12-31 23:54:07 लिए केवल एक ऑफसेट है। तो, किस ऑफसेट का उपयोग किया जाता है, इस पर निर्भर करता है कि या तो एक दूसरा अंतर या 5 मिनट और 53 सेकंड का अंतर होता है।

सामान्य एक घंटे की डेलाइट बचत (ग्रीष्मकालीन समय) के बजाय ऑफ़सेट की यह मामूली बदलाव, हम समस्या का थोड़ा सा अस्पष्ट उपयोग करते हैं।

ध्यान दें कि टाइमज़ोन डेटाबेस के 2013a अद्यतन ने कुछ सेकंड पहले इस विघटन को स्थानांतरित कर दिया था, लेकिन प्रभाव अभी भी देखा जा सकता है।

जावा 8 पर नया java.time पैकेज इसे और अधिक स्पष्ट रूप से देखने दें, और इसे संभालने के लिए टूल प्रदान करें। दिया हुआ:

DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
dtfb.append(DateTimeFormatter.ISO_LOCAL_DATE);
dtfb.appendLiteral(' ');
dtfb.append(DateTimeFormatter.ISO_LOCAL_TIME);
DateTimeFormatter dtf = dtfb.toFormatter();
ZoneId shanghai = ZoneId.of("Asia/Shanghai");

String str3 = "1927-12-31 23:54:07";  
String str4 = "1927-12-31 23:54:08";  

ZonedDateTime zdt3 = LocalDateTime.parse(str3, dtf).atZone(shanghai);
ZonedDateTime zdt4 = LocalDateTime.parse(str4, dtf).atZone(shanghai);

Duration durationAtEarlierOffset = Duration.between(zdt3.withEarlierOffsetAtOverlap(), zdt4.withEarlierOffsetAtOverlap());

Duration durationAtLaterOffset = Duration.between(zdt3.withLaterOffsetAtOverlap(), zdt4.withLaterOffsetAtOverlap());

फिर durationAtEarlierOffset एक सेकंड होगी, जबकि durationAtLaterOffset पांच मिनट और 53 सेकंड होगा।

इसके अलावा, ये दो ऑफसेट समान हैं:

// Both have offsets +08:05:52
ZoneOffset zo3Earlier = zdt3.withEarlierOffsetAtOverlap().getOffset();
ZoneOffset zo3Later = zdt3.withLaterOffsetAtOverlap().getOffset();

लेकिन ये दोनों अलग हैं:

// +08:05:52
ZoneOffset zo4Earlier = zdt4.withEarlierOffsetAtOverlap().getOffset();

// +08:00
ZoneOffset zo4Later = zdt4.withLaterOffsetAtOverlap().getOffset();

आप 1 1927-12-31 23:59:59 तुलना में 1 1927-12-31 23:59:59 तुलना में एक ही समस्या देख सकते हैं, हालांकि, इस मामले में, यह पहले ऑफसेट है जो लंबे विचलन का उत्पादन करता है, और यह है पहले की तारीख जिसमें दो संभावित ऑफसेट हैं।

इस तक पहुंचने का एक और तरीका यह जांचना है कि कोई संक्रमण चल रहा है या नहीं। हम ऐसा कर सकते हैं:

// Null
ZoneOffsetTransition zot3 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

// An overlap transition
ZoneOffsetTransition zot4 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

आप जांच सकते हैं कि संक्रमण एक ओवरलैप है - इस मामले में उस दिनांक / समय के लिए एक से अधिक वैध ऑफसेट हैं - या एक अंतर - जिस स्थिति में दिनांक / समय उस क्षेत्र आईडी के लिए मान्य नहीं है - isOverlap() का उपयोग करके और isGap() पर isGap() विधियों।

मुझे उम्मीद है कि जावा 8 व्यापक रूप से उपलब्ध होने के बाद या जेएसआर 310 बैकपोर्ट को अपनाने वाले जावा 7 का उपयोग करने वाले लोगों के लिए इस तरह के मुद्दे को संभालने में लोगों की मदद करता है।


प्रत्येक तिथि को बदलने के बजाय, आप निम्न कोड का उपयोग करते हैं

long difference = (sDt4.getTime() - sDt3.getTime()) / 1000;
System.out.println(difference);

और परिणाम देखें:

1

यह 31 दिसंबर को शंघाई में एक समय क्षेत्र परिवर्तन है।

शंघाई में 1 9 27 के विवरण के लिए यह पृष्ठ देखें। मूल रूप से 1 9 27 के अंत में मध्यरात्रि में, घड़ियों 5 मिनट और 52 सेकंड वापस चले गए। तो "1 927-12-31 23:54:08" वास्तव में दो बार हुआ, और ऐसा लगता है कि जावा उस स्थानीय दिनांक / समय के बाद के बाद के संभावित तत्काल के रूप में इसे पार्स कर रहा है - इसलिए अंतर।

समय क्षेत्र की अक्सर अजीब और अद्भुत दुनिया में बस एक और प्रकरण।

संपादित करें: प्रेस रोको! इतिहास बदलता है ...

TZDB के संस्करण 2013a के साथ पुनर्निर्मित होने पर मूल प्रश्न अब एक ही व्यवहार का प्रदर्शन नहीं करेगा। 2013 ए में, परिणाम 238:08 के बजाय 23:54:03 के संक्रमण समय के साथ 358 सेकेंड होगा।

मैंने केवल यह देखा क्योंकि मैं TZDB के रूप में नोडा टाइम में इस तरह के प्रश्न एकत्र कर रहा हूं ... परीक्षण अब बदल दिया गया है, लेकिन यह दिखाता है कि ऐतिहासिक डेटा भी सुरक्षित नहीं है।

संपादित करें: इतिहास फिर से बदल गया है ...

TZDB 2014f में, परिवर्तन का समय 1 9 00-12-31 तक चला गया है, और अब यह केवल 343 दूसरा परिवर्तन है (इसलिए t और t+1 बीच का समय 344 सेकेंड है, यदि आप देखते हैं कि मेरा क्या मतलब है)।

संपादित करें: 1 9 00 में एक संक्रमण के आसपास एक प्रश्न का उत्तर देने के लिए ... ऐसा लगता है कि जावा टाइमज़ोन कार्यान्वयन 1 9 00 यूटीसी की शुरुआत से पहले किसी भी समय के लिए अपने मानक समय में बस के रूप में सभी समय क्षेत्रों का इलाज करता है:

import java.util.TimeZone;

public class Test {
    public static void main(String[] args) throws Exception {
        long startOf1900Utc = -2208988800000L;
        for (String id : TimeZone.getAvailableIDs()) {
            TimeZone zone = TimeZone.getTimeZone(id);
            if (zone.getRawOffset() != zone.getOffset(startOf1900Utc - 1)) {
                System.out.println(id);
            }
        }
    }
}

उपरोक्त कोड मेरी विंडोज मशीन पर कोई आउटपुट नहीं बनाता है। तो किसी भी समय क्षेत्र जो 1 9 00 की शुरुआत में अपने मानक एक के अलावा किसी भी ऑफसेट है, उसे एक संक्रमण के रूप में गिना जाएगा। टीजेडीडीबी के पास पहले से कुछ डेटा वापस जा रहा है, और "निश्चित" मानक समय के किसी भी विचार पर भरोसा नहीं करता है (जो कि getRawOffset को वैध अवधारणा माना जाता है) ताकि अन्य पुस्तकालयों को इस कृत्रिम संक्रमण को पेश करने की आवश्यकता न हो।


समय बढ़ने पर आपको यूटीसी में वापस परिवर्तित करना चाहिए और फिर जोड़ना या घटाना चाहिए। केवल प्रदर्शन के लिए स्थानीय समय का उपयोग करें।

इस तरह आप किसी भी अवधि के दौरान चलने में सक्षम होंगे जहां घंटों या मिनट दो बार होते हैं।

यदि आप यूटीसी में परिवर्तित हो जाते हैं, तो प्रत्येक सेकेंड जोड़ें, और डिस्प्ले के लिए स्थानीय समय में कनवर्ट करें। आप 11:54:08 बजे एलएमटी - 11:59:59 बजे एलएमटी और फिर 11:54:08 बजे CST - 11:59:59 बजे CST माध्यम से जाएंगे।





timezone