//===================================================================================
// FFCalMOD_v20d.mq4
//
// **********************************************************************************
// LICENSING: This is free, open source software, licensed under
// Version 2 of the GNU General Public License (GPL).
//
// In particular, this means that distribution of this software in a binary
// format, e.g. as compiled in as part of a .ex4 format, must be accompanied
// by the non-obfuscated source code of both this file, AND the .mq4 source
// files which it is compiled with, or you must make such files available at
// no charge to binary recipients. If you do not agree with such terms you
// must not use this code. Detailed terms of the GPL are widely available
// on the Internet. The Library GPL (LGPL) was intentionally not used,
// therefore the source code of files which link to this are subject to
// terms of the GPL if binaries made from them are publicly distributed or
// sold.
//
// ANY USE OF THIS CODE NOT CONFORMING TO THIS LICENSE MUST FIRST RECEIVE
// PRIOR AUTHORIZATION FROM THE AUTHOR(S). ANY COMMERCIAL USE MUST FIRST
// OBTAIN A COMMERCIAL LICENSE FROM THE AUTHOR(S).
// **********************************************************************************
//
// This is a FORK of the original work done by:
// * Derk Wehler (derkwehler@gmail.com)
// * Paul Hampton-Smith
// * Mike Nguyen
// * Fai Hatena
// And many others who have contributed to this code
//
// Original code can be found at:
// http://www.forexfactory.com/showthread.php?t=19293
//
// **********************************************************************************
//
// This "indicator" calls DLLs to fetch a special XML file from the
// ForexFactory web site. It then parses it and writes it out as a .CSV
// file, which it places in the folder: experts/files so that IsNewsTime()
// can use that file to tell if it is near announcement time.
//
//=============================================================================
//
// Changes from the original version (FFCal v20):
//
// * Related Calencar functions externalized to library. This library is easily usable from an EA
// * The indicator / funtions have been abstracted from the source used (ForexFactory or DailyFX)
// * Code has been reduced. Redundant / similar functions have been unified.
// * Additional graphic capabilities added (future events lines, coloring of vertical lines, ...)
// * Backtesting capabilities added. The indicator can instruct the library to use an External Time
// reference. This reference can be easily provided by the EA undertest adding the following line
// of code at the beginning of the start() function:
// if(IsTesting()) GlobalVariableSet("_TimeReference", TimeCurrent());
// * For ForexFactory XML files are kept on a permanent basis with different name.
// * Many other changes ...
#property copyright "Copyright � 2006, Derk Wehler"
#property link "http://www.forexfactory.com"
#property indicator_chart_window
#property indicator_buffers 3
#include "../include/LibFFCal.mqh"
#define EA_NAME "FFCalv20d"
//====================================================================================
//====================================================================================
//====================================================================================
//====================================================================================
//====================================================================================
//====================================================================================
//====================================================================================
#define MAX_SIMULTANEOUS_EVENTS 4
extern string __events_selection__ = "--- EVENTS SELECTION ---";
extern bool SelectHighImpactEvents = true;
extern bool SelectMediumImpactEvents = true;
extern bool SelectLowImpactEvents = false; //true;
extern bool SelectSpeaksEvents = true; // news items with "Speaks" in them have different characteristics
extern string __symbols_selection__ = "--- SYMBOL SELECTION ---";
extern bool ReportAllForAll = true; // By default is only shows news related with the chart's pair
extern bool ReportAllForUSD = false;
extern bool ReportAllForEUR = false;
extern bool ReportAllForGBP = false;
extern bool ReportAllForNZD = false; // added and tested by a1ra, seems working OK.
extern bool ReportAllForJPY = false;
extern bool ReportAllForAUD = false;
extern bool ReportAllForCHF = false;
extern bool ReportAllForCAD = false;
extern bool ReportAllForCNY = false;
extern string __dashboard_configuration_ = "--- DASHBOARD CONFIGURATION ---";
extern bool NewsDashBoardShow = true; // Choose if we show the Dashboard.
extern int NewsDashboardCorner = 2; // Choose which corner to place the Dashboard (0=Upper Left, 1=Upper Right, 2=lower left , 3=lower right)
extern int NewsDashboardEvents = 4; // Number of events that will be showed at the Dashboard
extern bool NewsDashboardSummary = true; // If TRUE it will only draw one line per selected event.
extern color NewsDashboardTitleColor = Gold;
extern color NewsDashboardTextColor = White;
extern int NewsDashboardTextSize = 10;
extern string __events_color_ = "--- EVENTS COLORS ---";
extern color ColorImpactHigh = Red; // Color of description / vertical lines if IMPACT is HIGH
extern color ColorImpactMedium = Orange; // Color of description / vertical lines if IMPACT is MEDIUM
extern color ColorImpactLow = Khaki; // Color of description / vertical lines if IMPACT is LOW
extern color ColorImpactUndefined = Aqua; // Color of our vertical news line, if no IMPACT has been defined
extern color ColorImpactOld = Gray; // Color or description is event is OLD.
extern string __vertical_lines__ = "--- CHART VERTICAL LINES ---";
extern bool LineShowOldEvents = true; // Draw vertical lines of past events
extern bool LineShowFutureEvents = true; // Pending!!!! Draw vertical lines of future events.
extern color LineTextColor = LightGray; // Color of our vertical text color
extern int LineTextSize = 8; // Color of our vertical text
extern int LineTextLeftShift = 900; // How far away to the left of the line we want to place our vertical news text
extern int LineTextRightShift = 900; // How far away to the left of the line we want to place our vertical news text
extern int LineTextVerticalShift = 21; // How far away below the ask line we want to place our vertical news text
extern string __alerts__ = "--- ALERTS ---";
extern int AlertXMinutesBefore = 0; // Set to -1 for no Alert
extern int AlertYMinutesBefore = 240; // Set to -1 for no Alert
extern string __configuration__ = "--- ADDITIONAL CONFIGURATION ---";
extern bool AutoGMTZones = false;
extern int LocalGMTZone = 1; // GTM+X
extern int BrokerGMTZone = 2;
// extern bool AllowWebUpdates = true; // Set this to false when using in another EA or Chart, so that the multiple instances of the indicator dont fight with each other
extern bool IsEA_Call = false;
extern bool UseDailyFXAsSource = true;
extern bool UseGlobalTimeReference = false;
double ExtMapBuffer0[]; // Contains (minutes until) each news event
double ExtMapBuffer1[]; // Contains only most recent and next news event ([0] & [1])
double ExtMapBuffer2[]; // Contains impact value for most recent and next news event
int EventsArray[1000];
int minsTillNews;
int minutesTillNextEvent;
int indexOfNextEvent;
static int previousMinute = -1;
datetime LastTimeAlert1 = 0; // Used to make sure we only draw something once per annoucement. Added by MN
int globalNewsSelectedTotal = 0;
int globalNewsAllTotal = 0;
string objectPrefix = "CA*";
int init() {
// Buffers definitions
SetIndexStyle(0, DRAW_NONE);
SetIndexBuffer(0, ExtMapBuffer0);
SetIndexStyle(1, DRAW_NONE);
SetIndexBuffer(1, ExtMapBuffer1);
SetIndexStyle(1, DRAW_NONE);
SetIndexBuffer(2, ExtMapBuffer2);
IndicatorShortName("FFCalv20d");
// for(int i = 0; i < ObjectsTotal(); i++) {
// string objectName = ObjectName(i);
// if (StringFind(objectName, objectPrefix, 0) == 0)
// ObjectDelete(objectName);
// }
FFCalSetTimeReference (UseGlobalTimeReference);
FFCalSetTimeOffset (AutoGMTZones, LocalGMTZone, BrokerGMTZone);
FFCalInitLibrary ();
return(0);
}
int deinit() {
int i;
// Cycle through all the Objects looking for the Object Prefix
for(i = ObjectsTotal() - 1; i >= 0; i--) {
string objectName = ObjectName(i);
if (StringFind(objectName, objectPrefix) == 0)
ObjectDelete(objectName);
}
FFCalDeinitLibrary();
return(0);
}
int start() {
int i;
bool skipThisEvent;
datetime newsTimeGMT;
// check to make sure we are connected, otherwise exit. Added by MN
if (!IsConnected()) {
Print("News Indicator is disabled because NO CONNECTION to Broker!");
return(0);
}
// Perform remaining checks once per minute
if (!IsEA_Call && TimeMinute(AltTimeLocal()) == previousMinute)
return (true);
previousMinute = TimeMinute(AltTimeLocal());
// Print("FFCal NEW MINUTE...Refreshing News from XML file...");
// Init the buffer array to zero just in case
ArrayInitialize(ExtMapBuffer0, 0);
ArrayInitialize(ExtMapBuffer1, 0);
FFCalRefreshEvents (UseDailyFXAsSource);
// Get the currency pair, and split it into the two countries
string pair = Symbol();
string country1 = StringSubstr(pair, 0, 3);
string country2 = StringSubstr(pair, 3, 3);
// Parse the loaded events looking for an event to report
minutesTillNextEvent = 10080; // (a week)
globalNewsSelectedTotal = 0;
for (int index = 0; index < FFCalGetTotalEvents(); index++) {
skipThisEvent = false;
string eventData[8], nexteventData[8];
FFCalGetEvent (index, eventData);
// Test against filters that define whether we want to skip the selected announcement
if (country1 != eventData[FFCAL_COUNTRY] && country2 != eventData[FFCAL_COUNTRY] &&
((!ReportAllForUSD || eventData[FFCAL_COUNTRY] != "USD") &&
(!ReportAllForEUR || eventData[FFCAL_COUNTRY] != "EUR") &&
(!ReportAllForGBP || eventData[FFCAL_COUNTRY] != "GBP") &&
(!ReportAllForNZD || eventData[FFCAL_COUNTRY] != "NZD") &&
(!ReportAllForJPY || eventData[FFCAL_COUNTRY] != "JPY") &&
(!ReportAllForAUD || eventData[FFCAL_COUNTRY] != "AUD") &&
(!ReportAllForCAD || eventData[FFCAL_COUNTRY] != "CAD") &&
(!ReportAllForCHF || eventData[FFCAL_COUNTRY] != "CHF") &&
(!ReportAllForCNY || eventData[FFCAL_COUNTRY] != "CNY")))
skipThisEvent = true;
if (ReportAllForAll)
skipThisEvent = false;
if (!SelectHighImpactEvents && eventData[FFCAL_IMPACT] == "High")
skipThisEvent = true;
if (!SelectMediumImpactEvents && eventData[FFCAL_IMPACT] == "Medium")
skipThisEvent = true;
if (!SelectLowImpactEvents && eventData[FFCAL_IMPACT] == "Low")
skipThisEvent = true;
if (!SelectSpeaksEvents && (StringFind(eventData[FFCAL_TITLE], "speaks") != -1 ||
StringFind(eventData[FFCAL_TITLE], "Speaks") != -1) )
skipThisEvent = true;
if (eventData[FFCAL_TIME] == "All Day" ||
eventData[FFCAL_TIME] == "Tentative" ||
eventData[FFCAL_TIME] == "")
skipThisEvent = true;
// If this event is not skipped, then log it into the draw buffers
if (!skipThisEvent) {
EventsArray[globalNewsSelectedTotal] = index;
// Print(EA_NAME, "EVENT : Title: ", eventData[FFCAL_TITLE],
// " Country: ", eventData[FFCAL_COUNTRY],
// " Date: ", eventData[FFCAL_DATE],
// " Time: ", eventData[FFCAL_TIME],
// " Impact: ", eventData[FFCAL_IMPACT],
// " Forecast: ", eventData[FFCAL_FORECAST],
// " Previous: ", eventData[FFCAL_PREVIOUS],
// " GMT: ", eventData[FFCAL_GMT],
// " NOW: ", TimeToStr(AltTimeCurrent() - TimeGMT(), TIME_MINUTES));
globalNewsSelectedTotal++;
}
}
for (index = 0; index < globalNewsSelectedTotal; index++) {
FFCalGetEvent (EventsArray[index], eventData);
// First, convert the announcement time to seconds (in GMT)
newsTimeGMT = StrToTime(eventData[FFCAL_GMT]);
// Now calculate the minutes until this announcement (may be negative).
// minsTillNews = (newsTimeGMT - LocalTimeGMT()) / 60;
minsTillNews = (newsTimeGMT - TimeLocalToGMT(AltTimeLocal())) / 60;
// This "if" section added by MN
// If minsTillNews is zero, then it's the time our news is hit
if ( ((minsTillNews == 0) && (LastTimeAlert1 != newsTimeGMT)) ||
(!IsEA_Call && LineShowOldEvents && minsTillNews < 0) ||
(!IsEA_Call && LineShowFutureEvents && minsTillNews > 0) ){
DisplayVerticalNews(EventsArray[index], 0);
for (int shift = 1; shift < MAX_SIMULTANEOUS_EVENTS; shift++) {
FFCalGetEvent (EventsArray[index + shift], nexteventData);
if (eventData[FFCAL_TIME] == nexteventData[FFCAL_TIME])
DisplayVerticalNews(EventsArray[index + shift], shift);
}
// only draw once per announcement
if ((minsTillNews == 0) && (LastTimeAlert1 != newsTimeGMT))
LastTimeAlert1 = newsTimeGMT;
}
// Keep track of the most recent news announcement.
// Do that by saving each one until we get to the
// first annoucement that isn't in the past; i.e.
// minsTillNews > 0. Then, keep this one instead for
// display, but only once the minutes until the next
// news is SMALLER than the minutes since the last.
if (minsTillNews < 0 || MathAbs(minutesTillNextEvent) > minsTillNews) {
indexOfNextEvent = index;
minutesTillNextEvent = minsTillNews;
}
// Do alert if user has enabled
if (AlertXMinutesBefore != -1 && minsTillNews == AlertXMinutesBefore)
Alert(AlertXMinutesBefore, " minutes until news for ", pair, ": ", eventData[FFCAL_TITLE]);
if (AlertYMinutesBefore != -1 && minsTillNews == AlertYMinutesBefore)
Alert(AlertYMinutesBefore, " minutes until news for ", pair, ": ", eventData[FFCAL_TITLE]);
// Buffers are set up as so:
// ExtMapBuffer0 contains the time UNTIL each announcement (can be negative)
// e.g. [0] = -372; [1] = 25; [2] = 450; [3] = 1768 (etc.)
// ExtMapBuffer1[0] has the mintutes since the last annoucement.
// ExtMapBuffer1[1] has the mintutes until the next annoucement.
ExtMapBuffer0[index] = minsTillNews;
}
// Cycle through the events array and pick out the most recent
// past and the next coming event to put into ExtMapBuffer1.
// Put the corresponding impact for these two into ExtMapBuffer2.
bool first = true;
ExtMapBuffer1[0] = 99999;
ExtMapBuffer1[1] = 99999;
ExtMapBuffer2[0] = 0;
ExtMapBuffer2[1] = 0;
for (i = 0; i < globalNewsSelectedTotal; i++)
{
FFCalGetEvent (EventsArray[i], eventData);
if (ExtMapBuffer0[i] >= 0 && first)
{
first = false;
// Put the relevant info into the indicator buffers...
// Minutes SINCE - - - - - - - - - - - - - - - - - - - - - - - - -
// (does not apply if the first event of the week has not passed)
if (i > 0) {
string previouseventData[8];
FFCalGetEvent (EventsArray[i - 1], previouseventData);
ExtMapBuffer1[0] = MathAbs(ExtMapBuffer0[i-1]);
ExtMapBuffer2[0] = FFCalImpactToNumber(previouseventData[FFCAL_IMPACT]);
}
// Minutes UNTIL - - - - - - - - - - - - - - - - - - - - - - - - -
// Check if past the last event.
if (ExtMapBuffer0[i] > 0 || (ExtMapBuffer0[i] == 0 && ExtMapBuffer0[i+1] > 0))
ExtMapBuffer1[1] = ExtMapBuffer0[i];
ExtMapBuffer2[1] = FFCalImpactToNumber(eventData[FFCAL_IMPACT]);
}
}
// If we are past all news events, then neither one will have been
// set, so set the past event to the last (negative) minutes
if (ExtMapBuffer1[0] == 0 && ExtMapBuffer1[1] == 0) {
ExtMapBuffer1[0] = ExtMapBuffer0[i-1];
ExtMapBuffer1[1] = 999999;
}
if (!IsEA_Call && NewsDashBoardShow) {
for( i = 0; i < NewsDashboardEvents; i++ )
OutputToChart( i, indexOfNextEvent );
}
return (0);
}
// Draws the news veritically on the Chart so that we
// have a visual reference of when it occurs. Added by MN
void DisplayVerticalNews(int index, int shift) {
string eventData[8];
FFCalGetEvent (index, eventData);
// datetime TheTime = StrToTime(eventData[FFCAL_GMT]);
string dispCountry = eventData[FFCAL_COUNTRY];
string dispTitle = eventData[FFCAL_TITLE];
int eventImpact = FFCalImpactToNumber(eventData[FFCAL_IMPACT]);
datetime theTime = TimeGMTToCurrentTime(StrToTime(eventData[FFCAL_GMT]));
// if (theTime > AltTimeCurrent())
// return;
int BarShift = iBarShift(NULL, 0, theTime);
double Pivot, Height = 0.0;
// Calculate our pivot point to determine where to place the news text
Pivot = (iHigh(NULL, 1440, BarShift + 1) + iLow(NULL, 1440, BarShift + 1) + iClose(NULL, 1440, BarShift + 1)) / 3;
// If Open price is above the Pivot determine our height
if (Open[BarShift] > Pivot)
Height = Low[iLowest(NULL, 0, MODE_LOW, 5, BarShift)] - LineTextVerticalShift * Point;
else
// Otherwise Open is below Pivot
Height = High[iHighest(NULL, 0, MODE_HIGH, 5, BarShift)] + LineTextVerticalShift * Point;
// Draw a vertical line at the time of the news if it hasnt already been drawn
color actualcolor;
string vLineObject = StringConcatenate(objectPrefix, theTime);
if (ObjectFind(vLineObject) == -1) {
ObjectCreate(vLineObject, OBJ_TREND, 0, theTime, 0, theTime, iHigh(NULL, 0, BarShift)); // experimental
ObjectSet(vLineObject, OBJPROP_STYLE, STYLE_DOT);
// Put object in the background behind any other object on the chart
ObjectSet(vLineObject, OBJPROP_BACK, true);
actualcolor = ColorImpactUndefined;
} else
actualcolor = ObjectGet(vLineObject, OBJPROP_COLOR);
if (eventImpact == FFCAL_IMPACT_HIGH && (actualcolor != ColorImpactHigh))
ObjectSet(vLineObject, OBJPROP_COLOR, ColorImpactHigh);
else if (eventImpact == FFCAL_IMPACT_MEDIUM &&
(actualcolor != ColorImpactHigh && actualcolor != ColorImpactMedium))
ObjectSet(vLineObject, OBJPROP_COLOR, ColorImpactMedium);
else if (eventImpact == FFCAL_IMPACT_LOW &&
(actualcolor != ColorImpactHigh && actualcolor != ColorImpactMedium && actualcolor != ColorImpactLow))
ObjectSet(vLineObject, OBJPROP_COLOR, ColorImpactLow);
string headlineObject = StringConcatenate(objectPrefix, theTime, shift);
if (ObjectFind(headlineObject) == -1) {
ObjectCreate(headlineObject, OBJ_TEXT, 0, theTime - LineTextLeftShift + LineTextRightShift * shift, Height);
// Rotate the text 90 degrees
ObjectSet(headlineObject, OBJPROP_ANGLE, 90);
ObjectSetText(headlineObject, StringConcatenate(dispCountry, " ", dispTitle), LineTextSize, "Arial", LineTextColor);
}
//force a redraw of our chart
WindowRedraw();
return(0);
}
int OutputToChart( int id, int idxOfNext ) {
static int pos_Y = 0;
if( id == 0 ) {
string ObjectSource = objectPrefix + "Source";
pos_Y = 4;
if (ObjectFind(ObjectSource) == -1) {
ObjectCreate(ObjectSource, OBJ_LABEL, 0, 0, 0);
ObjectSet(ObjectSource, OBJPROP_CORNER, NewsDashboardCorner);
ObjectSet(ObjectSource, OBJPROP_XDISTANCE, 10);
ObjectSet(ObjectSource, OBJPROP_YDISTANCE, pos_Y);
}
if (!UseDailyFXAsSource && !UseGlobalTimeReference)
ObjectSetText(ObjectSource, "Source: ForexFactory (date " +
TimeToStr(AltTimeCurrent(), TIME_DATE|TIME_MINUTES) + " / GMT " +
TimeToStr(TimeLocalToGMT(AltTimeLocal()), TIME_DATE|TIME_MINUTES) +
")", NewsDashboardTextSize - 2, "Arial Bold", NewsDashboardTitleColor);
else
ObjectSetText(ObjectSource, "Source: DailyFX (date " +
TimeToStr(AltTimeCurrent(), TIME_DATE|TIME_MINUTES) + " / GMT " +
TimeToStr(TimeLocalToGMT(AltTimeLocal()), TIME_DATE|TIME_MINUTES) +
")", NewsDashboardTextSize - 2, "Arial Bold", NewsDashboardTitleColor);
}
pos_Y += NewsDashboardTextSize + 6;
color NewsDashboardTextColor_ = NewsDashboardTextColor;
color TxtColorImpact_ = ColorImpactHigh;
string eventData[8];
FFCalGetEvent (EventsArray[idxOfNext + id], eventData);
string title = eventData[FFCAL_TITLE];
string country = eventData[FFCAL_COUNTRY];
string impact = eventData[FFCAL_IMPACT];
string forecast = eventData[FFCAL_FORECAST];
string previous = eventData[FFCAL_PREVIOUS];
int minutesTillEvent = (StrToTime(eventData[FFCAL_GMT]) - TimeLocalToGMT(AltTimeLocal())) / 60;
string TimeStr = GenerateTimeString (TimeGMTToLocalTime (StrToTime(eventData[FFCAL_GMT])));
if (impact == "Low") TxtColorImpact_ = ColorImpactLow;
if (impact == "Medium") TxtColorImpact_ = ColorImpactMedium;
string Line1Object = objectPrefix + "L1N" + id;
string Line2Object = objectPrefix + "L2N" + id;
if( id != 0 && StringLen (title) == 0 ) return( 0 );
if (ObjectFind(Line2Object) == -1) {
ObjectCreate(Line2Object, OBJ_LABEL, 0, 0, 0 );
ObjectSet(Line2Object, OBJPROP_CORNER, NewsDashboardCorner );
ObjectSet(Line2Object, OBJPROP_XDISTANCE, 10 );
}
ObjectSet( Line2Object, OBJPROP_YDISTANCE, pos_Y );
if ( minutesTillEvent == 999999 ) {
ObjectSetText( Line2Object, " (No more events this week)", NewsDashboardTextSize, "Arial Bold", ColorImpactOld );
} else {
if (NewsDashboardSummary) {
if ( minutesTillEvent < 0 )
TxtColorImpact_ = ColorImpactOld;
ObjectSetText( Line2Object, country + ": " + TimeStr + title + makeKPIsummary( forecast, previous ), NewsDashboardTextSize, "Arial Bold", TxtColorImpact_ );
if (ObjectFind(Line1Object) != -1)
ObjectDelete(Line1Object);
} else {
if ( minutesTillEvent < 0 )
NewsDashboardTextColor_ = ColorImpactOld;
ObjectSetText( Line2Object, country + ": " + title + makeKPIsummary( forecast, previous ), NewsDashboardTextSize, "Arial Bold", NewsDashboardTextColor_ );
pos_Y += NewsDashboardTextSize + 4;
if (ObjectFind(Line1Object) == -1) {
ObjectCreate( Line1Object, OBJ_LABEL, 0, 0, 0 );
ObjectSet( Line1Object, OBJPROP_CORNER, NewsDashboardCorner );
ObjectSet( Line1Object, OBJPROP_XDISTANCE, 10 );
ObjectSet (Line1Object, OBJPROP_YDISTANCE, pos_Y);
}
ObjectSetText( Line1Object, TimeStr + impact + " impact event [GMT " + eventData[FFCAL_TIME] + "]", NewsDashboardTextSize, "Arial Bold", TxtColorImpact_ );
}
}
return( 0 );
}
string makeKPIsummary( string f, string p ) {
if( StringLen( f ) == 0 && StringLen( p ) == 0 )
return( "" );
if( StringLen( f ) > 0 && StringLen( p ) == 0 )
return( " (" + f + "/-.-)" );
if( StringLen( f ) == 0 && StringLen( p ) > 0 )
return( " (-.-/" + p + ")" );
return( " (" + f + "/" + p + ")" );
}
string GenerateTimeString (datetime time) {
int minutesTillEvent = (time - AltTimeLocal()) / 60;
// string times = TimeToStr (TimeGMTToCurrentTime (StrToTime(eventData[FFCAL_GMT])), TIME_DATE | TIME_MINUTES);
int Days, Hours, Mins; // to display time in days, hours, minutes
string TimeStr;
// If the time is 0 or negative, we want to say
// "xxx mins SINCE ... news event", else say "UNTIL ... news event"
string sinceUntil = "until ";
int dispMins = minutesTillEvent;
if (minutesTillEvent <= 0) {
sinceUntil = "since ";
dispMins *= -1;
}
// added the following to show hours and days for longer durations
// this could be enhanced to suppress 0 hours and 0 minutes
if (dispMins == 999999) {
TimeStr = " (No more events this week)";
} else if (dispMins < 60) {
TimeStr = dispMins + " mins ";
} else { // time is 60 minutes or more
Hours = MathRound(dispMins / 60);
Mins = dispMins % 60;
if (Hours < 24) { // less than a day: show hours and minutes
TimeStr = Hours + " hrs " + Mins + " mins ";
} else { // days, hours, and minutes
Days = MathRound(Hours / 24);
Hours = Hours % 24;
TimeStr = Days + " days " + Hours + " hrs " + Mins + " mins ";
}
}
return (TimeStr + sinceUntil);
}