iOS13+ 性能和耗电量信息收集框架

8,842 阅读14分钟

前言

在介绍框架之前,先看一张图:

在系统设置中可以查看过去24小时(甚至更久)的电池使用情况。除了电池使用信息之外,系统还收集了APP的其他信息,比如占用CPU时间,内存使用峰值等。 这些信息都可以通过MetricKit框架获取到。

一、MetricKit 简介

MetricKit适用于iOS 13.0+的设备。它会在一天结束后,将过去24小时搜集的性能数据归集在一起,然后在下一次启动 App 后,在 delegate 的回调中提供给我们。

实际使用时除了获取到过去24小时的数据,24小时之前未被收集过的数据也会一并返回。

MetricKit 只是统计线上APP某些指标的使用情况,如果想统计某行代码/某个函数的资源使用情况,可以使用该框架提供的打点功能。

MetricKit 能统计哪些数据

  • 设备信息,APP版本信息
  • CPU
  • GPU
  • 蜂窝网络
  • 前后台运行时间
  • 定位(位置)
  • 网络
  • APP启动时间
  • APP挂起
  • 磁盘IO
  • 内存
  • 屏幕显示
  • 自定义代码块打点

具体指标信息见下文的表格

接入 MetricKit 能产生什么价值

MetricKit 将系统收集的数据交给开发者,由开发来决定这些指标数据如何来处理。(例如解析后直接上传到第三方统计平台)

与MetricKit一起发布的还有另外两个工具

  • XCTest Metrics (开发和测试阶段)
  • MetricsKit (内测阶段和线上阶段)
  • Xcode Metrics Organizer (线上阶段)

Xcode11 已经提供了 Xcode Metrics Organizer 功能,可使用快捷键 option + command + shift + o(英文o) 调出。这些数据是由iOS系统自动收集的,开发者只能在该窗口查看。

二、如何使用

MetricKit基本属于无侵入式接入,对原项目代码影响较小(接入后是否会带来性能问题有待研究),只需要简单几步即可使用。

一般在AppDelegate中接入即可。

1. 获取MetricManager单例

MXMetricManager *metricManager = [MXMetricManager sharedManager];

从目前来看,框架并没有提供任何自定义manager的接口,只能通过shared获取单例。

2. 为MetricManager单例添加订阅者

[metricManager addSubscriber:self];

注:框架同时提供了移除订阅者的api

[metricManager removeSubscriber:self];

3. 订阅者实现回调

// 1. 实现 MXMetricManagerSubscriber 协议
@interface ViewController ()<MXMetricManagerSubscriber>
@end

@implementation ViewController

// 2. 完善回调方法
- (void)didReceiveMetricPayloads:(NSArray<MXMetricPayload *> *)payloads {
    // 每个payload表示一个24小时内的统计数据包。
    // 数组包括过去24小时和其他时间段还未被处理过的数据
}

@end

通过上述三步就可以轻松接入MetricKit

完整接入案例

#import "ViewController.h"
#import <MetricKit/MetricKit.h>

@interface ViewController ()<MXMetricManagerSubscriber>
@property (nonatomic, strong) MXMetricManager *metricManager;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 1. 获取MetricManager单例
    self.metricManager = [MXMetricManager sharedManager];
    // 2. 为MetricManager单例添加订阅者
    [self.metricManager addSubscriber:self];
}

// 3. 完善回调方法
- (void)didReceiveMetricPayloads:(NSArray<MXMetricPayload *> *)payloads {
    for (MXMetricPayload *payload in payloads) {
        NSLog(@"%@", payload);
    }
}

// 4. 移除订阅者
- (void)dealloc {
    [self.metricManager removeSubscriber:self];
}

* 自定义打点

与iOS12发布的 os_signpost API 使用类似,因为MetricKit负责的是线上版本数据的收集,所以代码段的打点信息可以在回调中获取。

os_log_t loginterest = [MXMetricManager makeLogHandleWithCategory:@"categoryName"];
os_signpost_id_t spidinterest = os_signpost_id_generate(loginterest);

MXSignpostIntervalBegin(testHandle, spidinterest, "Launch");

// do something...

MXSignpostIntervalEnd(testHandle, spidinterest, "Launch");

最后得到的数据样式

signpostMetrics = (
    {
        signpostCategory = TestSignpostCategory1;
        signpostIntervalData =             {
            histogrammedSignpostDurations =                 {
                histogramNumBuckets = 3;
                histogramValue =                     {
                    0 =                         {
                        bucketCount = 50;
                        bucketEnd = "100 ms";
                        bucketStart = "0 ms";
                    };
                    1 =                         {
                        bucketCount = 60;
                        bucketEnd = "400 ms";
                        bucketStart = "100 ms";
                    };
                    2 =                         {
                        bucketCount = 30;
                        bucketEnd = "700 ms";
                        bucketStart = "400 ms";
                    };
                };
            };
            signpostAverageMemory = "100,000 kB";
            signpostCumulativeCPUTime = "30,000 ms";
            signpostCumulativeLogicalWrites = "600 kB";
        };
        signpostName = TestSignpostName1;
        totalSignpostCount = 30;
    }
);

三、注意事项

  1. 线上环境是24小时触发一次,开发阶段可以使用Xcode模拟触发,Xcode > Debug > Simulate MetricKit Payloads, 注意需要在真机调试才可以模拟,如果是模拟器,按钮是灰色的,无法点击。
  2. 该框架的大部分数据都是以 (value: double型数值,uint: 单位) 的方式返回,例如 1s, 1ms, 1kb。
  3. MXMetricPayload类(可理解为24小时内获取的数据总包)和MX_ _ _Metric类(具体到某种设备数据)都含有 - (NSData *)JSONRepresentation;- (NSDictionary *)DictionaryRepresentation;,可快速转换为JSON类型查看。下文提到的payload均表示一个过去24小时的数据包。
  4. 部分设备数据以直方图(柱状图,histogram)的概念表示,整个结构会包括 numbBuckets 表示统计了几个区间,然后会有 value 数组表示多个区间的具体数据, 每个区间对象会包括 bucketStart 区间起始坐标, bucketEnd 区间结束坐标,bucketCount 区间具体数值。 以APP启动时间和蜂窝信号指标举例:
// APP启动时间
// 假设过去24小时数据统计内,共启动了110次,可以被划分为2个使用区间。
// 有50次启动时间出现在1000-1010ms内,60次在2000-2010ms内
histogrammedTimeToFirstDrawKey =     {
    histogramNumBuckets = 2;
    histogramValue =         {
        0 =             {
            bucketCount = 50;
            bucketEnd = "1,010 ms";
            bucketStart = "1,000 ms";
        };
        1 =             {
            bucketCount = 60;
            bucketEnd = "2,010 ms";
            bucketStart = "2,000 ms";
        };
    };
};


// 蜂窝信号:bucketCount表示当前信号下APP使用时间的百分比。 
// 在此payload的统计下,有20%的时间是1格信号,30%是2格信号,50%是三格信号
{
    cellConditionTime =     {
        histogramNumBuckets = 3;    // 共统计了三个区间
        histogramValue =         {
            0 =             {
                bucketCount = 20;
                bucketEnd = "1 bars";
                bucketStart = "1 bars";
            };
            1 =             {
                bucketCount = 30;
                bucketEnd = "2 bars";
                bucketStart = "2 bars";
            };
            2 =             {
                bucketCount = 50;
                bucketEnd = "3 bars";
                bucketStart = "3 bars";
            };
        };
    };
}

四、More

os_signpost()

iOS12 提供的代码段打点功能,配合Instruments,可视化观察打点代码段的运行时间。 详细使用参考: www.jianshu.com/p/4c112d850… everettjf.github.io/2018/08/13/…

NSDimension

度量单位抽象类,该框架很多数据都提供了具体单位

MXHistogram

我理解是此类包含一个数组的数量,和用于快速遍历数组的NSEnumerator对象。 类似于一个数组快速迭代器。

五、参考文章

* 框架内部具体实现

MXMetricManager

Metric管理类。

有三个常用的方法:

    • (id)sharedManager 单例
    • (void)addSubscriber: 添加订阅者
    • (void)removeSubscriber: 移除订阅者

当一个对象实现了 MXMetricManagerSubscriber 协议,并且将自己添加到MXMetricManager单例中,就可以接收系统收集的指标信息;

其他属性和方法:

  • NSArray<MXMetricPayload *> *pastPayloads 接收到的性能指标信息
    • (os_log_t)makeLogHandleWithCategory: 自定义打点有关

MXMetricManagerSubscriber

该协议有一个 @required 的方法需要实现,用以接收性能信息。

调用时机: 每天我们的应用最多只会收到一次回调,该次回调会把上一段 24 小时收集到的数据返回给我们。同时,如果在上一个 24 小时之前,存在老数据没有返回给我们的,也会在该次回调中一并返回。返回的数据会存储成数组的形式,每个数组的元素表示一天的数据。

- (void)didReceiveMetricPayloads:(NSArray<MXMetricPayload *> * _Nonnull)payloads;

MXMetricPayload

用以封装每天的指标信息。类似于一个24小时内的数据总包,内部通过只读属性提供具体的指标信息(CPU,GPU,蜂窝网络等)。

  1. MXMetricPayload封装了MetricKit目前支持的指标类型(如CPU,GPU,网络等,形如MX__Metric的类),这些指标信息可以为空,表示此指标数据不可用。
  2. MXMetricPayload提供了快速格式化为JSON的方法。 - (NSData *)JSONRepresentation;- (NSDictionary *)DictionaryRepresentation;,可以转换成NSData或者NSDictionary.
  3. MXMetricPayload包含24小时应用程序使用期间的数据。可以使用timeStampBegin和timeStampEnd属性来确定数据的时间范围。
  4. 考虑到APP会有不同的版本,MXMetricPayload会携带一个latestApplicationVersion的字符串,此字段会和数据统计时项目配置中的Version版本保持一致。语义上latest并不是表示APP的最新版本,而是说,在此24小时数据采集的区间内,如果你从1.0版本升级到了1.1版本,也就是当前payload的数据是两个版本的混合数据,latestApplicationVersion应该是显示的1.1而不是1.0。
  5. (BOOL)includesMultipleApplicationVersions 字段表示是否发生了4中提到的,数据采集期间发生更新版本的现象。

总结 MXMetricPayload 内容如下

下面所提到的所有指标根据对设备的影响可被分为几类,电量指标、性能指标、磁盘访问指标、自定义指标

属性/方法 类型 介绍
latestApplicationVersion NSString 收集此数据时的最高版本信息
(例如:1.0), 参考上面第4条
includesMultipleApplicationVersions BOOL 当前24小时数据包是否
包含多个版本,参考上面4,5
timeStampBegin NSDate 数据采集的开始时间
timeStampEnd NSDate 数据采集的结束时间
cpuMetrics MXCPUMetric 【电量】CPU指标
gpuMetrics MXGPUMetric 【电量】GPU指标
cellularConditionMetrics MXCellularConditionMetric 【电量】蜂窝网络指标
applicationTimeMetrics MXAppRunTimeMetric 【性能】运行时间指标,
前/后台运行时间,
后台媒体、定位的运行时间等
locationActivityMetrics MXLocationActivityMetric 【电量】定位(位置)指标
networkTransferMetrics MXNetworkTransferMetric 【电量】网络指标
applicationLaunchMetrics MXAppLaunchMetric 【性能】APP启动指标
applicationResponsivenessMetrics MXAppResponsivenessMetric 【电量】APP挂起指标
diskIOMetrics MXDiskIOMetric 磁盘IO指标
memoryMetrics MXMemoryMetric 【性能】内存指标
displayMetrics MXDisplayMetric 【电量】显示指标
signpostMetrics NSArray<MXSignpostMetric *> MXSignpost打点,
针对关键代码块打点,
记录性能数据
metaData MXMetaData 杂项元数据指标
(手机版本,os版本,Build等)
- JSONRepresentation (NSData *) JSON格式化
- DictionaryRepresentation (NSDictionary *) JSON格式化

MXMetric

各种指标类的抽象基类,目前只提供了 JSONRepresentationDictionaryRepresentation 两个通用的方法。

MXCPUMetric - 【电量】CPU指标

  • cumulativeCPUTime: 记录CPU运行时间, 数据表示整个payload期间,该APP消耗的总CPU时间,会返回一个double时间值和unit单位。

MXGPUMetric - 【电量】GPU指标

记录GPU运行时间,规则同CPU

MXCellularConditionMetric - 【电量】蜂窝网络指标

  • histogrammedCellularConditionTime: 此数据表示应用程序在不同蜂窝信号强度下运行的时间百分比。

在该类中使用到了一个自定义的单位 - bars MXUnitSignalBars: 一个表示信号强度的信号条数自定义度量单位

得到的JSON数据结构如下:

// APP在此payload的统计下,有20%的时间是1格信号,30%是2格信号,50%是三格信号
{
    cellConditionTime =     {
        histogramNumBuckets = 3;
        histogramValue =         {
            0 =             {
                bucketCount = 20;
                bucketEnd = "1 bars";
                bucketStart = "1 bars";
            };
            1 =             {
                bucketCount = 30;
                bucketEnd = "2 bars";
                bucketStart = "2 bars";
            };
            2 =             {
                bucketCount = 50;
                bucketEnd = "3 bars";
                bucketStart = "3 bars";
            };
        };
    };
}

MXAppRunTimeMetric - 【性能】运行时间指标

表示APP在前后台,以及后台音频和定位服务所运行的时间,每个属性均有时长和单位

  • cumulativeForegroundTime: 用户可见(前台)的运行时间
  • cumulativeBackgroundTime: 用户不可见(后台)的运行时间
  • cumulativeBackgroundAudioTime:后台播放音频的累计时间
  • cumulativeBackgroundLocationTime:后台获取和处理位置信息的累计时间

MXLocationActivityMetric - 【电量】定位指标

在与相关的CLLocation中给出了几个枚举值:

  • kCLLocationAccuracyBest:精度最高的定位
  • kCLLocationAccuracyBestForNavigation:最适合导航用的定位
  • kCLLocationAccuracyNearestTenMeters:定位精度在10米以内
  • kCLLocationAccuracyHundredMeters:定位精度在100米以内
  • kCLLocationAccuracyKilometer:定位精度在1000米以内
  • kCLLocationAccuracyThreeKilometers:定位精度在3000米以内

所以该指标类提供了对应定位类型所用的时间(下面不再注释),结果为数值+时间单位

  • cumulativeBestAccuracyTime
  • cumulativeBestAccuracyForNavigationTime
  • cumulativeNearestTenMetersAccuracyTime
  • cumulativeHundredMetersAccuracyTime
  • cumulativeKilometerAccuracyTime
  • cumulativeThreeKilometersAccuracyTime

MXNetworkTransferMetric - 【电量】网络传输指标

返回值包含数值和单位 (单位转换提供了1:1000和1:1024两种方式,请自行选择)

  • cumulativeWifiUpload:通过Wifi上传的累计数据量
  • cumulativeWifiDownload:通过Wifi下载的累计数据量
  • cumulativeCellularUpload:通过蜂窝网络上传的累计数据量
  • cumulativeCellularDownload:通过蜂窝网络下载的累计数据量

MXAppLaunchMetric -【性能】APP启动指标

本类包含两个时间相关的数组,在过去24小时内,用户会不止一次的打开/后台切换APP,所以一天内会记录多个值,最后以10ms为间隔绘制直方图,然后显示某个时间区间出现的次数。

  • histogrammedTimeToFirstDraw:应用程序启动所需时间的柱状图。(从点击APP图标开始,到第一个不是LaunchScreen的屏幕出现)
  • histogrammedApplicationResumeTime:从后台恢复应用程序所需时间的柱状图。
// 在过去24小时内,有50次启动时间出现在1000-1010ms秒内
histogrammedTimeToFirstDrawKey =     {
    histogramNumBuckets = 3;
    histogramValue =         {
        0 =             {
            bucketCount = 50;
            bucketEnd = "1,010 ms";
            bucketStart = "1,000 ms";
        };
        1 =             {
            bucketCount = 60;
            bucketEnd = "2,010 ms";
            bucketStart = "2,000 ms";
        };
        2 =             {
            bucketCount = 30;
            bucketEnd = "3,010 ms";
            bucketStart = "3,000 ms";
        };
    };
};

MXAppResponsivenessMetric -【电量】APP挂起指标

当APP挂起超过9秒时,将被记录到最后一个时间区间内。

  • histogrammedApplicationHangTime:挂起时间分布直方图

MXDiskIOMetric - 磁盘IO指标

  • cumulativeLogicalWrites: 累计写入的数据量,带单位

MXMemoryMetric - 【性能】内存指标

属性值包括数值和单位

  • peakMemoryUsage:此APP在当前数据统计包下的内存使用峰值
  • averageSuspendedMemory:此APP在当前数据统计包下的内存使用平均值

MXDisplayMetric - 【电量】显示指标

  • averagePixelLuminance: OLED显示器上像素的平均亮度,值为0-100,为null则表示屏幕不支持APL。

MXSignpostMetric - 【自定义】代码块打点

os_signpost API 类似。(代码段打点,配合Instruments,可视化观察打点代码段的运行时间。) os_signpost 是开发阶段通过 Instruments 查看,MetricKit的打点功能可以将线上APP代码段的资源消耗情况提供给开发者。

  • signpostName: 打点名称
  • signpostCategory:打点分类名称
  • signpostIntervalData:记录具体的的指标,如果当前name和category下没有记录信息,则为nil (MXSignpostIntervalData)
  • totalCount:在此payload下使用该打点名称发出的打点总数。

MXSignpostIntervalData - 自定义打点的数据类 (还在研究)

  • histogrammedSignpostDuration:当前name和category下打点在不同时间间隔分布的直方图
  • cumulativeCPUTime:代码段累计CPU时间,nullable
  • averageMemory:内存快照的平均值,nullable
  • cumulativeLogicalWrites:累计逻辑写入数据的大小,nullable

MXMetaData - 设备信息

设备的部分信息(已经全部列出)

  • regionFormat
  • osVersion
  • deviceType
  • applicationBuildVersion

MXSignpost - 与自定义打点相关的宏定义

在 MXSignpost.h 中声明了几个调用 os_signpost API 的宏,

使用 MXSignpost 的打点功能要比普通 os_signpost 开销大,如果过度使用 MXSignpost, 会使APP性能倒退

// 具体参考 <os/signpost.h>

#define MXSignpostEventEmit(log, event_id, name, ...) _MXSignpostEventEmit_guaranteed_args(log, event_id, name, "" __VA_ARGS__)

#define MXSignpostIntervalBegin(log, event_id, name, ...) _MXSignpostIntervalBegin_guaranteed_args(log, event_id, name, "" __VA_ARGS__)

#define MXSignpostIntervalEnd(log, event_id, name, ...) _MXSignpostIntervalEnd_guaranteed_args(log, event_id, name, "" __VA_ARGS__)

一个payload模拟数据Dictionary格式如下

MetricKit提供的Dict格式化方法返回的数据并不是完整的,部分参数已被省略,详细参数请参考上表。

{
    appVersion = "1.2";
    applicationLaunchMetrics =     {
        histogrammedResumeTime =         {
            histogramNumBuckets = 3;
            histogramValue =             {
                0 =                 {
                    bucketCount = 60;
                    bucketEnd = "210 ms";
                    bucketStart = "200 ms";
                };
                1 =                 {
                    bucketCount = 70;
                    bucketEnd = "310 ms";
                    bucketStart = "300 ms";
                };
                2 =                 {
                    bucketCount = 80;
                    bucketEnd = "510 ms";
                    bucketStart = "500 ms";
                };
            };
        };
        histogrammedTimeToFirstDrawKey =         {
            histogramNumBuckets = 3;
            histogramValue =             {
                0 =                 {
                    bucketCount = 50;
                    bucketEnd = "1,010 ms";
                    bucketStart = "1,000 ms";
                };
                1 =                 {
                    bucketCount = 60;
                    bucketEnd = "2,010 ms";
                    bucketStart = "2,000 ms";
                };
                2 =                 {
                    bucketCount = 30;
                    bucketEnd = "3,010 ms";
                    bucketStart = "3,000 ms";
                };
            };
        };
    };
    applicationResponsivenessMetrics =     {
        histogrammedAppHangTime =         {
            histogramNumBuckets = 3;
            histogramValue =             {
                0 =                 {
                    bucketCount = 50;
                    bucketEnd = "100 ms";
                    bucketStart = "0 ms";
                };
                1 =                 {
                    bucketCount = 60;
                    bucketEnd = "400 ms";
                    bucketStart = "100 ms";
                };
                2 =                 {
                    bucketCount = 30;
                    bucketEnd = "700 ms";
                    bucketStart = "400 ms";
                };
            };
        };
    };
    applicationTimeMetrics =     {
        cumulativeBackgroundAudioTime = "30 sec";
        cumulativeBackgroundLocationTime = "30 sec";
        cumulativeBackgroundTime = "40 sec";
        cumulativeForegroundTime = "700 sec";
    };
    cellularConditionMetrics =     {
        cellConditionTime =         {
            histogramNumBuckets = 3;
            histogramValue =             {
                0 =                 {
                    bucketCount = 20;
                    bucketEnd = "1 bars";
                    bucketStart = "1 bars";
                };
                1 =                 {
                    bucketCount = 30;
                    bucketEnd = "2 bars";
                    bucketStart = "2 bars";
                };
                2 =                 {
                    bucketCount = 50;
                    bucketEnd = "3 bars";
                    bucketStart = "3 bars";
                };
            };
        };
    };
    cpuMetrics =     {
        cumulativeCPUTime = "100 sec";
    };
    diskIOMetrics =     {
        cumulativeLogicalWrites = "1,300 kB";
    };
    displayMetrics =     {
        averagePixelLuminance =         {
            averageValue = "50 apl";
            sampleCount = 500;
            standardDeviation = 0;
        };
    };
    gpuMetrics =     {
        cumulativeGPUTime = "20 sec";
    };
    locationActivityMetrics =     {
        cumulativeBestAccuracyForNavigationTime = "20 sec";
        cumulativeBestAccuracyTime = "30 sec";
        cumulativeHundredMetersAccuracyTime = "30 sec";
        cumulativeKilometerAccuracyTime = "20 sec";
        cumulativeNearestTenMetersAccuracyTime = "30 sec";
        cumulativeThreeKilometersAccuracyTime = "20 sec";
    };
    memoryMetrics =     {
        averageSuspendedMemory =         {
            averageValue = "100,000 kB";
            sampleCount = 500;
            standardDeviation = 0;
        };
        peakMemoryUsage = "200,000 kB";
    };
    metaData =     {
        appBuildVersion = 1;
        deviceType = "iPhone11,2";
        osVersion = "iPhone OS 13.1.2 (17A861)";
        regionFormat = CN;
    };
    networkTransferMetrics =     {
        cumulativeCellularDownload = "80,000 kB";
        cumulativeCellularUpload = "70,000 kB";
        cumulativeWifiDownload = "60,000 kB";
        cumulativeWifiUpload = "50,000 kB";
    };
    signpostMetrics =     (
                {
            signpostCategory = TestSignpostCategory1;
            signpostIntervalData =             {
                histogrammedSignpostDurations =                 {
                    histogramNumBuckets = 3;
                    histogramValue =                     {
                        0 =                         {
                            bucketCount = 50;
                            bucketEnd = "100 ms";
                            bucketStart = "0 ms";
                        };
                        1 =                         {
                            bucketCount = 60;
                            bucketEnd = "400 ms";
                            bucketStart = "100 ms";
                        };
                        2 =                         {
                            bucketCount = 30;
                            bucketEnd = "700 ms";
                            bucketStart = "400 ms";
                        };
                    };
                };
                signpostAverageMemory = "100,000 kB";
                signpostCumulativeCPUTime = "30,000 ms";
                signpostCumulativeLogicalWrites = "600 kB";
            };
            signpostName = TestSignpostName1;
            totalSignpostCount = 30;
        },
                {
            signpostCategory = TestSignpostCategory2;
            signpostIntervalData =             {
                histogrammedSignpostDurations =                 {
                    histogramNumBuckets = 3;
                    histogramValue =                     {
                        0 =                         {
                            bucketCount = 60;
                            bucketEnd = "200 ms";
                            bucketStart = "0 ms";
                        };
                        1 =                         {
                            bucketCount = 70;
                            bucketEnd = "300 ms";
                            bucketStart = "201 ms";
                        };
                        2 =                         {
                            bucketCount = 80;
                            bucketEnd = "500 ms";
                            bucketStart = "301 ms";
                        };
                    };
                };
                signpostAverageMemory = "60,000 kB";
                signpostCumulativeCPUTime = "50,000 ms";
                signpostCumulativeLogicalWrites = "700 kB";
            };
            signpostName = TestSignpostName2;
            totalSignpostCount = 40;
        }
    );
    timeStampBegin = "2019-11-24 16:00:00 +0000";
    timeStampEnd = "2019-11-25 15:59:00 +0000";
}