简体   繁体   中英

Finding and consuming the user's timezone in JavaScript's Date constructor

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

The date itself is being provided and formatted via PHP. However, if the user is not logged in, then I do not know what their timezone offset is which is why im being forced to use JavaScript.

How can I find the offset and apply and still keep it all in one line of code?

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;

Don't attempt to discover the user's time zone. Instead pass the time in unix time (seconds 1/1/1970 00:00 UTC) and set it from there:

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

This will produce a Date set to the time you pass to it, according to the browser's local time zone.

In PHP you can convert a date/time to unix time with $date->getTimestamp();

The rule of thumb is, always store and pass your times around your program (including between browser and server) in UTC, and convert to a local date/time as late as is practical.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM