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

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

我怎样才能找到偏移量并应用并且仍然将其全部保存在一行代码中?

4

2 回答 2

1

Sorry, dude. Dates are hard.

When possible, let the server format your date strings and send them down to the browser with the web page or ajax response or whatever.

If really, really, really want to do time zone conversions on the client in javascript, you've got to send the timezone rules you're interested in down to the client. If you have a Windows machine, below is a powershell script that will generate a javascript file with timezone rules and a function to do the conversions. I used this when I needed to convert dates from a 3rd party web api to a timezone different than the browser's time zone.

You should only go this approach if you need to get a date object into a timezone other than the browser's timezone or utc. Even then, you might consider a web service to format the dates for you as an alternative.

# 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;
于 2012-04-10T22:13:23.120 回答
0

不要试图发现用户的时区。而是以 unix 时间(秒 1/1/1970 00:00 UTC)传递时间并从那里设置:

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

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

在 PHP 中,您可以将日期/时间转换为 unix 时间$date->getTimestamp();

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

于 2012-04-10T22:19:26.207 回答