当中国的时区过去时,不会像Java一样出色地工作吗? - c#

由于某些游戏开发人员可能会使用过去或将来的日期时间,并且可能会在日期时间模拟的任何时区中使用该日期时间,所以即使我们实际上从未接触过它们。对于此假设,并不是说我要基于伪造的date time进行计算,而是为了像在现实中那样在任何时区中的任何日期时间进行计算的正确性。

我之前曾问过一个有关Java中的中国时区问题的问题,该问题被视为重复问题,因此我删除了它。但是,从此comment thread,我知道这是Java中的某种时间倒带(转换?)问题,而不仅仅是时区变化。

现在,我以不同的方式重新发布该问题,以提出以下问题:

Java中的

  • 代码
    import org.joda.time.*;
    import java.util.*;
    
    class PandaTest {
        static long Subtract(
            Date minuend, Date subtrahend, DateTimeZone zone) {
            long millis;
    
            if(null==zone)
                millis=minuend.getTime()-subtrahend.getTime();
            else {
                long rhs=
                    (new LocalDateTime(subtrahend)).toDateTime(zone)
                    .getMillis();
    
                long lhs=
                    (new LocalDateTime(minuend)).toDateTime(zone)
                    .getMillis();
    
                millis=lhs-rhs;
            }
    
            return millis/1000;
        }
    
        static Date MakeTime(
            int year, int month, int day, int hour, int minute, int second
            ) {
            Calendar calendar=
                Calendar.getInstance(TimeZone.getTimeZone("PRC"));
    
            calendar.set(year, month-1, day, hour, minute, second);
            return calendar.getTime();
        }
    
        static void puts(String arg0) {
            System.out.println(arg0);
        }
    
        static void ShowDuration(DateTimeZone zone, Date ... args) {
            int argc=args.length;
    
            puts("--- ");
            puts("Time Zone: "+(null!=zone?zone.toString():"unspecified"));
    
            for(int i=0; argc-->0; ++i) {
                puts("Time "+i+": "+args[i]);
    
                if(i>0) {
                    long duration=Subtract(args[i], args[i-1], zone);
                    puts("Duration = "+duration);
                }
            }
        }
    
        public static void main(String[] args) {
            Date retainsOfTheDay=MakeTime(1900, 1, 1, 8, 5, 51+0);
            Date somewhereInTime=MakeTime(1900, 1, 1, 8, 5, 51+1);
            DateTimeZone zone=DateTimeZone.forID("PRC");
            ShowDuration(null, retainsOfTheDay, somewhereInTime);
            ShowDuration(zone, retainsOfTheDay, somewhereInTime);
        }
    }
    
  • 如果我从Java的LocalDateTime构造了JodaTime的Date,则会出现问题。 JDK的版本是7u17,JodaTime的版本是2.2。使用NodaTime的C#不会发生这种情况,为了便于对比,我在本文的后面放置了替代版本的代码。

  • 问题
  • 过渡是如何发生的,与Unix Epoch完全一样吗?

    我可能以错误的方式使用过渡一词。我的意思是在Java中用1900/1/1 8:5:52减去1900/1/1 8:5:51的奇怪结果。当时没有时区更改。

    这样的事情是否仅在特定时区或所有时区(可能在不同的瞬间)发生?

  • 如果可以在任何时区对任意date time进行计算,并期望结果总是正确的,是否应该使用DateCalendar

    是,如何使用而不会出现问题?

    一旦我们有可能对1970年之前或2038年之后的Date进行计算,我们是否应该不再在Java中使用Calenderdate time

  • 备用代码

    该代码包含C#和Java的内容,我们可以方便地对比一下C#和Java的结果:

    // Like Java, like Sharp ... the code contains content either in Java or C# 
    // An odd number of `slash-star-slash` at the beginning to compile in Java
    // An even number of `slash-star-slash` at the beginning to compile in C#
    // p.s.: zero would be treated as an even number
    
    using Date=System.DateTime;
    using NodaTime.TimeZones;
    using NodaTime;
    using System.Collections.Generic;
    using System.Linq;
    using System; /*/
    import org.joda.time.*;
    import java.util.*;
    
    // ClockCant in Java 
    class ClockCant {
        public static Date MakeTime(
            int year, int month, int day, int hour, int minute, int second
            ) {
            Calendar calendar=
                Calendar.getInstance(TimeZone.getTimeZone("PRC"));
    
            calendar.set(year, month-1, day, hour, minute, second);
            return calendar.getTime();
        }
    
        public static DateTimeZone GetZoneFromId(String id) {
            return DateTimeZone.forID(id);
        }
    
        public static String GetYourZoneId() {
            return DateTimeZone.getDefault().getID();
        }
    
        public static long Subtract(
            Date minuend, Date subtrahend, DateTimeZone zone) {
            long millis;
    
            if(null==zone)
                millis=minuend.getTime()-subtrahend.getTime();
            else {
                long rhs=
                    (new LocalDateTime(subtrahend)).toDateTime(zone)
                    .getMillis();
    
                long lhs=
                    (new LocalDateTime(minuend)).toDateTime(zone)
                    .getMillis();
    
                millis=lhs-rhs;
            }
    
            return millis/1000;
        }
    }
    
    // a minimum implementation of C#-like List<T> for Java
    class List<T> {
        public T[] ToArray() {
            return _items;
        }
    
        public int Count() {
            return _items.length;
        }
    
        public List(T ... args) {
            _items=args;
        }
    
        T[] _items;
    }
    
    /*/// ClockCant in C#
    class ClockCant {
        public static Date MakeTime(
            int year, int month, int day, int hour, int minute, int second) {
            return new Date(year, month, day, hour, minute, second);
        }
    
        public static DateTimeZone GetZoneFromId(String id) {
            return DateTimeZoneProviders.Tzdb[id];
        }
    
        public static String GetYourZoneId() {
            return DateTimeZoneProviders.Tzdb.GetSystemDefault().Id;
        }
    
        public static long Subtract(
            Date minuend, Date subtrahend, DateTimeZone zone) {
            long ticks;
    
            if(null==zone)
                ticks=minuend.Subtract(subtrahend).Ticks;
            else {
                var rhs=
                    LocalDateTime.FromDateTime(subtrahend)
                    .InZoneLeniently(zone);
    
                var lhs=
                    LocalDateTime.FromDateTime(minuend)
                    .InZoneLeniently(zone);
    
                ticks=(lhs.ToInstant()-rhs.ToInstant()).Ticks;
            }
    
            return ticks/TimeSpan.TicksPerSecond;
        }
    }
    
    // extension for Java-like methods in C#
    static partial class JavaExtensions {
        public static String toString(this Object x) {
            return x.ToString();
        }
    }
    
    class PandaTest { /*/ class PandaTest {
        // in Java
        public static void main(String[] args) {
            Language="Java";
            Main(args);
        }
    
        static void puts(String arg0) {
            System.out.println(arg0);
        }
    
        static void ShowDuration(DateTimeZone zone, Date ... args) {
            ShowDuration(zone, new List<Date>(args));
        }
    
        /*/// in C#
        static void puts(String arg0) {
            Console.WriteLine(arg0);
        }
    
        static void ShowDuration(DateTimeZone zone, params Date[] args) {
            ShowDuration(zone, args.ToList());
        }
    
        /**/// the following code are for both C# and Java
        static void ShowDuration(DateTimeZone zone, List<Date> args) {
            int argc=args.Count();
            Date[] argv=args.ToArray();
    
            puts("--- ");
            puts("Time Zone: "+(null!=zone?zone.toString():"unspecified"));
    
            for(int i=0; argc-->0; ++i) {
                puts("Time "+i+": "+argv[i]);
    
                if(i>0) {
                    long duration=ClockCant.Subtract(argv[i], argv[i-1], zone);
                    puts("Duration = "+duration);
                }
            }
        }
    
        static void Main(String[] args) {
            Date retainsOfTheDay=ClockCant.MakeTime(1900, 1, 1, 8, 5, 51+0);
            Date somewhereInTime=ClockCant.MakeTime(1900, 1, 1, 8, 5, 51+1);
            DateTimeZone zone=ClockCant.GetZoneFromId("PRC");
            puts("Current Time Zone: "+ClockCant.GetYourZoneId());
            puts("Language: "+Language);
            ShowDuration(null, retainsOfTheDay, somewhereInTime);
            ShowDuration(zone, retainsOfTheDay, somewhereInTime);
        }
    
        static String Language="C#";
    }
    

    要使用Java进行编译,请在代码的开头添加/*/,如下所示:

    /*/// Like Java, like Sharp ... 
    

    参考方案

    我相信您的Joda Time测试已经失败,因为基本上您已经使它变得不必要的复杂。这是一个简单但简短但完整的程序,说明了不同之处:

    import java.util.*;
    import org.joda.time.*;
    
    public class ChinaTest {
        public static void main(String[] args) {
            DateTime startOf1900 = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeZone.UTC);
            DateTime endOf1899 = startOf1900.minusMillis(1);
    
            DateTimeZone jodaZone = DateTimeZone.forID("Asia/Shanghai");
            System.out.println("Joda at start of 1900: " +
                               jodaZone.getOffset(startOf1900));
            System.out.println("Joda at end of 1899: " +
                               jodaZone.getOffset(endOf1899));
            TimeZone javaZone = TimeZone.getTimeZone("Asia/Shanghai");
            System.out.println("Java at start of 1900: " + 
                               javaZone.getOffset(startOf1900.getMillis()));
            System.out.println("Java at end of 1899: " + 
                               javaZone.getOffset(startOf1900.getMillis() - 1));
        }
    }
    

    输出:

    Joda at start of 1900: 29152000
    Joda at end of 1899: 29152000
    Java at start of 1900: 29152000
    Java at end of 1899: 28800000
    

    因此,基本上,Java的时区认为在1900年初有过渡,而Joda Time则没有。

    正如我之前所写的,Java的时区实现基本上假设夏令时在1900 UTC开始之前不存在-因此,在任何夏时制在1900年初生效的时区中,都表示过渡。

    乔达时间(Joda Time)和野田时间(Noda Time)都不做这个假设,这就是为什么您会看到差异。

    这与Unix时代无关,也与2038年无关。所有的意思是,您应该期望java.util.TimeZone将1900 UTC开始之前的任何日期/时间都视为该时区的“标准时间”。

    Java-搜索字符串数组中的字符串 - java

    在Java中,我们是否有任何方法可以发现特定字符串是字符串数组的一部分。我可以避免出现一个循环。例如String [] array = {"AA","BB","CC" }; string x = "BB" 我想要一个if (some condition to tell wheth…

    Java Scanner读取文件的奇怪行为 - java

    因此,在使用Scanner类从文件读取内容时,我遇到了一个有趣的问题。基本上,我试图从目录中读取解析应用程序生成的多个输出文件,以计算一些准确性指标。基本上,我的代码只是遍历目录中的每个文件,并使用扫描仪将其打开以处理内容。无论出于何种原因,扫描程序都不会读取其中的一些文件(所有UTF-8编码)。即使文件不是空的,scanner.hasNextLine()在…

    Java Globbing模式以匹配目录和文件 - java

    我正在使用递归函数遍历根目录下的文件。我只想提取*.txt文件,但不想排除目录。现在,我的代码如下所示:val stream = Files.newDirectoryStream(head, "*.txt") 但是这样做将不会匹配任何目录,并且返回的iterator()是False。我使用的是Mac,所以我不想包含的噪音文件是.DS_ST…

    直接读取Zip文件中的文件-Java - java

    我的情况是我有一个包含一些文件(txt,png,...)的zip文件,我想直接按它们的名称读取它,我已经测试了以下代码,但没有结果(NullPointerExcepion):InputStream in = Main.class.getResourceAsStream("/resouces/zipfile/test.txt"); Buff…

    Java RegEx中的单词边界\ b - java

    我在使用\b作为Java Regex中的单词定界符时遇到困难。对于text = "/* sql statement */ INSERT INTO someTable"; Pattern.compile("(?i)\binsert\b");找不到匹配项Pattern insPtrn = Pattern.compile(&…