2015-10-31 100 views
1

我已經轉換和從Unix紀元下面的轉換方法時間戳日期時間轉換爲Unix紀元添加幻影小時

public static class DateTimeHelpers 
{ 
    public static DateTime UnixEpoch() 
    { 
     return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 
    } 

    public static DateTime FromMillisecondsSinceUnixEpoch(long milliseconds) 
    { 
     return UnixEpoch().AddMilliseconds(milliseconds).ToLocalTime(); 
    } 

    public static long ToMillisecondsSinceUnixEpoch(DateTime dateTime) 
    { 
     return (long)(dateTime - UnixEpoch()).TotalMilliseconds; 
    } 
} 

問題是(男孩,這似乎是基本的東西),我設置一個DateTime我想然後嘗試轉換爲Unix-Time,但返回的毫秒時間戳爲+01:00小時,我想知道爲什麼?

我使用的代碼是

DateTime startDate = new DateTime(2015, 10, 1, 0, 0, 0, 0, DateTimeKind.Utc); 

long startMillis = DateTimeHelpers.ToMillisecondsSinceUnixEpoch(startDate); 

這給startMillis = 1443657600000這是 「星期四2015年10月1日01:00:00(上午)在時區歐洲/倫敦(BST)」。我想從ToMillisecondsSinceUnixEpoch回到「2015/10/01 00:00:00」的時間戳,我在這裏錯過了什麼?

感謝您的時間。


編輯。我想做一些Java代碼的等價物。這產生了正確的時間戳。爲什麼我可以用Java而不是C#來做到這一點?但無論如何,代碼

private static long ukTimeStringToUtcMillis(String s) { 
    SimpleDateFormat sdf = makeSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); 
    try { 
     return sdf.parse(s).getTime(); 
    } catch (ParseException e) { 
     throw new RuntimeException(e); 
    } 
} 

private static SimpleDateFormat makeSimpleDateFormat(String s) { 
    SimpleDateFormat sdf = new SimpleDateFormat(s); 
    sdf.setTimeZone(TimeZone.getTimeZone("Europe/London")); 
    return sdf; 
} 

我使用這樣的

long timestamp = ukTimeStringToUtcMillis("2015-10-01T00:00:00.000"); 

這給timestamp = 1443654000000這是「星期四2015年10月1日00:00:00(上午)在時區歐洲/倫敦(BST) 」。我錯過了什麼C#?我試過

var ukTimeZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time"); 
DateTime unixEpoch = TimeZoneInfo.ConvertTime(
    new DateTime(1970, 1, 1, 0, 0, 0), ukTimeZone, ukTimeZone); 

long startMillis = (long)(startDate - unixEpoch).TotalMilliseconds; 
long endMillis = (long)(endDate - unixEpoch).TotalMilliseconds; 

這個ADDS一個小時!

+0

'歐洲/倫敦(BST)'現在不是'UTC +01:00'嗎?這是否正常返回'Oct 01 2015 01:00:00'? http://www.epochconverter.com/epoch/timezones.php?epoch=1443657600 –

+0

我正在嘗試做一些C#相當於一些Java。我編輯了這個問題。如果你可以投你的眼睛,這將是最感謝! – MoonKnight

+0

'return UnixEpoch()。AddMilliseconds(milliseconds).ToLocalTime();'你沒有以本地時間開始,所以返回的本地時間將包含TZ偏移量。返回UTC,它使往返罰款。 – Plutonix

回答

1

有幾件事情:

  • 如果可能的話,你應該DateTimeOffset工作而不是DateTime這種類型的操作。DateTimeOffset總是在一個特定的時刻,而DateTime可能是,也可能不是,這取決於Kind以及你如何堅持Kind是如何通過各種方法解釋的微妙之處。

  • 如果您確實使用DateTimeOffset,並且您的目標是.NET 4.6或更高版本(或.NET Core),那麼您可以使用內置的DateTimeOffset.FromUnixTimeMillisecondsToUnixTimeMilliseconds方法,而不是創建自己的方法。

  • 您可能會考慮使用Noda Time開源庫,因爲它爲大多數使用日期和時間的應用程序增加了重要價值。

    • 舉例來說,如果你想與TZDB時區像你提到的"Europe/London"區工作,那麼你可以使用DateTimeZoneProviders.Tzdb["Europe/London"]

現在我的答案的其餘部分假定你不採取任何上述建議,並與您有關的問題提供的代碼。

  • 您有UnixEpoch作爲靜態方法實現。由於它的值永遠不會改變,它可能應該作爲一個靜態屬性來實現,並且有一個私有的只讀後臺字段。它也可以作爲公共靜態只讀字段來實現,儘管大多數人更喜歡通過屬性來暴露這些字段。 (這些只是編碼準則,但不會引入任何錯誤。)

  • 在您的FromMillisecondsSinceUnixEpoch方法中,您致電.ToLocalTime()。這應該被省略。你也不需要撥打.ToUniversalTime()。只需返回加上毫秒的結果。 Kind將爲Utc。如果您需要使用當地時間,請稍後再進行轉換 - 而不是在此功能中。

  • 認識到ID "GMT Standard Time"是倫敦,而不是UTC。倫敦要麼是格林威治標準時間(UTC + 00:00),要麼是BST(UTC + 01:00),具體取決於相關日期和時間。

  • 認識到DateTime.ToLocalTimeDateTime.ToUniversalTime UTC和計算機上的當前本地時區之間進行轉換,其中的代碼運行。這可能是倫敦,或者根據您的使用情況可能是其他東西。如果您運行的是服務器,例如在ASP.Net Web應用程序中,那麼依賴系統本地時區並不是一個好習慣。

  • 在你表現出與TimeZoneInfo.ConvertTime,因爲你沒有指定DateTimeKind.Utc到輸入端的代碼,該值將有DateTimeKind.UnspecifiedConvertTime將解釋爲已屬於源時區。既然你已經給出了相同的目的地時區,那麼在大多數情況下,這將是一個無操作。

  • 在該功能中,由於前面指定的原因,根據倫敦時間定義unixEpoch是無效的。另外請注意,在1970年1月1日,倫敦不在格林威治標準時間,但實際上在BST(當時稱爲「英國標準時間」,而不是「英國夏令時」)。 TZDB知道這一點,但它對於Windows時區和TimeZoneInfo來說太過遙遠了。 Windows區域只反映BST/GMT周圍的當前規則,而不是那些當時有效的規則。

至於將你提供的Java代碼,該函數在ISO 8601格式讀取的字符串精確到毫秒,解釋它在倫敦的時區,將其轉換爲UTC,由於給出的時間以毫秒爲單位Unix時代。還有,你可以做一些方法是:

  • 淨3.5+

    public static long UkTimeStringToUtcMillis(string s) 
    { 
        string format = "yyyy-MM-dd'T'HH:mm:ss.FFF"; 
        DateTime dt = DateTime.ParseExact(s, format, CultureInfo.InvariantCulture); 
        TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time"); 
        DateTime utc = TimeZoneInfo.ConvertTimeToUtc(dt, tz); 
        DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 
        return (long) (utc - epoch).TotalMilliseconds; 
    } 
    
  • 淨4.6+/.NET CoreCLR

    public static long UkTimeStringToUtcMillis(string s) 
    { 
        string format = "yyyy-MM-dd'T'HH:mm:ss.FFF"; 
        DateTime dt = DateTime.ParseExact(s, format, CultureInfo.InvariantCulture); 
        TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time"); 
        TimeSpan offset = tz.GetUtcOffset(dt); 
        DateTimeOffset dto = new DateTimeOffset(dt, offset); 
        return dto.ToUnixTimeMilliseconds(); 
    } 
    
  • 野田佳彥時間

    public static long UkTimeStringToUtcMillis(string s) 
    { 
        LocalDateTimePattern pattern = LocalDateTimePattern.ExtendedIsoPattern; 
        LocalDateTime dt = pattern.Parse(s).Value; 
        DateTimeZone tz = DateTimeZoneProviders.Tzdb["Europe/London"]; 
        Instant i = dt.InZoneLeniently(tz).ToInstant(); 
        return i.Ticks/NodaConstants.TicksPerMillisecond; 
    } 
    
+0

非常感謝您的時間。這是最感激和真正幫助我理解這個問題。 – MoonKnight

3

如果我跟着你在做什麼,你的測試代碼開始與UTC時間:

DateTime startDate = new DateTime(2015, 10, 1, 0, 0, 0, 0, DateTimeKind.Utc); 

FromMillisecondsSinceUnixEpoch返回本地時間。使用代碼,它不會讓往返:

Console.WriteLine(dt.ToUniversalTime()); 
Console.WriteLine("{0} {1}", ToMillisecondsSinceUnixEpoch(dt), 
    FromMillisecondsSinceUnixEpoch(ToMillisecondsSinceUnixEpoch(dt))); 

2015年10月1日上午5時00分00秒
1443657600000 2015年10月1日12:00:00 AM

如果我改變FromMillisecondsSinceUnixEpoch

public static DateTime FromMillisecondsSinceUnixEpoch(long milliseconds) 
{ 
    return UnixEpoch().AddMilliseconds(milliseconds).ToUniversalTime(); 
} 

現在,它將使往返:

2015年10月1日上午05時00分00秒
1443675600000 2015年10月1日上午05時00分00秒

注比MilliSecondsSince對於每個相同的。你不能只看這個,因爲沒有上下文。

我無法在參考源中找到它,但肯定DateTime足夠聰明,可以在TZs不同時進行調整,然後再進行相減。(否則就很難解釋如何1443675600000可以代表3周的時間跨度爲同一日期(2我和一個給你)

+0

非常感謝您的回覆。你最欣賞的時間。我認同你是否在這裏誤導了你。我知道在當地時間下來往返是不可能的。這是在我玩的時候添加的......問題是獲取2015/10/01 00:00:00的毫秒時間戳。當我創建'New DateTime(2015,10,1,0,0,0)'並嘗試獲取UNIX紀元中的時間戳時,爲什麼會給出「2015/10/01 01:00:00」的時間戳?我如何獲得00:00:00的時間戳,爲什麼會添加一個小時,我對'ToLocalTime()'無所作爲? – MoonKnight

+1

自從(1443675600000)開始的毫秒數包含本地機器的TZ偏移量。這就是爲什麼創建爲UTC或Local的DateTime返回相同的Unix流逝值。無論時間是UTC,BST還是其他任何東西,都已經過相同的MS數量。 – Plutonix

相關問題