java - لماذا يتم طرح هذين المرتين(في عام 1927) لإعطاء نتيجة غريبة؟




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 ؟

إذا قمت بتغيير التواريخ إلى ثانية واحدة في وقت لاحق:

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

IMHO التوطين الضمني الواسع الانتشار في Java هو عيب التصميم الأكبر. قد يكون مخصصًا لواجهات المستخدم ، لكن بصراحة ، من يستخدم Java بالفعل لواجهات المستخدم اليوم باستثناء بعض IDEs حيث يمكنك تجاهل التعريب بشكل أساسي لأن المبرمجين ليسوا الجمهور المستهدف بالضبط. يمكنك إصلاحها (خاصة على خوادم Linux) من خلال:

  • تصدير LC_ALL = C TZ = UTC
  • تعيين ساعة النظام الخاص بك إلى UTC
  • عدم استخدام التطبيقات المترجمة مطلقًا ما لم يكن ضروريًا تمامًا (أي للعرض فقط)

لأعضاء عملية مجتمع جافا أوصي:

  • جعل الطرق المترجمة ليست الافتراضية ، ولكن تتطلب من المستخدم طلب الترجمة بشكل صريح.
  • استخدم UTF-8 / UTC مثل الافتراضي FIXED بدلا من ذلك لأنه ببساطة الافتراضي اليوم. لا يوجد سبب للقيام بشيء آخر ، إلا إذا كنت ترغب في إنتاج سلاسل محادثات كهذه.

أعني ، هيا ، أليست متغيرات ثابتة عالمية نمطًا مضادًا لـ OO؟ لا شيء آخر هو تلك الافتراضات المنتشرة من قبل بعض المتغيرات البيئة البدائية .......


إنه تغيير في المنطقة الزمنية في 31 ديسمبر في شنغهاي.

انظر هذه الصفحة للحصول على تفاصيل حول 1927 في شنغهاي. أساسا في منتصف الليل في نهاية عام 1927 ، عادت الساعات 5 دقائق و 52 ثانية. لذا حدث "1927-12-31 23:54:08" في الواقع مرتين ، ويبدو أن جافا تعالجه كحالة لاحقة محتملة لذلك التاريخ / الوقت المحلي - ومن هنا جاءت الفارق.

مجرد حلقة أخرى في العالم الغريب والرائع في المناطق الزمنية.

تحرير: توقف الصحافة! تغييرات التاريخ ...

لم يعد السؤال الأصلي يظهر السلوك نفسه تمامًا ، إذا أعيد بناؤه باستخدام الإصدار 2013a من TZDB . في 2013 أ ، ستكون النتيجة 358 ثانية ، مع وقت انتقالي 23:54:03 بدلاً من 23:54:08.

لقد لاحظت هذا فقط لأنني أجمع أسئلة مثل هذه في نودا تايم ، في شكل TZDB ... تم الآن تغيير الاختبار ، ولكن الأمر لا يراه إلا أن البيانات التاريخية ليست آمنة.

تحرير: لقد تغير التاريخ مرة أخرى ...

في TZDB 2014f ، انتقل وقت التغيير إلى 1900-12-31 ، وهو الآن مجرد تغيير 343 ثانية (لذا فإن الوقت بين t و t+1 هو 344 ثانية ، إذا رأيت ما أعنيه).

تحرير: للإجابة على سؤال حول الانتقال في عام 1900 ... يبدو أن تطبيق Java المنطقة الزمنية يعامل جميع المناطق الزمنية ببساطة في الوقت القياسي لأي لحظة قبل بداية UTC 1900:

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

لا ينتج الرمز أعلاه أي إخراج على جهاز Windows الخاص بي. لذلك فإن أي منطقة زمنية لديها أي إزاحة غير الموازنة القياسية في بداية عام 1900 ستحسب ذلك كعملية انتقالية. يحتوي TZDB نفسه على بعض البيانات التي تعود في وقت سابق من ذلك ، ولا يعتمد على أي فكرة عن الوقت القياسي "الثابت" (وهو ما يفترض أن يكون getRawOffset مفهومًا صالحًا) حتى لا تحتاج المكتبات الأخرى إلى إدخال هذا الانتقال الاصطناعي.


بدلاً من تحويل كل تاريخ ، تستخدم التعليمة البرمجية التالية

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

ونرى النتيجة هي:

1

عند زيادة الوقت ، يجب عليك العودة إلى التوقيت العالمي المنسق ثم إضافة أو طرح. استخدم التوقيت المحلي للعرض فقط.

بهذه الطريقة ستتمكن من المشي خلال أي فترات تحدث فيها الساعات أو الدقائق مرتين.

إذا قمت بالتحويل إلى UTC ، فأضف كل ثانية ، وقم بالتحويل إلى التوقيت المحلي للعرض. أنت ستذهب خلال 11:54:08 pm LMT - 11:59:59 مساء LMT ثم 11:54:08 pm CST - 11:59:59 pm CST.


لقد واجهت انقطاعًا للوقت المحلي :

عندما كان الوقت القياسي المحلي على وشك الوصول إلى يوم الأحد ، 1. يناير 1928 ، 00:00:00 تم تحويل الساعات إلى الوراء 0:05:52 ساعة إلى يوم السبت 31 ديسمبر 1927 ، 23:54:08 التوقيت القياسي المحلي بدلاً من ذلك

هذا ليس غريباً على وجه الخصوص وقد حدث في كل مكان في وقتٍ أو آخر حيث تم تغيير أو تغيير المناطق الزمنية بسبب الإجراءات السياسية أو الإدارية.


يؤسفني أن أقول ذلك ، لكن الوقت قد توقف بعض الوقت

JDK 6 قبل عامين ، وفي JDK 7 مؤخرا في التحديث 25 .

الدرس المراد تعلمه: تجنب الأوقات غير المنسقة (UTC) بأي ثمن ، باستثناء العرض.





timezone