当你的应用大规模启动时,用户的增量就会增加。很可能发生的情况是,用户的位置不仅在同一区域,还可能在具有不同时区的另一个区域。因此作为后端开发人员,与处理时区差异相关的事情是非常需要考虑的。
我最近遇到了一个涉及时区的问题。老实说,处理日期和时间是人类必须处理的最复杂的领域之一。这是我学习如何在服务器端正确处理日期和时间的机会。
在这篇文章中,我将分享我作为后端开发人员如何处理服务器端时区差异的经验。也许如果有人愿意纠正并提供对我有价值的额外意见。
时区是全世界使用的标准时分系统,用于组织和标准化时间的测量。这一概念的出现是为了响应全球时间协调的需求,特别是随着通信和运输技术的发展。
时区的基本原理是将地球划分为24个时区。每个时区通常与其相邻时区相差一小时。时区的主要参考是格林威治标准时间 (GMT) 或协调世界时 (UTC),位于穿过英国格林威治的零度经线。
来自维基共享资源的 Hellerick 插图
一个小例子是,当雅加达的时钟显示中午 12:00 时,纽约的时间显示 00:00 或午夜。这意味着当雅加达人正在享用午餐时,纽约人才刚刚开始新的一天。从这里您当然可以想象在构建应用程序时正确处理时区的重要性。
看完上面的解释后,现在我们将讨论当我们的服务器应用程序收到来自访问我们的 API 来处理其时区的客户端的请求时可以执行的操作。
在本文中,我将讨论在服务器端处理时区的几种方法。这里我将使用Golang(Go)语言的代码示例。 Golang 有一个用于处理与时间相关的数据的时间包,它被认为是相当完整的。以下是我们将讨论的一些要点:
我们要讨论的第一件事是我们将在数据库中保存哪些时间,例如我们有一个进行闪购的电子商务应用程序,我们的应用程序已经在国际范围内。
当用户在美国或印度尼西亚处理购买交易时,用户会将不同的当地时间发送到服务器。问题是,我们的数据库会按照用户当地时间存储时间数据吗?如果答案是肯定的,那么当我们想要检索数据或者例如管理员想要进行数据处理时,这可能是一个复杂的问题,哪个用户最早进行交易。
为了克服这个问题,最佳实践是将交易时间存储在 UTC(协调世界时)时区,这是时钟和时间设置的主要时间标准。这里是将时间应用到 UTC。
package main import ( "fmt" "time" ) func main() { now := time.Now() fmt.Printf("Local time: %s\n", now) nowInUTC := now.UTC() fmt.Printf("UTC time: %s\n", nowInUTC) }
我们看看上面代码的含义。
首先,在 now := time.Now() 代码行中,该行使用 time 包中的 Now() 函数根据系统本地时区获取当前时间。结果存储在当前变量中。
然后,在 nowInUTC := now.UTC() 行中,我们使用 UTC() 方法将本地时间(现在)转换为 UTC 时间。结果存储在一个新的变量nowInUTC中,这个时间可以存储在服务器上,希望开发者能够避免由于不同时区的各个地区的服务器和用户之间的时区差异而可能出现的歧义和错误。
如果我们运行上面的代码,结果如下:
但这并不总是您应该使用的最佳解决方案。在某些用例中,您可能需要记住一些要点,其中之一是我们的用户真的来自不同时区吗?如果不可能,也许以 UTC 形式存储时间会增加代码的复杂性。
在上面的讨论中,我们同意将用户时间数据存储在一个位置,即 UTC。现在用户如何根据自己的位置查看准确的时间。上面讨论的一个例子是我们拥有的电子商务应用程序上的限时抢购,用户还想知道有关哪个用户进行了第一笔交易的信息。所以此时,将我们存储在数据库中的时间转换为用户本地时间是我们不应该忽视的另一件重要的事情。
我采取的方法是服务器端总是要求客户端发送用户端的时区。这可以在请求端完成,客户端发送带有关键时区的标头并具有用户的时区值。例如,印度尼西亚有3个时区划分,分别是WIT(9)、WITA(8)、WIB(7)。其中每个区域有1小时的时差。如果之前在我们的服务器上我们存储的 UTC 时间为 00.00,那么在 WIT 端它为 09.00,然后在 WITA 端它为 08.00,WIB 为 07.00。
下面是处理上述情况的示例代码:
package main import ( "fmt" "time" ) func ParseTimezoneFromString(tz string) *time.Location { if len(tz) > 0 { t, err := time.Parse("2006 -07:00", fmt.Sprintf("2007 %s", tz)) if err != nil { panic(err) } else { return t.Location() } } return time.Now().Location() } func main() { timeServerInUTC := "2024-08-04 00:00:00" nowInUTC, err := time.Parse("2006-01-02 15:04:05", timeServerInUTC) if err != nil { panic(err) } fmt.Printf("UTC time: %s\n", nowInUTC) witLocation := ParseTimezoneFromString(" 09:00") nowInWIT := nowInUTC.In(witLocation) fmt.Printf("WIT time: %s\n", nowInWIT) witaLocation := ParseTimezoneFromString(" 08:00") nowInWITA := nowInUTC.In(witaLocation) fmt.Printf("WITA time: %s\n", nowInWITA) wibLocation := ParseTimezoneFromString(" 07:00") nowInWIB := nowInUTC.In(wibLocation) fmt.Printf("WIB time: %s\n", nowInWIB) }
归功于 dikac 创建此函数 ParseTimezoneFromString
我们来理解一下上面代码的含义:
首先,我们创建一个函数 ParseTimezoneFromString,该函数用于根据参数 tz 或给定用户位置的时区查找时间位置。如果字符串值 tz 有效,我们将使用 time.Parse 函数转换字符串的时区,将字符串转换为时间对象,然后从该对象中提取位置(时区)。我们还处理如果字符串为空或解析失败,函数返回系统的本地时区。
func ParseTimezoneFromString(tz string) *time.Location { if len(tz) > 0 { t, err := time.Parse("2006 -07:00", fmt.Sprintf("2007 %s", tz)) if err != nil { panic(err) } else { return t.Location() } } return time.Now().Location() }
接下来我们还定义以下字符串格式的时间数据:
timeServerInUTC := "2024-08-04 00:00:00" nowInUTC, err := time.Parse("2006-01-02 15:04:05", timeServerInUTC) if err != nil { panic(err) }
您可以将其视为我们从服务器获取的计时数据。并将其解析为时间对象。
接下来,我们尝试根据我们之前根据定义的字符串参数创建的 ParseTimezoneFromString 函数来查找用户的准确位置。需要注意的是,这个字符串参数是客户端通过传入请求发送的时区标头的值的含义。
我们可以使用从 ParseTimezoneFromString 函数获取的位置将从服务器获取的时间转换或转移到用户的本地时间。我们可以使用 In 函数来完成此操作,该函数也包含在 time 包中,我们可以在以下代码片段中看到:
nowInWIT := nowInUTC.In(witLocation) nowInWITA := nowInUTC.In(witaLocation) nowInWIB := nowInUTC.In(wibLocation)
如果我们运行它,我们将获得与我们定义的时区位置相对应的时间。
最后一点同样重要,即测试和验证。当开发过程经常导致开发人员犯下意想不到的错误时,测试和验证总是很重要。
在上面第 2 点的讨论中,ParseTimezoneFromString 函数在处理时区方面非常重要。重复测试和验证对于使我们的应用程序获得满足我们期望的结果非常重要。
测试时,建议使用单元测试,在最小的单元上进行测试,并可以添加多个场景。场景越多,处理这些时间差异的可能性就越小。
处理时区对于后端开发人员来说确实很棘手。然而,重要的是要记住,我们克服的每一项具有挑战性的任务都有助于我们的成长和技能提高。正确管理时区不仅仅是技术上的需要,它还能确保调度的准确性,并为不同地区的应用程序用户提供流畅的体验。
本文中分享的有关以 UTC 格式存储时间、转换为用户本地时间以及实现强大的转换函数的要点是解决这一复杂问题的起点。然而,我承认所讨论的方法可能存在缺点或需要改进的地方。这就是为什么开发者社区的额外意见和建议非常宝贵。
我真诚地希望本文中提供的见解和代码示例能够在您将来在项目中遇到与时区相关的挑战时对您有所帮助。请记住,我们的目标是创建能够为用户无缝工作的应用程序,无论他们的地理位置如何。
让我们在下面的评论部分继续讨论。我很想听听您处理时区的经验、您遇到的任何挑战,或者您发现有效的替代方法。您的见解对于我和其他面临类似问题的读者来说非常有价值。
感谢您的阅读,希望本文对您的开发之旅有所帮助。让我们一起不断学习、共同进步! ?
免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。
Copyright© 2022 湘ICP备2022001581号-3