Cách khởi tạo Ngày JavaScript theo múi giờ cụ thể


232

Tôi có thời gian ngày trong một múi giờ cụ thể dưới dạng chuỗi và tôi muốn chuyển đổi thời gian này thành giờ địa phương. Nhưng, tôi không biết cách đặt múi giờ trong đối tượng Ngày.

Ví dụ, tôi có Feb 28 2013 7:00 PM ET,sau đó tôi có thể

var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);  

Theo tôi biết, tôi có thể đặt giờ UTC hoặc giờ địa phương. Nhưng, làm cách nào để đặt thời gian ở múi giờ khác?

Tôi đã cố gắng sử dụng phép cộng / trừ phần bù từ UTC nhưng tôi không biết làm thế nào để chống lại sự tiết kiệm ánh sáng ban ngày. Tôi không chắc chắn nếu tôi đang đi đúng hướng.

Làm cách nào tôi có thể chuyển đổi thời gian từ múi giờ khác sang giờ địa phương trong javascript?



1
Các đối tượng ngày không có múi giờ, chúng là UTC.
RobG

Câu trả lời:


352

Lý lịch

DateĐối tượng của JavaScript theo dõi thời gian trong UTC trong nội bộ, nhưng thường chấp nhận đầu vào và tạo đầu ra theo thời gian cục bộ của máy tính mà nó đang chạy. Nó có rất ít phương tiện để làm việc với thời gian ở các múi giờ khác.

Biểu diễn bên trong của một Dateđối tượng là một số duy nhất, biểu thị số mili giây đã trôi qua kể từ đó 1970-01-01 00:00:00 UTC, không liên quan đến giây nhuận. Không có múi giờ hoặc định dạng chuỗi được lưu trữ trong chính đối tượng Date. Khi các chức năng khác nhau của Dateđối tượng được sử dụng, múi giờ địa phương của máy tính được áp dụng cho biểu diễn bên trong. Nếu hàm tạo ra một chuỗi, thì thông tin ngôn ngữ của máy tính có thể được xem xét để xác định cách tạo ra chuỗi đó. Các chi tiết khác nhau cho mỗi chức năng, và một số là cụ thể thực hiện.

Các hoạt động duy nhất mà Dateđối tượng có thể thực hiện với các múi giờ không cục bộ là:

  • Nó có thể phân tích một chuỗi chứa phần bù UTC số từ bất kỳ múi giờ nào. Nó sử dụng điều này để điều chỉnh giá trị được phân tích cú pháp và lưu trữ tương đương UTC. Thời gian cục bộ ban đầu và bù không được giữ lại trong Dateđối tượng kết quả . Ví dụ:

    var d = new Date("2020-04-13T00:00:00.000+08:00");
    d.toISOString()  //=> "2020-04-12T16:00:00.000Z"
    d.valueOf()      //=> 1586707200000  (this is what is actually stored in the object)
  • Trong các môi trường đã triển khai API quốc tế hóa ECMASCript (còn gọi là "Intl"), một Dateđối tượng có thể tạo ra một chuỗi cụ thể theo miền địa phương được điều chỉnh theo một mã định danh múi giờ nhất định. Điều này được thực hiện thông qua timeZonetùy chọn toLocaleStringvà các biến thể của nó. Hầu hết các triển khai sẽ hỗ trợ các định danh múi giờ IANA, chẳng hạn như 'America/New_York'. Ví dụ:

    var d = new Date("2020-04-13T00:00:00.000+08:00");
    d.toLocaleString('en-US', { timeZone: 'America/New_York' })
    //=> "4/12/2020, 12:00:00 PM"
    // (midnight in China on Apring 13th is noon in New York on April 12th)

    Hầu hết các môi trường hiện đại đều hỗ trợ bộ định danh múi giờ IANA đầy đủ ( xem bảng tương thích tại đây ). Tuy nhiên, hãy nhớ rằng số nhận dạng duy nhất cần được hỗ trợ bởi Intl là 'UTC', do đó bạn nên kiểm tra cẩn thận nếu bạn cần hỗ trợ các trình duyệt cũ hơn hoặc môi trường không điển hình (ví dụ: các thiết bị IoT nhẹ).

Thư viện

Có một số thư viện có thể được sử dụng để làm việc với các múi giờ. Mặc dù chúng vẫn không thể làm cho Dateđối tượng hoạt động khác đi, nhưng chúng thường triển khai cơ sở dữ liệu múi giờ IANA tiêu chuẩn và cung cấp các hàm để sử dụng nó trong JavaScript. Các thư viện hiện đại sử dụng dữ liệu múi giờ được cung cấp bởi API quốc tế, nhưng các thư viện cũ hơn thường có chi phí hoạt động, đặc biệt nếu bạn đang chạy trong trình duyệt web, vì cơ sở dữ liệu có thể hơi lớn. Một số thư viện này cũng cho phép bạn giảm có chọn lọc tập dữ liệu, theo đó múi giờ được hỗ trợ và / hoặc theo phạm vi ngày bạn có thể làm việc.

Dưới đây là các thư viện để xem xét:

Thư viện dựa trên quốc tế

Phát triển mới nên chọn một trong những triển khai này, dựa trên API quốc tế cho dữ liệu múi giờ của họ:

Thư viện phi quốc tế

Các thư viện này được duy trì, nhưng mang gánh nặng đóng gói dữ liệu múi giờ của riêng họ, có thể khá lớn.

* Mặc dù Thời điểm và Mốc thời gian đã được đề xuất trước đây, nhóm Hiện tại thích người dùng chọn Luxon để phát triển mới.

Thư viện ngừng hoạt động

Các thư viện này đã chính thức ngừng hoạt động và không còn được sử dụng.

Đề xuất trong tương lai

Các đề xuất TC39 Temporal nhằm mục đích cung cấp một tập mới của các đối tượng tiêu chuẩn để làm việc với ngày tháng và thời gian bằng ngôn ngữ Javascript chính nó. Điều này sẽ bao gồm hỗ trợ cho một đối tượng nhận thức múi giờ.


1
Vui lòng thay đổi "đại diện" thành "đầu ra / phân tích", vì các dấu thời gian được đại diện là độc lập với múi giờ
Bergi

1
@Bergi - Tôi đã suy nghĩ lại về điều này và đồng ý với bạn. Cập nhật câu trả lời của tôi cho phù hợp.
Matt Johnson-Pint

1
Khi bạn thực hiện việc này trong bảng điều khiển Fireorms : var date_time = new Date(), giá trị date_timelà (đối với tôi trong AEST) Date {Sun Aug 21 2016 22:18:47 GMT+1000 (AEST)}và do đó có vẻ như nó đã lưu trữ múi giờ. Làm cách nào tôi có thể đảm bảo date_timehoàn toàn là thời gian UTC, không có múi giờ bao gồm?
dùng1063287

Hmm, theo câu trả lời này ( stackoverflow.com/a/8047885/1063287 ), đây là: new Date().getTime();
user1063287

1
@ user1063287, phương thức toString sử dụng phần bù múi giờ của máy chủ để tạo ngày và giờ trong múi giờ "cục bộ". Bản thân đối tượng ngày có giá trị thời gian là phần bù từ 1970-01-01T00: 00: 00Z, do đó, UTC rất hiệu quả. Để xem ngày và giờ của UTC, hãy sử dụng toISOString .
RobG

69

Như Matt Johnson đã nói

Nếu bạn có thể giới hạn việc sử dụng của mình đối với các trình duyệt web hiện đại, giờ đây bạn có thể thực hiện các thao tác sau mà không cần bất kỳ thư viện đặc biệt nào:

new Date().toLocaleString("en-US", {timeZone: "America/New_York"})

Đây không phải là một giải pháp toàn diện, nhưng nó hoạt động cho nhiều tình huống chỉ yêu cầu chuyển đổi đầu ra (từ UTC hoặc giờ địa phương sang múi giờ cụ thể, nhưng không phải theo hướng khác).

Vì vậy, mặc dù trình duyệt không thể đọc múi giờ IANA khi tạo ngày hoặc có bất kỳ phương pháp nào để thay đổi múi giờ trên đối tượng Ngày hiện tại, dường như có một hack xung quanh nó:

function changeTimezone(date, ianatz) {

  // suppose the date is 12:00 UTC
  var invdate = new Date(date.toLocaleString('en-US', {
    timeZone: ianatz
  }));

  // then invdate will be 07:00 in Toronto
  // and the diff is 5 hours
  var diff = date.getTime() - invdate.getTime();

  // so 12:00 in Toronto is 17:00 UTC
  return new Date(date.getTime() + diff);

}

// E.g.
var there = new Date();
var here = changeTimezone(there, "America/Toronto");

console.log(`Here: ${here.toString()}\nToronto: ${there.toString()}`);


6
Tôi đang tự hỏi tại sao câu trả lời này không nhận được nhiều tình yêu hơn. Đối với mục đích của tôi, nó hoàn toàn là giải pháp tốt nhất. Trong ứng dụng của tôi, tất cả các ngày là UTC, nhưng chúng cần phải là đầu vào hoặc đầu ra trong các múi giờ IANA rõ ràng trong UI. Đối với mục đích này, tôi sử dụng giải pháp này và nó hoạt động hoàn hảo. Đơn giản, hiệu quả, chuẩn mực.
logidelic

2
@logidelic nó là một bài khá mới. Thành thật mà nói, tôi ngạc nhiên khi tôi nghĩ rằng bạn có thể sử dụng toLocaleStringđể đạt được hiệu ứng ngược, và vâng, đây phải là kiến ​​thức phổ biến! hãy viết một bài báo về nó và đề cập đến tôi :-)
commonpike 11/2/19

1
Đối với môi trường nút chưa có trong GMT, đoạn mã trên cần thay đổi từ var invdate = new Date (date.toLocaleString ('en-US', {timeZone: ianatz})); vào var invdate = ngày mới ($ {date.toLocaleString ('en-US', {timeZone: ianatz})} GMT`);
Mike P.

5
Các trình duyệt không bắt buộc phải phân tích đầu ra của toLocaleString , do đó, nó dựa trên tiền đề bị lỗi.
RobG

3
"... tại sao câu trả lời này không nhận được nhiều tình yêu hơn?" - bởi vì Dateđối tượng mà nó trả lại là một lời nói dối. Ngay cả ví dụ được hiển thị, đầu ra chuỗi bao gồm múi giờ của máy cục bộ. Trên máy tính của tôi, cả hai kết quả đều ghi "GMT-0700 (Giờ ban ngày Thái Bình Dương)", trong đó chỉ đúng với lần đầu tiên (Toronto thực sự là theo Giờ phương Đông.) Ngoài đầu ra chuỗi, dấu thời gian được giữ trong Dateđối tượng đã được chuyển sang một thời điểm khác nhau. Đây có thể là một kỹ thuật hữu ích, nhưng đi kèm với rất nhiều cảnh báo - chủ yếu là các Datehàm đối tượng sẽ không có đầu ra bình thường.
Matt Johnson-Pint

30

Bạn có thể chỉ định bù múi giờ trên new Date(), ví dụ:

new Date('Feb 28 2013 19:00:00 EST')

hoặc là

new Date('Feb 28 2013 19:00:00 GMT-0500')

Datelưu trữ thời gian UTC (tức là getTimetrả về trong UTC), javascript sẽ chuyển đổi thời gian thành UTC và khi bạn gọi những thứ như toStringjavascript sẽ chuyển đổi thời gian UTC thành múi giờ cục bộ của trình duyệt và trả về chuỗi theo múi giờ địa phương, tức là nếu tôi đang sử dụng UTC+8:

> new Date('Feb 28 2013 19:00:00 GMT-0500').toString()
< "Fri Mar 01 2013 08:00:00 GMT+0800 (CST)"

Ngoài ra, bạn có thể sử dụng getHours/Minute/Secondphương pháp bình thường :

> new Date('Feb 28 2013 19:00:00 GMT-0500').getHours()
< 8

(Điều này 8có nghĩa là sau khi thời gian được chuyển đổi thành giờ địa phương của tôi - UTC+8, số giờ là 8.)


16
Phân tích cú pháp bất kỳ định dạng nào ngoài định dạng mở rộng ISO 8601 đều phụ thuộc vào việc triển khai và không nên dựa vào. Không có tiêu chuẩn cho chữ viết tắt múi giờ, ví dụ "EST" có thể đại diện cho bất kỳ một trong 3 khu vực khác nhau.
RobG

1
Các ví dụ trong nhận xét này thường không hoạt động trong trình duyệt internet explorer. Như đã đề cập trong bình luận trước cho bài viết này, ISO 8601 rất quan trọng. Tôi đã xác nhận bằng cách đọc Đặc tả ngôn ngữ phiên bản ECMA-262 (Javascript 5).
Dakusan

12

Điều này sẽ giải quyết vấn đề của bạn, xin vui lòng cung cấp sửa chữa. Phương pháp này cũng sẽ tính đến thời gian tiết kiệm ánh sáng ban ngày cho ngày đã cho.

dateWithTimeZone = (timeZone, year, month, day, hour, minute, second) => {
  let date = new Date(Date.UTC(year, month, day, hour, minute, second));

  let utcDate = new Date(date.toLocaleString('en-US', { timeZone: "UTC" }));
  let tzDate = new Date(date.toLocaleString('en-US', { timeZone: timeZone }));
  let offset = utcDate.getTime() - tzDate.getTime();

  date.setTime( date.getTime() + offset );

  return date;
};

Cách sử dụng với múi giờ và giờ địa phương:

dateWithTimeZone("America/Los_Angeles",2019,8,8,0,0,0)

Giải pháp này phù hợp với nhu cầu của tôi. Tôi không chắc chính xác làm thế nào tôi có thể tính đến việc tiết kiệm ánh sáng ban ngày.
Hossein Amin

Tại sao không trả lại tzDatetrực tiếp?
levi

8

Tôi tìm thấy cách được hỗ trợ nhiều nhất để làm điều này, mà không phải lo lắng về thư viện của bên thứ ba, bằng cách sử dụng getTimezoneOffsetđể tính dấu thời gian thích hợp hoặc cập nhật thời gian sau đó sử dụng các phương pháp bình thường để có được ngày và giờ cần thiết.

var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);

// ET timezone offset in hours.
var timezone = -5;
// Timezone offset in minutes + the desired offset in minutes, converted to ms.
// This offset should be the same for ALL date calculations, so you should only need to calculate it once.
var offset = (mydate.getTimezoneOffset() + (timezone * 60)) * 60 * 1000;

// Use the timestamp and offset as necessary to calculate min/sec etc, i.e. for countdowns.
var timestamp = mydate.getTime() + offset,
    seconds = Math.floor(timestamp / 1000) % 60,
    minutes = Math.floor(timestamp / 1000 / 60) % 60,
    hours   = Math.floor(timestamp / 1000 / 60 / 60);

// Or update the timestamp to reflect the timezone offset.
mydate.setTime(mydate.getTime() + offset);
// Then Output dates and times using the normal methods.
var date = mydate.getDate(),
    hour = mydate.getHours();

BIÊN TẬP

Trước đây tôi đã sử dụng UTCcác phương thức khi thực hiện các phép biến đổi ngày, không chính xác. Với việc thêm phần bù vào thời gian, sử dụng các gethàm cục bộ sẽ trả về kết quả mong muốn.


Bạn tính ở timezone_offsetđâu?
Anand Somani

1
Rất tiếc, tôi đã đánh dấu sai một trong các biến của mình. timezone_offsetnên có offset, hoặc ngược lại. Tôi đã chỉnh sửa câu trả lời của mình để hiển thị tên biến chính xác.
Gà trống Shaun

3

Tôi gặp phải một vấn đề tương tự với các bài kiểm tra đơn vị (cụ thể là trong trò đùa khi bài kiểm tra đơn vị chạy cục bộ để tạo ảnh chụp nhanh và sau đó máy chủ CI chạy trong (có khả năng) một múi giờ khác khiến việc so sánh ảnh chụp không thành công). Tôi đã chế giễu chúng tôi Datevà một số phương pháp hỗ trợ như vậy:

describe('...', () => {
  let originalDate;

  beforeEach(() => {
    originalDate = Date;
    Date = jest.fn(
      (d) => {
        let newD;
        if (d) {
          newD = (new originalDate(d));
        } else {
          newD = (new originalDate('2017-05-29T10:00:00z'));
        }
        newD.toLocaleString = () => {
          return (new originalDate(newD.valueOf())).toLocaleString("en-US", {timeZone: "America/New_York"});
        };
        newD.toLocaleDateString = () => {
          return (new originalDate(newD.valueOf())).toLocaleDateString("en-US", {timeZone: "America/New_York"});
        };
        newD.toLocaleTimeString = () => {
          return (new originalDate(newD.valueOf())).toLocaleTimeString("en-US", {timeZone: "America/New_York"});
        };
        return newD;
      }
    );
    Date.now = () => { return (Date()); };
  });

  afterEach(() => {
    Date = originalDate;
  });

});


2

Dựa trên các câu trả lời ở trên, tôi đang sử dụng một lớp lót gốc này để chuyển đổi chuỗi múi giờ dài thành chuỗi ba chữ cái:

var longTz = 'America/Los_Angeles';
var shortTz = new Date().
    toLocaleString("en", {timeZoneName: "short", timeZone: longTz}).
    split(' ').
    pop();

Điều này sẽ cung cấp cho PDT hoặc PST tùy thuộc vào ngày được cung cấp. Trong trường hợp sử dụng cụ thể của tôi, phát triển trên Salesforce (Aura / Lightning), chúng tôi có thể lấy múi giờ của người dùng ở định dạng dài từ phụ trợ.


Thỉnh thoảng, vì tôi thích chơi trò chơi trẻ con, 'America / Los_Angele' không phải là múi giờ, đó là "vị trí đại diện" cho một quy tắc tiết kiệm và ánh sáng ban ngày cụ thể và lịch sử của những thay đổi đó (theo cơ sở dữ liệu múi giờ IANA ) . ;-)
RobG

Haha, bạn phải đăng nhận xét đó về nhiều câu trả lời tại đây: P
Shane

2

Tôi biết 3 năm đã quá muộn, nhưng có lẽ nó có thể giúp đỡ người khác vì tôi chưa tìm thấy thứ gì như vậy ngoại trừ thư viện múi giờ, không chính xác như những gì anh ta yêu cầu ở đây.

Tôi đã làm một cái gì đó tương tự cho múi giờ của Đức, điều này hơi phức tạp vì tiết kiệm thời gian ban ngày và những năm nhuận mà bạn có 365 ngày.

nó có thể cần một chút công việc với chức năng "isDaylightSavingTimeInGermany" trong khi các múi giờ khác nhau thay đổi vào các thời điểm khác nhau trong thời gian tiết kiệm ánh sáng ban ngày.

dù sao, hãy xem trang này: https://github.com/zerkotin/german-timezone-converter/wiki

các phương thức chính là: convertLocalDateToGermanTimezone convertGermanDateToLocalTimezone

Tôi đã nỗ lực ghi lại nó, vì vậy nó sẽ không gây nhầm lẫn.


Đây phải là một nhận xét, nó không phải là một câu trả lời.
RobG

1

Hãy thử: ngày từ múi giờ , nó giải quyết ngày dự kiến ​​với sự giúp đỡ có sẵn Intl.DateTimeFormat.

Tôi đã sử dụng phương pháp đó trong một trong những dự án của mình được vài năm rồi, nhưng giờ tôi quyết định xuất bản nó thành dự án HĐH nhỏ :)


0

Đối với người dùng Ionic, tôi rất thích điều này vì .toISOString()phải sử dụng mẫu html.

Điều này sẽ lấy ngày hiện tại, nhưng tất nhiên có thể được thêm vào câu trả lời trước cho một ngày đã chọn.

Tôi đã sửa nó bằng cách này:

date = new Date();
public currentDate: any = new Date(this.date.getTime() - this.date.getTimezoneOffset()*60000).toISOString();

* 60000 đang chỉ ra UTC -6 là CST nên bất kể TimeZone là gì, số lượng và chênh lệch đều có thể thay đổi.


Đây không phải là câu trả lời mà người hỏi đang tìm kiếm. Ông đang tìm cách để có được ngày đến múi giờ cụ thể.
Richard Vergis

@RichardVergis Câu hỏi nêu rõ người dùng muốn đổi sang giờ địa phương, nhưng không nêu rõ múi giờ họ đang ở. Tôi đã nêu múi giờ của tôi làm ví dụ và người dùng có thể đọc rõ ràng dựa trên ví dụ của tôi, họ có thể nhập bù múi giờ của họ.
Stephen Romero

0

Có lẽ điều này sẽ giúp bạn

/**
 * Shift any Date timezone.
 * @param {Date} date - Date to update.
 * @param {string} timezone - Timezone as `-03:00`.
 */
function timezoneShifter(date, timezone) {
  let isBehindGTM = false;
  if (timezone.startsWith("-")) {
    timezone = timezone.substr(1);
    isBehindGTM = true;
  }

  const [hDiff, mDiff] = timezone.split(":").map((t) => parseInt(t));
  const diff = hDiff * 60 + mDiff * (isBehindGTM ? 1 : -1);
  const currentDiff = new Date().getTimezoneOffset();

  return new Date(date.valueOf() + (currentDiff - diff) * 60 * 1000);
}



const _now = new Date()
console.log(
  [
    "Here: " + _now.toLocaleString(),
    "Greenwich: " + timezoneShifter(_now, "00:00").toLocaleString(),
    "New York: " + timezoneShifter(_now, "-04:00").toLocaleString(),
    "Tokyo: " + timezoneShifter(_now, "+09:00").toLocaleString(),
    "Buenos Aires: " + timezoneShifter(_now, "-03:00").toLocaleString(),
  ].join('\n')
);


-2

Đã đối mặt với cùng một vấn đề, sử dụng cái này

Console.log (Date.parse ("ngày 13 tháng 6 năm 2018 10:50:39 GMT + 1"));

Nó sẽ trả về một phần nghìn giây mà bạn có thể kiểm tra có +100 timzone nội tâm hóa thời gian của Anh Hy vọng nó sẽ giúp !!


3
(Bài đăng này dường như không cung cấp câu trả lời chất lượng cho câu hỏi. Vui lòng chỉnh sửa câu trả lời của bạn hoặc chỉ đăng bài dưới dạng nhận xét cho câu hỏi).
sɐunıɔ qɐp
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.