【问题标题】:filtering a json set of data when dateformat is a string当 dateformat 为字符串时过滤 json 数据集
【发布时间】:2019-12-17 12:18:19
【问题描述】:

假设你有一个像这样的 json 响应字符串,并且你想按日期过滤。 您只想在特定日期之后显示记录。

[{btc=0, datetime=2018-01-30 12:16:11, eur=410.00, fee=0.00, usd=0, id=***, btc_usd=0.00, type=0}, {btc=0, eth_eur=692.81, datetime=2018-02-06 16:45:16, eur=-5.20, fee=0.02, usd=0, eth=0.00750000, id=***, type=2, order_id=***}, {btc=0, eth_eur=699, datetime=2018-02-05 17:04:25, eur=5.24, fee=0.02000000, usd=0, eth=-0.00750000, id=***, type=2, order_id=***}, {btc=0, eth_eur=562.1, datetime=2018-02-04 17:52:09, eur=-5.62, fee=0.02, usd=0, eth=0.01000000, id=***, type=2, order_id=***}, {btc=0, eth_eur=567.2, datetime=2018-02-06 20:24:07, eur=5.67, fee=0.02000000, usd=0, eth=-0.01000000, id=***, type=2, order_id=***}, {btc=0, datetime=2018-04-28 17:22:21, eur=0, fee=0.00000000, usd=0, eth=0.38811442, id=***, btc_usd=0.00, type=0}, {btc=0, eth_eur=563, datetime=2018-04-28 17:42:51, eur=218.50, fee=0.55000000, usd=0, eth=-0.38810000, id=***, type=2, order_id=***}, {btc=0, eth_eur=563, datetime=2018-04-28 17:47:52, eur=0.01, fee=0.01000000, usd=0, eth=-0.00001442, id=***, type=2, order_id=***}, {btc=0.01786568, btc_eur=5583.33, datetime=2018-08-14 12:01:13, eur=-99.75, fee=0.25, usd=0, id=***, type=2, order_id=***}]

类似情况

{btc_available=0.01489932, eth_reserved=0.00000000, eur_balance=1858.63, btcusd_fee=0.500, xrpeur_fee=0.500, btc_balance=0.01489932, xrp_withdrawal_fee=0.02000000, ethusd_fee=0.500, ltceur_fee=0.500, eth_balance=1.30423351, xrp_reserved=0.00000000, bchusd_fee=0.500, eur_reserved=0.00, bch_available=0.00000000, usd_available=0.18, xrp_available=328.75000000, xrpusd_fee=0.500, ltcbtc_fee=0.500, bcheur_fee=0.500, ltc_available=0.00000000, btc_reserved=0.00000000, ltc_withdrawal_fee=0.00100000, usd_reserved=0.00, btc_withdrawal_fee=0.00050000, eurusd_fee=0.500, xrp_balance=328.75000000, ltcusd_fee=0.500, ltc_balance=0.00000000, bch_reserved=0.00000000, bch_withdrawal_fee=0.00010000, eur_available=1858.63, ltc_reserved=0.00000000, bchbtc_fee=0.500, ethbtc_fee=0.500, etheur_fee=0.500, usd_balance=0.18, eth_available=1.30423351, btceur_fee=0.500, eth_withdrawal_fee=0.00100000, bch_balance=0.00000000, xrpbtc_fee=0.500}

我可以应用这个过滤器:

var keys    = Object.keys(data);
var values  = Object.keys(data).map(function(e){return data[e]});

var result = [];
var k;
for (k=0; k<keys.length; k++){
  if (keys[k].slice(4, 13) == "available") {result.push([keys[k], values[k]]);}}

在这种情况下我无法申请。

所以我想我会申请for(){} 案例,但是有没有更合理和有效的方法来做到这一点?

 var i;
  for (i=0; i < data.length; i++) {

   var d         = data[i].datetime;  
   var bits      = d.split(/\D/);
   var date      = new Date(bits[0], --bits[1], bits[2], bits[3], bits[4], bits[5]); 
   var date      = (date.getTime().toFixed(0))/1000;
   var date      = date.toString(); 

   if (date >  lastDate) { //IFFONE
   }
   }

我什至考虑尝试从 json 字符串中提取每对键/值集并将它们呈现为数组,这样我就可以始终指向确切的位置(在这种情况下为日期时间),但我想有一个更直接的和一致的方式。你会怎么做?谢谢
ps:我忘了提到我们在 google apps script

编辑:应用@contributorpw 过滤器时的结果:

这个脚本

var data  = {key: cred.key, signature: signature, nonce: nonce, sort: 'asc', limit: '10'};

var options = {'method' : 'post', 'muteHttpExceptions' : true, 'payload' : data};

var data    = UrlFetchApp.fetch('https://www.bitstamp.net/api/v2/user_transactions/', options);
var data    = JSON.parse(data.getContentText());
var values  = data;

生成以下 json 数据集:

[{btc=0, datetime=2018-01-31 12:15:11, eur=410.00, fee=0.00, usd=0, id=50575781, btc_usd=0.00, type=0},
{btc=0, eth_eur=692.81, datetime=2018-02-04 16:49:16, eur=-5.20, fee=0.02, usd=0, eth=0.00750000, id=52010995, type=2, order_id=893820507}, 
{btc=0, eth_eur=699, datetime=2018-02-04 17:04:25, eur=5.24, fee=0.02000000, usd=0, eth=-0.00750000, id=52013332, type=2, order_id=893880089}, 
{btc=0, eth_eur=562.1, datetime=2018-02-05 17:52:09, eur=-5.62, fee=0.02, usd=0, eth=0.01000000, id=52338326, type=2, order_id=900603492},
{btc=0, eth_eur=567.2, datetime=2018-02-05 20:29:07, eur=5.67, fee=0.02000000, usd=0, eth=-0.01000000, id=52444353, type=2, order_id=900621129}, 
{btc=0, datetime=2018-04-30 17:29:21, eur=0, fee=0.00000000, usd=0, eth=0.38811442, id=64286847, btc_usd=0.00, type=0},
{btc=0, eth_eur=563, datetime=2018-04-30 17:41:51, eur=218.50, fee=0.55000000, usd=0, eth=-0.38810000, id=64287296, type=2, order_id=1424216470}, 
{btc=0, eth_eur=563, datetime=2018-04-30 17:41:52, eur=0.01, fee=0.01000000, usd=0, eth=-0.00001442, id=64287298, type=2, order_id=1424216470}, 
{btc=0, datetime=2018-05-24 13:45:15, eur=1300.00, fee=0.00, usd=0, id=66875907, btc_usd=0.00, type=0}, 
{btc=0.01786568, btc_eur=5583.33, datetime=2018-08-12 12:01:13, eur=-99.75, fee=0.25, usd=0, id=72064184, type=2, order_id=1986400456}]

为了简洁起见,它仅限于 10 条记录。

现在,假设我们要应用一个过滤器,它只返回日期 之后 2018, 4, 16 的数据。
以下是发生的事情:

[19-12-17 12:08:48:897 PST] [
 {
  "fee": "0.00",
  "btc_usd": "0.00",
  "datetime": "2018-05-24 13:45:15",
  "usd": 0,
  "btc": 0,
  "type": "0",
  "id": 66875907,
  "eur": "1300.00"
 },
 {
  "fee": "0.25",
  "order_id": 1986400456,
  "datetime": "2018-08-12 12:01:13",
  "usd": 0,
  "btc": "0.01786568",
  "btc_eur": 5583.33,
  "type": "2",
  "id": 72064184,
  "eur": "-99.75"
 }
]

而你应该有更多的记录..我做错了什么?
这是完整的脚本:

/* nuova funzione nonce */
_generateNonce = function() {
  var now = new Date().getTime();

  if(now !== this.last)
    this.nonceIncr = -1;

  this.last = now;
  this.nonceIncr++;

  // add padding to nonce incr
  var padding =
    this.nonceIncr < 10 ? '000' :
      this.nonceIncr < 100 ? '00' :
        this.nonceIncr < 1000 ?  '0' : '';
  return now + padding + this.nonceIncr;
} //fine funzione nonce

var nonce = this._generateNonce(); 

var cred = {
       id:'***',
      key:'***',
   secret:'***'};


var message = nonce + cred.id +  cred.key;

var res = Utilities.computeHmacSha256Signature(message, cred.secret).map(function(e) {return ("0" + (e < 0 ? e + 256 : e).toString(16)).slice(-2)}).join("");
var signature = res.toUpperCase();

  var data  = {key: cred.key, signature: signature, nonce: nonce, sort: 'asc', limit: '10'};

var options = {'method' : 'post', 'muteHttpExceptions' : true, 'payload' : data};

var data    = UrlFetchApp.fetch('https://www.bitstamp.net/api/v2/user_transactions/', options);
var data    = JSON.parse(data.getContentText());
var values  = data;

var date = new Date(2018, 4, 16).toISOString().split(/t/i)[0];

var filteredData = values.filter(function(item) {
  var d = item.datetime.split(/\s/)[0];
  return d >= date;});

Logger.log(JSON.stringify(filteredData, null, ' '));

第二次编辑:问题出在哪里
问题只是我手动输入的日期和脚本生成的实际日期之间有一个月的偏移差异:所以这搞砸了所有结果,我不明白为什么。

也就是说,当手动输入时,这行命令

var date = new Date(2018, 3, 28).toISOString().split(/t/i)[0];

Logger.log(date);

产生这个结果:

[19-12-18 13:54:17:932 CET] 2018-04-27

另一方面,如果您直接从单元格(已经是日期格式)提供数据,则不会发生任何奇怪的事情:

var date = aDate.toISOString().split(/t/i)[0];

var filteredData = values.filter(function(item) {
  var d = item.datetime.split(/\s/)[0];
  return d >= date;
});

read this参考。
我不得不感谢@contributorpw 和@Diego 的贡献。最后,我选择了来自contributorpw 的答案,因为我发现它在代码上更苗条和敏捷,但两者都很好且功能强大。谢谢

【问题讨论】:

  • @RobG 不幸的是,您链接的答案不相关。我的脚本的问题与days missing 有关,我认为这不仅仅是本地时间与UTC 的差异问题。至于第二个,我们不是在谈论comparing,而是在谈论filtering。无论如何,我都会查看您链接的答案,看看它们是否有帮助,即使我在发布之前做了一些研究。在我看来,仅仅调用一个副本太容易了。还是谢谢
  • 由于时区偏移,new Date(2018, 3, 28).toISOString() 在您的时区(CET 或 UTC+1)中生成 2018-04-27 的日期。过滤需要比较,如return d &gt;= date
  • @RobG 您能否阐明您的评论,即使在必要时提供特定答案或链接到(明确)来源。我知道这很重要,但我不明白如何将您所说的应用于我的案子。谢谢

标签: javascript json date datetime google-apps-script


【解决方案1】:

我想使用字符串

var values = [{
    btc: 0,
    datetime: '2018-01-30 12:16:11',
    eur: 410.0,
    fee: 0.0,
    usd: 0,
    id: '***',
    btc_usd: 0.0,
    type: 0
  },
  ...
];
var date = new Date(2018, 3, 28).toISOString().split(/t/i)[0];
var filteredData = values.filter(function(item) {
  var d = item.datetime.split(/\s/)[0];
  return d >= date;
});
Logger.log(JSON.stringify(filteredData, null, ' '));

效果很好。

如果您想比较日期时间,请获取date as

var date = new Date(2018, 3, 28, 17 + 5, 42)
  .toISOString()
  .split(/[t\.]/i)
  .slice(0, 2)
  .join(' ');

并将其与

进行比较
 var d = item.datetime.split(/\s/)[0];
 return d >= date;

你已经考虑过时移17 + 5

您可以随时使用跳过日期

var filteredData = values.filter(function(item) {
  return item.datetime >= '2018-04-28';
});

【讨论】:

  • 我喜欢你的脚本:它简单而优雅;但它显然有一个问题:它错过了 Json 集中的一些数据:即所有标记为 type=0 的行。如果您查看我的帖子,您会注意到并非所有 json 字符串都共享相同的模式。有些是type 0(存款),有些是1(提款)和一些2(交易)。显然你的代码只返回type 2,交易。我错了吗?如果我是对的,请检查它,因为它是一个非常好的代码,我还有一些问题要问。非常感谢
  • 不。对我来说很好bitbucket.org/snippets/contributorpw/XLbMrd 它返回类型 0 和类型 2。
  • 这会将 本地 日期与 UTC 日期进行比较,因此对于时区偏移为负的用户会显示一天。
  • @JohnGalassi 抱歉,您需要分享更多示例数据。在我的示例中,您给我们的内容已完全过滤。我同意 Rob 的观点。
  • 昨天晚上我终于找到了问题所在,幸运的是能够解决它。现在给我一些时间来整理我的想法,我会在编辑的问题中写下细节,因为我刚醒来:毕竟我猜我们有不同的时区! :)
【解决方案2】:

过滤需要比较值,在这种情况下是具有 datetime 属性的特定日期。由于 datetime 是 ISO 8601 格式(虽然不是 ECMA-262 支持的格式),因此可以使用相同格式的字符串进行比较。这种方法意味着生成一个比较字符串,然后在不进一步处理 datetime 的情况下进行过滤。

或者,datetime 字符串可以转换为日期,然后通过比较日期对象或时间值(实际上是相同的东西)过滤数组。这可能效率较低,因为每个 datetime 都必须转换为 Date。

这两个问题之前已经讨论过:

没有时区的日期字符串应被视为本地日期。如果您有单独的信息表明它们应被视为 UTC,则应将其包含在问题中。解析应该手动完成,不要相信内置解析器(至少有一个当前浏览器会将'2018-01-31 12:15:11'解析为无效日期)。

生成正确格式的日期可以使用简单的格式化函数,然后与data[i].datetime进行比较。同样,将 datetime 转换为 Date 需要一个简单的解析函数(或库):

let data = [{datetime: '2018-01-31 12:15:11'},
            {datetime: '2018-02-04 16:49:16'}, 
            {datetime: '2018-02-04 17:04:25'}
           ];

// Filter as strings
function toISO(d) {
  let z = n => (n<10? '0':'') + n;
  return d.getFullYear() + '-' +
         z(d.getMonth() + 1) + '-' +
         z(d.getDate()) + ' ' +
         z(d.getHours()) + ':' +
         z(d.getMinutes()) + ':' +
         z(d.getSeconds());
}

let d = new Date(2018, 1, 1); // 1 Feb 2018
let filterDate = toISO(d);    // 2018-02-01 00:00:00
let filteredData_A = data.filter( o => o.datetime > filterDate);

console.log(filteredData_A);

// Filter as Date objects

function parseDate(s) {
  let b = s.split(/\D/);
  return new Date(b[0], b[1]-1, b[2], b[3], b[4], b[5]);
}

let filteredData_B = data.filter(o => parseDate(o.datetime) > d);

console.log(filteredData_B);

PS 发布时,将代码减少到演示问题所需的最少代码并确保其有效(例如 OP 中的“JSON”)会很有帮助。见How to create a minimal, reproducible example

【讨论】:

  • 非常感谢,让我看懂了再回复你
  • 我还有一个问题:假设我只想阅读data[0].datetime 以便接收这种格式的输出Sat Mar 03 00:00:00 GMT+01:00 2018 :我应该使用什么命令?因为如果我尝试用简单的Logger.log(data[0].datetime); 阅读它,我得到的只是:2018-01-31 12:15:11。如果我希望读取在 Logger 上产生类似 Sat Mar 03 00:00:00 GMT+01:00 2018 的输出,我应该使用什么命令行?我希望问题很清楚,谢谢
  • 我正在尝试使用您的代码,我有点喜欢。我读了你的解释,发现它松了一口气。问题是我似乎无法在 GAS 中使用箭头函数,你能把这段代码翻译成正常的函数语法吗? // Filter as strings function toISO(d) { let z = n =&gt; (n&lt;10? '0':'') + n; return d.getFullYear() + '-' + z(d.getMonth() + 1) + '-' + z(d.getDate()) + ' ' + z(d.getHours()) + ':' + z(d.getMinutes()) + ':' + z(d.getSeconds()); }
  • 是的:明天我要写一份更详细的报告,但重点是我从 json 获得的日期(即从 api 收集的数据)是 gmt 时区(UTC),而 mytimezone 是 gmt+1。
  • @RobG 我认为这里要理解的重要上下文是此代码在 Google Apps 脚本上运行,而不是在浏览器中。这就是为什么您对与内置日期解析器不一致的其他非常有效的担忧不太可能影响这个特定问题。箭头函数和 let 在 Apps 脚本中都不起作用,因为它是 based on JavaScript 1.6
【解决方案3】:

您可以轻松地将该字符串转换为ISO 8601 格式的日期/时间字符串,然后从中创建一个新的Date

/**
 * Parse a datetime value formatted like "2018-01-30 12:16:11" into a JavaScript
 * Date object by converting it into a simple ISO 8601 formatted date time,
 * without a time zone designator. As such, the date returned is in the executing
 * machine's time zone (i.e. project time zone setting).
 * @param {String} datetime
 * @returns {Date}
 */
function parseDatetime(datetime) {
  var dateParts = datetime.split(" ");
  return new Date(dateParts.join("T"));
}

/**
 * Test that parseDatetime() works as expected.
 */
function test_parseDatetime() {
  var datetime = "2018-01-30 12:16:11";
  var date = parseDatetime(datetime);
  Logger.log(date); // Tue Jan 30 12:16:11 GMT+00:00 2018
}

一旦你有了日期,你就可以像你写的那样使用比较运算符。

请注意,我已将项目属性中的时区设置为“(GMT+00:00) GMT(无夏令时)”。 parseDatetime() 返回的日期将受项目时区设置的影响。不过,只要您保持一致,就不会影响您的比较。

您的最终代码可能如下所示。我提供了两个过滤选项:(1) 使用 for 循环和 (2) 使用 .filter()。您可以通过检查 Logger 输出看到它们是等效的。在此示例中,我还对 parseDatetime() 函数进行了修改,以指定 UTC+0 时区,以解决 Date.parse() 带来的特定问题,但我认为这不是完全必要的。

function filterOrders() {
  var orders = [
    {btc:0, datetime:"2018-01-30 12:16:11", eur:410.00, fee:0.00, usd:0, id:"***", btc_usd:0.00, type:0},
    {btc:0, eth_eur:692.81, datetime:"2018-02-06 16:45:16", eur:-5.20, fee:0.02, usd:0, eth:0.00750000, id:"***", type:2, order_id:"***"},
    {btc:0, eth_eur:699, datetime:"2018-02-05 17:04:25", eur:5.24, fee:0.02000000, usd:0, eth:-0.00750000, id:"***", type:2, order_id:"***"},
    {btc:0, eth_eur:562.1, datetime:"2018-02-04 17:52:09", eur:-5.62, fee:0.02, usd:0, eth:0.01000000, id:"***", type:2, order_id:"***"},
    {btc:0, eth_eur:567.2, datetime:"2018-02-06 20:24:07", eur:5.67, fee:0.02000000, usd:0, eth:-0.01000000, id:"***", type:2, order_id:"***"},
    {btc:0, datetime:"2018-04-28 17:22:21", eur:0, fee:0.00000000, usd:0, eth:0.38811442, id:"***", btc_usd:0.00, type:0},
    {btc:0, eth_eur:563, datetime:"2018-04-28 17:42:51", eur:218.50, fee:0.55000000, usd:0, eth:-0.38810000, id:"***", type:2, order_id:"***"},
    {btc:0, eth_eur:563, datetime:"2018-04-28 17:47:52", eur:0.01, fee:0.01000000, usd:0, eth:-0.00001442, id:"***", type:2, order_id:"***"},
    {btc:0.01786568, btc_eur:5583.33, datetime:"2018-08-14 12:01:13", eur:-99.75, fee:0.25, usd:0, id:"***", type:2, order_id:"***"}
  ];

  // Select all orders where datetime is greater than April 1, 2018
  var filterDate = new Date("2018-04-01");

  // FOR loop method
  var filteredOrders1 = [];
  for (var i = 0; i < orders.length; i++) {
    var order = orders[i];
    if (parseDatetime(order.datetime) > filterDate) {
      filteredOrders1.push(order);
    }
  }

  // FILTER method
  var filteredOrders2 = orders.filter(function(order) {
    return parseDatetime(order.datetime) > this;
  }, filterDate);

  // Same results? 
  Logger.log(JSON.stringify(filteredOrders1) == JSON.stringify(filteredOrders2)); // true

  Logger.log(JSON.stringify(filteredOrders1));
}

/**
 * Parse a datetime value formatted like "2018-01-30 12:16:11" into a JavaScript
 * Date object by converting it into a simple ISO 8601 formatted date time, with
 * UTC as the time zone.
 * @param {String} datetime
 * @returns {Date}
 */
function parseDatetime(datetime) {
  var dateParts = datetime.split(" ");
  return new Date(dateParts.join("T") + "Z");
}

【讨论】:

  • 感谢您的回答,让我尝试一下,以防万一要求澄清。待会见!
  • 我在这种方法中遇到了两个问题,希望您能帮助我。首先,在我看来,即使采用这种格式,我也无法直接比较日期,因此无论如何转换为timestamp 似乎是必要的,以便能够if (date &gt; lastDate)。其次,最重要的是:我仍然无法过滤特定日期范围的 json 数据集。我如何将您的方法应用于数据集并对其进行过滤,例如dates &gt; a specific date。可以展示给我吗?谢谢
  • @JohnGalassi 是的。我刚刚编辑了我的答案以包含一个示例。您可以像我一样使用.filter(),或者轻松修改它以使用for 循环。
  • 亲爱的@Diego,我对您的代码有几个问题。但是,考虑到我可能由于缺乏知识而根本无法理解您的脚本,请您一步一步地解释它吗?所以我确定我没有遗漏任何东西,谢谢
  • @JohnGalassi 我更新了示例以展示它是如何使用 for 循环完成的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-06
  • 1970-01-01
  • 2019-09-21
  • 2019-07-30
  • 2013-11-25
  • 2017-02-04
相关资源
最近更新 更多