使用 Shiny 时如何检索客户端的当前时间和时区?

Posted

技术标签:

【中文标题】使用 Shiny 时如何检索客户端的当前时间和时区?【英文标题】:How to retrieve the client's current time and time zone when using Shiny? 【发布时间】:2014-09-10 14:36:16 【问题描述】:

我想知道是否有一些巧妙的方法可以让客户获得当前时间和时区,以便在 Shiny 应用程序的 server.R 部分中使用它。如果没有,最简单的方法是什么?

【问题讨论】:

【参考方案1】:

我发现了一种有效的方法,它只是对 Reading javascript variable into shiny/R on app load 的 *** 答案的一个小修改。它不检索实际时区,而是检索时区偏移量。

在 ui.R

html('<input type="text" id="client_time" name="client_time" style="display: none;"> '),
HTML('<input type="text" id="client_time_zone_offset" name="client_time_zone_offset" style="display: none;"> '),

tags$script('
  $(function() 
    var time_now = new Date()
    $("input#client_time").val(time_now.getTime())
    $("input#client_time_zone_offset").val(time_now.getTimezoneOffset())
  );    
')

上面创建了两个divs,javascript代码检索客户端时间和时区偏移并将它们放在divs中。

在服务器.R

client_time <- reactive(as.numeric(input$client_time) / 1000) # in s
time_zone_offset <- reactive(as.numeric(input$client_time_zone_offset) * 60 ) # in s 

上面创建了两个可以在服务器代码中使用的反应变量。为了便于处理,我还将input$client_time 从 ms 转换为 s,将 input$client_time_zone_offset 从 min 转换为 s。

【讨论】:

【参考方案2】:

在你发帖之前我才听说过 Shiny。通读文档,看起来 Shiny 应用程序的客户端部分是用 R 编写的,但随后呈现为 ​​HTML/CSS/JavaScript,因此它可以在浏览器中运行。您要求的信息必须来自 JavaScript。

在 JavaScript 中获取当前时间非常简单:

var now = new Date();

结果是一个Date 对象,它具有来自客户端时钟的当前日期和时间。在内部,它被跟踪为自 1970 年 1 月 1 日午夜 UTC 以来的 UTC 时间(以毫秒为单位)。但是,Date 对象在生成输出(例如使用.toString())或使用许多其他功能时会考虑客户端的本地时区。您可以在MDN reference documentation 中阅读有关Date 对象的更多信息。

现在,如果您确实需要客户端的时区,那就另当别论了。 Date 对象只能使用.getTimezoneOffset() 函数为您提供特定日期和时间的时区偏移。例如,您可以判断客户端当前比 UTC 晚 420 分钟 (UTC-07:00),但您无法判断客户端位于 America/Los_Angeles 时区 - 在 UTC-07:00 和 UTC 之间交替-08:00 为夏令时。在the timezone tag wiki 中阅读更多内容。

有一个 JavaScript 库 jsTimeZoneDetect 会尝试猜测时区,并且做得相当不错。

所以 - 现在的问题是,如何从 R 中的 Shiny 应用程序调用自定义 JavaScript?我不是这方面的专家,但this part of the Shiny documentation 似乎涵盖了它。

所有这些都将在客户端完成。然后,您必须将其发送到服务器以使用应用程序的 server.R 部分中的信息。

【讨论】:

【参考方案3】:

您可以通过套接字通信获取它,但是开销很大,或者如果您的客户端之间的时间是同步的,您可以信任您的本地时间,获取他们的时区,评估服务器和客户端之间的时区差异,加上/减去差异,那么您无需太多工作即可获取他们的时间和时区。 (不知何故,但在大多数情况下,这样做会由于计算机跟上而损失 2 到 3 秒的精度)

【讨论】:

【参考方案4】:

另一种方法:随时向客户端发送消息并使用输入对象检索结果。您还将测量延迟。 (或者如果用户在闪亮会话期间更改了时区......)。

获取服务器时间和时区,通过sendCustomMessage发送给客户端。

# R
triggerClientTime <- function(session=shiny::getDefaultReactiveDomain())
  serverTime <- Sys.time()
  serverTimeZone <- as.integer(strftime(serverTime,"%z"))/100
session$sendCustomMessage(
  type="getClientTime",
  message=list(
    serverPosix = as.numeric(serverTime),
    serverTimeZone = serverTimeZone
    )
  )

将此函数放入服务器文件中。

在 javascript 中,获取消息,检索时间和区域偏移,将其作为新的输入条目发送回服务器:

// js
  Shiny.addCustomMessageHandler("getClientTime",
      function(s)
        var d = new Date()
        var clientPosix = parseInt(d.getTime()/1000);
        var clientTimeZone = -(d.getTimezoneOffset() / 60);
        var res =  
          serverPosix:s.serverPosix,
          serverTimeZone:s.serverTimeZone,
          clientPosix:clientPosix,
          clientTimeZone:clientTimeZone
        
        Shiny.onInputChange("clientTime",res)
      )

将此代码放在脚本标签或单独的js文件中。

在闪亮的会话中:

# Observe and print time from client and server
  observe( 
    print(input$clientTime)
  )
# Ask the client for current time and time zone (hours from UTC)
  triggerClientTime()

它应该输出如下内容:

# output
$serverPosix
[1] 1449827815

$serverTimeZone
[1] 1

$clientPosix
[1] 1449827816

$clientTimeZone
[1] 1

【讨论】:

【参考方案5】:

我们遇到的一个问题是我们使用生理数据并希望将此数据与用户填写的日历数据同步。生理数据总是带有 UTC 时间的日期时间戳(之后可以根据您自己的本地时区添加时间戳),而典型的日历数据是本地时钟时间。

对于本地 R 会话,这不会导致任何问题。日历时钟时间数据从您的本地计算机读取时区 (Sys.timezone()),然后附加 UTC 日期时间戳(然后也可以使用您的本地时区进行时间戳)。

但是,在 Shiny 服务器 (shinyapps.io) 上,这会导致不需要的行为。 Shiny 服务器使用 UTC 时间,而日历时钟时间数据是在用户的时区中填写的。这实际上意味着我们想要调整的日历时钟时间存在未知的偏移量(Shinyapps.io 不提供用户时区)。据我所知,Shinyapps.io 没有检测用户时区的方法(但如果我错了,请纠正我)。

基于查找用户时区的几个答案,我创建了一个闪亮的小应用程序来根据用户的浏览器获取用户时区。也许它可能对其他人有用,因为我花了一些时间才弄清楚这一点。

长话短说:将 Shiny 代码复制并粘贴到 Rscript 中,以根据用户的浏览器获取用户的 unix 时间、偏移量和时区

library(shiny)
runApp(list(
  ui = bootstrapPage(

tags$script('
  $(function() 
    var time_now = new Date()
    $("input#client_time").val(time_now.getTime())
    $("input#client_time_zone_offset").val(time_now.getTimezoneOffset())
    $("input#client_time_zone_char").val(time_now.toTimeString())
    $("input#client_time_zone_international").val(Intl.DateTimeFormat().resolvedOptions().timeZone)
  );    
'),

textInput("client_time", "Client Time", value = ""),
textInput("client_time_zone_offset", "Time zone offset", value = ""),
textInput("client_time_zone_char", "Time zone verbatim", value = ""),
textInput("client_time_zone_international", "Time zone international", value = "")

),
server = function(input, output) 

  client_time <- reactive(as.numeric(input$client_time) / 1000) # in s
  client_time_zone_offset <- reactive(as.numeric(input$client_time_zone_offset) * 60 ) # in s 
  client_time_zone_char <- reactive(input$client_time_zone_char)
  client_time_zone_international <- reactive(input$client_time_zone_international)
  
  observe(
    paste(input$client_time, sep = "; ")
    paste(input$client_time_zone_offset, sep = "; ")
    paste(input$client_time_zone_char, sep = "; ")
    paste(input$client_time_zone_international, sep = "; ")
    
    # Capture timezone in vector
    browser_tz <- paste(input$client_time_zone_international, sep = "; ")
    
    )


))

【讨论】:

以上是关于使用 Shiny 时如何检索客户端的当前时间和时区?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Google AppEngine XMPP 库查询客户端的时区

如何检测客户端的时区?

如何在 Java 的预期时区中检索 oracle Timestamp 列?

R Shiny 如何在根据用户选择从 mysqlDB 检索数据时使用 renderPlot 构建条形图

检索 Lync 客户端的呼叫转接(路由)规则

如何使用js将UTC日期转换为本地时间? [复制]