在JavaScript的Date构造函数中查找和使用用户的时区

时间:2012-04-10 21:58:52

标签: javascript datetime

new Date('April 12, 2012 05:00:00 ').toLocaleString();

日期本身正在通过PHP提供和格式化。但是,如果用户没有登录,那么我不知道他们的时区偏移是什么,这就是为什么我被迫使用JavaScript。

如何找到偏移并应用并仍然将其全部保存在一行代码中?

2 个答案:

答案 0 :(得分:1)

抱歉,伙计。日期很难。

如果可能,让服务器格式化您的日期字符串,并使用网页或ajax响应或其他任何方式将它们发送到浏览器。

如果真的,真的,真的想在javascript中对客户端进行时区转换,则必须将您感兴趣的时区规则发送给客户端。如果你有一台Windows机器,下面是一个powershell脚本,它将生成一个带有时区规则的javascript文件和一个执行转换的函数。当我需要将日期从第三方web api转换为不同于浏览器时区的时区时,我使用了这个。

如果您需要将日期对象放入浏览器时区或utc以外的时区,则应该采用此方法。即使这样,您也可以考虑使用Web服务来格式化日期作为替代方案。

# Serialize .Net TimeZoneInfo objects to a javascript file.
Add-Type -AssemblyName System.Web.Extensions;
$f = "timezoneinfo.js";
$ser = new-object System.Web.Script.Serialization.JavaScriptSerializer;
$tzi = [System.TimeZoneInfo]::FindSystemTimeZoneById("Central Standard Time");

# To keep the file size reasonable, exclude timezoneinfos where this web site has no activity.
$exclude = @("Afghanistan Standard Time",
"Arab Standard Time",
"Arabian Standard Time",
"Arabic Standard Time",
"Argentina Standard Time",
"Atlantic Standard Time",
"AUS Central Standard Time",
"AUS Eastern Standard Time",
"Azerbaijan Standard Time",
"Azores Standard Time",
"Bangladesh Standard Time",
"Cape Verde Standard Time",
"Caucasus Standard Time",
"Cen. Australia Standard Time",
"Central America Standard Time",
"Central Asia Standard Time",
"Central Brazilian Standard Time",
"Central Europe Standard Time",
"Central European Standard Time",
"Central Pacific Standard Time",
"China Standard Time",
"Dateline Standard Time",
"E. Africa Standard Time",
"E. Australia Standard Time",
"E. Europe Standard Time",
"E. South America Standard Time",
"Egypt Standard Time",
"Ekaterinburg Standard Time",
"Fiji Standard Time",
"FLE Standard Time",
"Georgian Standard Time",
"Greenland Standard Time",
"Greenwich Standard Time",
"Hawaiian Standard Time",
"India Standard Time",
"Iran Standard Time",
"Israel Standard Time",
"Jordan Standard Time",
"Kaliningrad Standard Time",
"Kamchatka Standard Time",
"Korea Standard Time",
"Magadan Standard Time",
"Mauritius Standard Time",
"Mid-Atlantic Standard Time",
"Middle East Standard Time",
"Montevideo Standard Time",
"Morocco Standard Time",
"Myanmar Standard Time",
"N. Central Asia Standard Time",
"Namibia Standard Time",
"Nepal Standard Time",
"Newfoundland Standard Time",
"North Asia East Standard Time",
"North Asia Standard Time",
"Pacific SA Standard Time",
"Pakistan Standard Time",
"Paraguay Standard Time",
"Romance Standard Time",
"Russian Standard Time",
"SA Eastern Standard Time",
"SA Pacific Standard Time",
"SA Western Standard Time",
"Samoa Standard Time",
"SE Asia Standard Time",
"Singapore Standard Time",
"Sri Lanka Standard Time",
"Syria Standard Time",
"Taipei Standard Time",
"Tasmania Standard Time",
"Tokyo Standard Time",
"Tonga Standard Time",
"Turkey Standard Time",
"Ulaanbaatar Standard Time",
"Venezuela Standard Time",
"Vladivostok Standard Time",
"W. Australia Standard Time",
"W. Central Africa Standard Time",
"W. Europe Standard Time",
"West Asia Standard Time",
"West Pacific Standard Time",
"Yakutsk Standard Time");

"/*This file was generated by a tool*/" | out-file $f;
"(function (TimeZoneInfo, undefined) {" | out-file $f -Append;

$first = $true;
"var infos = {" | out-file $f -Append;
[System.TimeZoneInfo]::GetSystemTimeZones() | ? {
    $exclude -notcontains $_.Id} | % {

    # Serizliae the TimeZoneInfo
    $s1 = $ser.Serialize($_);
    # Serialize the adjustment rules.  Convert MS Ajax dates into JS date constructors.
    $s2 = $ser.Serialize($_.GetAdjustmentRules()) -replace "\`"\\/Date\((?<ticks>.+?)\)\\/\`"", "new Date(`$1)";
    # Add the adjustment rules to the resulting JSON.
    $s3 = $s1.Substring(0, $s1.Length - 1) + ",AdjustmentRules:" + $s2 + "}";
    # Add item to associative JS array.
    $s3 = "`"" + $_.ID + "`":" + $s3;
    if (!$first) {      
        $s3 = "," + $s3;
        $s3 | write-host;
    } else {
        $first = $false;        
    }

    # Cheesy code here.  But the next few steps remove properties we don't care about to get keep the file size down.
    # Remove strings.
    $s3 = $s3 -replace "`"(Id|DisplayName|StandardName|DaylightName)`":`".+?`"(,)?", "";
    # Remove all of the TimeSpan properties except TotalMinutes.
    $s3 = $s3 -replace "`"(Ticks|Days|Hours|Milliseconds|Minutes|Seconds|TotalDays|TotalHours|TotalMilliseconds)`":[-0-9.]+?,", "";
    $s3 = $s3 -replace ",`"TotalSeconds`":(-)?\d+", "";

    $s3 | out-file $f -Append;
}
"};" | out-file $f -Append;


# Add the client function to do the conversion.  This mainly comes from using reflector and looking as MSDN for IsFixedDateRule.
$s4 = "
// This function will shift a date the approprate number of hours to make it appear correct for the given timezoneid.
TimeZoneInfo.ConvertTime = function(dt, tzid) {
    if (typeof(dt) === `"undefined`" || dt == null) { return null; }
    if (typeof(tzid) === `"undefined`" || tzid == null || tzid === `"`") { return dt; }
    var tz = infos[tzid];
    if (typeof(tz) === `"undefined`") { return dt; }

    var clientOffset = dt.getTimezoneOffset();

    var adj = null;
    var j = tz.AdjustmentRules.length;  
    for (var i = 0; i < j; i++) {
        if (tz.AdjustmentRules[i].DateStart <= dt) {
            if (tz.AdjustmentRules[i].DateEnd >= dt) {
                adj = tz.AdjustmentRules[i];
                break;
            }
        } 
    }

    function GetDaysInMonth(y, m)
    {
        return 32 - new Date(y, m, 32).getDate();
    }

    function GetTransitionDayOfMonth(t, y) {
        var startOfWeek = t.Week * 7 - 6;
        var firstDayOfWeek = new Date(y, t.Month - 1, 1).getDay();
        var transitionDay = null;
        var changeDayOfWeek = t.DayOfWeek;
        if (firstDayOfWeek <= changeDayOfWeek) {
            transitionDay = startOfWeek + (changeDayOfWeek - firstDayOfWeek);
        } else {
            transitionDay = startOfWeek + (7 - firstDayOfWeek + changeDayOfWeek);
        }
        if (transitionDay > GetDaysInMonth(y, t.Month - 1)) {
            transitionDay -= 7;
        }
        return transitionDay;
    }

    var dstOffset = 0;
    if (adj != null) {
        var dstStart = adj.DaylightTransitionStart.IsFixedDateRule ?
            new Date(dt.getFullYear(), adj.DaylightTransitionStart.Month - 1, adj.DaylightTransitionStart.Day, adj.DaylightTransitionStart.TimeOfDay.getHours()) :
            new Date(dt.getFullYear(), adj.DaylightTransitionStart.Month - 1, GetTransitionDayOfMonth(adj.DaylightTransitionStart, dt.getFullYear()), adj.DaylightTransitionStart.TimeOfDay.getHours());
        var dstEnd = adj.DaylightTransitionEnd.IsFixedDateRule ?
            new Date(dt.getFullYear(), adj.DaylightTransitionEnd.Month - 1, adj.DaylightTransitionEnd.Day, adj.DaylightTransitionEnd.TimeOfDay.getHours()) :
            new Date(dt.getFullYear(), adj.DaylightTransitionEnd.Month - 1, GetTransitionDayOfMonth(adj.DaylightTransitionEnd, dt.getFullYear()), adj.DaylightTransitionEnd.TimeOfDay.getHours());
        if (dstStart <= dt && dstEnd >= dt) {
            dstOffset = adj.DaylightDelta.TotalMinutes;
        }
    }
    var dtcopy = new Date(dt.getTime());
    dtcopy.setMinutes(dtcopy.getMinutes() + clientOffset + tz.BaseUtcOffset.TotalMinutes + dstOffset);
    return dtcopy;  
};
TimeZoneInfo.Get = function(tzid) { return infos[tzid]; };
";


$s4 | out-file $f -Append;


"} (window.TimeZoneInfo = window.TimeZoneInfo || {}));" | out-file $f -Append;

#//TimeZoneInfo.ConvertTime(new Date(2011,7,1,16,45), "Central Standard Time");
#//TimeZoneInfo.ConvertTime(new Date(2011,7,1,16,45), "Eastern Standard Time");
#//TimeZoneInfo.ConvertTime(new Date(2011,7,1,16,45), "South Africa Standard Time");
#notepad.exe $f;

答案 1 :(得分:0)

请勿尝试发现用户的时区。而是在unix时间(UTC 1/1/1970 00:00 UTC)中传递时间并从那里设置它:

d = new Date(); d.setTime(the_time_value);

根据浏览器的本地时区,这将生成一个日期设置为传递给它的时间。

在PHP中,您可以使用$date->getTimestamp();

将日期/时间转换为unix时间

经验法则是,始终以UTC格式存储并传递您的程序(包括浏览器和服务器之间),并尽可能晚地转换为本地日期/时间。