Skip navigation
All Places > PI Developers Club > Blog > 2009 > August

Sample OPC UA Client

Posted by amaksumov Aug 31, 2009



Associating times with paths

Posted by smohr Aug 28, 2009

This is covered in the CTP documentation, but I want to explore the issue a little here.  The way the WS matches times with paths works like this.  If a time has the name of a path in its datasource property, that time is applied to the path.  If not, the first time in the array is used for a path.


So, if you are doing a trend with three tags, you send the WS three paths and one time range. Got a single value and a trend in your app?  Give use all the paths, then give us the time range of the trend, followed by the single value time instant with the single value path as the value of its datasource property. 


We never, ever, march through the context array trying to match paths and tags one-for-one in order.  This might seem a little counter-intuitive, but it works pretty well and keeps the context array short -- an important consideration for data volume on the wire.  Bear in mind there are a few other wrinkles.  Ranges and instants are just the first context types, and we plan on "casting" contexts.  There are also some provisions in the context realm for intermediate structures to provide for M:M associations between paths and contexts, but that seemed like overkill on the first release.


What do you say?  Can you think of use cases that are hard to satisfy with the rules we have in the CTP?

In my last post, I pre-announced the availability of PI JDBC Driver 1.0 on vCampus this week. I'm pleased to say the software is now available in the Download Center and documentation can be found in the Library. I also pre-announced that I'd be writing more often, so I guess I'm 2 for 2. ;-)


So what are the use cases for PI JDBC Driver?
1) Developing custom Java applications that access PI data
2) Accessing PI data from non-Windows operating systems
3) Using 3rd party Java tools that support JDBC out-of-the-box


For you Java folks out there, check out the PI JDBC Driver manual and system requirements then give this new data access product a try. If you have any questions or run into any issues, feel free to contact our outstanding Technical Support. We also created a new Discussion Hall forum dedicated to PI JDBC Development.


Oh, and if you find this to be a valuable product, leave us a note somewhere here on vCampus - we never get tired of hearing your success stories. Now I'm off to grab my morning cup of Java...

In my previous blog post I wrote about the PI OPC UA Server, but I didn't say anthing about the client side. You might be wondering how could someone develop such an application and how difficult that can be. If you want to learn how to do it, you came to the right place! I will show you how to do it in 10 easy steps. You can also get the attached sample project and build it with Visual Studio 2005 or 2008.

Since we had the OPC UA webinar yesterday, I think it would be appropriate to make a very brief point about the two prospective products.  OPC UA goes the big standards route.  You have to invest in a lot in terms of the object model and infrastructure, but it offers a lot in return.  If you want industry  standards and a big, robust infrastructure, OPC UA is your path.  PI Web Services, in contrast, was designed from the start to be the way to get data out of PI with a minimum investment of time and effort.  I'm grossly oversimplifying, but the only things the two have in common is web service protocols and PI data.


So -- do you think the path-context-manner-column calling convention meets that design intention?

Hi Folks! This is Jay Lakumb here. Welcome back to the PM Blog. You've come to the right place for current news and future plans regarding all PI products. Yes, we've been a little quiet lately. Too quiet. All that is about to change as we introduce not one, but two new products today...


First, a little about myself. I've been with OSIsoft for nearly 6 years, based in our San Leandro office. I started as the Dev Lead for PI WebParts then joined the PM team in 2007. I'm responsible for PI Data Access products (including all the ones represented on vCampus) as well as Microsoft Office integration (including DataLink and DataLink for Excel Services). I'm passionate about a few things: my 6-year old son (which parent isn't?), traveling, golf!, and delivering great software to our customers and partners.


Now on to the news of the day. As you probably know, we announced the availability of the PI Web Services Community Technology Preview (CTP) last month. As of this week, the VM is finally live. My colleague Steve blogged about it here and here. Feel free to try this out and let us know what you think. Considering your past, current, and future projects using web services, how well does PI Web Services match your needs? You can send feedback (questions, comments, or criticisms - be nice!) to, post in the Web Services and PI discussion forum, or just add a comment here on the PM blog.


The other piece of news, in advance of the webinar on Aug 26, is the availability of PI OPC UA Server CTP. Again, the VM is live and here is the URL:


Please send feedback to, post in the PI OPC Development forum, or add a comment here. We will be providing more info on how to access both of these data access tools at the webinar tomorrow. Look forward to seeing you there!


As a bonus, I'll pre-announce the availability of PI JDBC Driver 1.0 on vCampus. Keep your eyes open for the bits in the Download Center (later this week?). With the recent releases of 64-bit PI SDK, AF SDK, and PI OLEDB, this is an exciting year for enabling newer and better access to all the goodness in the PI System. I encourage you to stay tuned to this blog - we'll try to share more than once every 10 months... ;-)


Java sample

Posted by smohr Aug 25, 2009

Just so our friends in the Java world don't feel left out:


This is a CTP client of the IPIClientServices interface.  It is a little less involved than the WinForms sample.  It uses SWT + Axis, so some assembly of your environment may be required.


Tags, we've got tags

Posted by smohr Aug 25, 2009

Let's say you've jumped in and created a client.  You want to see if it works with the CTP.  What's a good path?


First, the PI server is named jmaytumwin2k8 (hi, John!).  It has the usual suspects -- sinusoid, sinusoidu, cdep158, cdt158, and so forth.  I've also created two tags to test data entry: InsertTest1 and InsertTest2. Since we don't have your user ID in a secure, federated kind of way, I had to open these tags up to the world for read/write access.  So you can get your data in there, but look fast and don't expect any great degree of integrity.


CTP documentation

Posted by smohr Aug 25, 2009

The CTP documentation is up in the vCampus Library, under "vCampus PI Products Kit" and then"Data Access Technologies".  The last entry (currently) is "PI Web Services -- Community Technology Preview". If you click that, you get a PDF which you can save locally.


The documentation contains guidance on using the two interfaces and the structures used to pass parameters to the web service and receive data.  Just remember the path to the WSDL is different.  For that, see


How to access the CTP WS

Posted by smohr Aug 25, 2009

We finally have a hosted CTP of the PI Web Service.  We'll be posting the documentation soon, but there was one change.  Our host uses a static IP address, so we are using static WSDL that has been edited to reflect this.  If you use either URL below, you can create a web service reference or create an InfoPath form that points to the hosted CTP web service.  You can kick the tires for yourself!    (for the InfoPath friendly interface)   (for the more flexible interface)






Okay, NOW it's ready

Posted by smohr Aug 25, 2009

Sample code for a WinForms client can be found here:


The best part?  It targets the live CTP server...running now.


The code isn't glamorous, but it does provide examples for all three methods of the IPIExtendedServices interface.

The last posts on my Blog did show you some small bits of code I think are useful. However, I recently discovered this almost ready white paper floating around on my desktop. After I have finished it (Thanks to the original contributors for some of the included material!) I thought it is never going to be "finished" at all.


So I would like to invite you to take a look at the "A Few ProcessBook VBA Tips" white paper on the vCampus Library (under the "White Papers and Tutorials>PI ProcessBook" branch) and let me know what you think of it as a "working document" - I would like to ask you if I should continue to collect little code snippets, write my own, etc. and add this to the White Paper to have one place to look for ideas and VBA sample code?

Thanks to all of you who made the discussion forums active, we now have over 1500 posts on the discussion forums! With the launch of OSIsoft vCampus in mid-November 2008, that represents less than 200 business days and an average of 7.5 posts/day! I think this is great, especially when you think that the few first months were (obviously) less active... Thanks again and keep the questions and comments coming!


You might also have noticed I was less active on the forums these last days (thanks to Rhys, who covered for me! )... well I have an excuse for this - a good excuse that is: I was less active because I was with our "Events" team, helping at the preparation of the OSIsoft vCampus Live! event, our 'technology' event of the year (and the the years to come!).


Indeed, you might remember how our last Users Conference was made more "business-oriented", focusing on business and industry topics. Well, the "other half" of the conference - the technical half - is the OSIsoft vCampus Live! event. It will be held in...  actually, no... I won't be talking about this in my blog...  we created a new blog for that event, so make sure you check it out and subscribe to its RSS feed!


As you'll see on that blog during the coming days and weeks, we are looking forward to getting your thoughts, ideas and maybe even some of your code :))



Over the past 20 years OSIsoft has hosted user events for our community.  The annual Users Conference has been the main stay of these events.   Four years ago, we added a Developer Conference (DevCon) specifically for the development community so that we could focus on more detailed technical aspects of our offering- which were sometimes shorted in the Users Conference to focus on how customers were solving their business problems using PI.  Another important reason for the change is that some of the enhancements in the PI System needed to be supported in the server core before they are added to clients and other user tools.  This presents an opportunity for partners and developers to start their work at the same time, so that there are rich tools and applications available for customers quickly.  DevCon proved this was important to users and partners alike, who want to expand the capability of their PI systems. 


The trend continues this year as we rename DevCon,  change its format for a more interactive and dynamic event,  and expand our invitations to include more technical material to more technical users.  


If you are "all business" you won't want to miss the annual Users Conference this year (April 2010).  If you an über geek or someone in the middle, then you need to come where PI geeks meet... The OSIsoft vCampus Live! 2009 event!


I know what you're thinking: "Another conference?!  In this day of limited travel budgets?"  In the past it has been necessary to bring both technical and business people to the Users Conference and participate in both discussions which limited the depth of conversations for both groups.  Or one person had to pick between the technical and business sessions they wanted to attend.  By separating these events we can offer more content and increase the amount of open dialog between you, us and our partners.  


Making these changes delivers a better event to each group - one directed specifically at how you use PI - and at the least possible expense to you.  In the end it is all about creating an event that is the most valuable for you!


This year's OSIsoft vCampus Live! event is shaping up to be a whopper!  Here's what we have lined up for you:

  • An under-the-hood technical look at PI and PI development
  • Live, in-person roundtables
  • Extreme Code Reviews
  • Hands-On Learning Labs
  • Network with peers and OSIsoft personnel
  • Deep dive into current and upcoming technologies 

If you're interested but still not sure if it's right for you, drop us a line on this blog or send an email to - We'll be glad to answer your questions about the event and its agenda.  It is being held on December 1st and 2nd (with an optional day December 3rd) at the glorious Palace Hotel in downtown San Francisco.


Already Convinced?  Take a look at the registration site (click here). You'll notice that members of the OSIsoft Virtual Campus (vCampus) program are eligible for a discount.


This event is designed for users looking to customize or expand out of the box capabilities and members of OSIsoft Virtual Campus, with limited seating. We strive to keep an intimate climate with lots of interaction between attendees and Product Managers and Developers.  So don't wait to register!


Wordsize Trends

Posted by cescamilla Aug 10, 2009


Showing data from a PI Server in a trend fashion in PI ProcessBook or Excel (with PI DataLink) is really simple (even for most end-users or at least that is what we like to think, thanks to the great smart clients OSIsoft provides :). However there are some custom devices, graphic sizes and specific graphics that cannot be created with the amazing trend control included in PI ProcessBook - a control that is only available inside PI ProcessBook and (in a different flavor) inside Excel.


 There also is the issue of having an effective way to show information about some values in a Word document, or a printed piece of paper. And this is exactly what we will attempt here: say you want to know the values for tag CDT158cdt158.png and its trend for the last 24 hours in an easy to read and easy to understand way inside a text flow (like this one). We'll achieve this programmatically and you can use this code anywhere - from mobile devices to a Word add-in.


In order to show trend data into a custom size trend we may need to go back to high school and remember how to do a graphical representation of data - the hardest part being to fit and fill the X (horizontal) axis as it is mapped to the 'time' variable. We'll have another problem too: graphics have the 'origin' or 'zero' vector at the bottom-left corner of the image but the graphic devices have the 'origin' or 'zero' vector at the top-left corner, so this has to be taken in consideration before plotting the values (otherwise we will get upside-down graphics).

The Contents of a Graphic

They say an image is worth 1 Megapixel (hehehehe) and here we go, our first image:




  The blue boxes show the way the computer screen is addressed.
  The orange boxes show the way a trend is interpreted.
  The purple part is the trending area.


  We need to keep this in mind as we draw the trend step-by-step.

Connecting to the PI Server

For simplicity sake I'll be using the OSIsoft.PISDK.Controls.ServPickList control, which is part of OSIsoft.PISDK.Controls.PISDKCtrlDlg Assembly and has the SelectedServer.Open() Method and the SelectedServer.Connected Property which we will be calling. If you do not have the PI SDK Control Dialogs in your Visual Studio 2008 toolbox then:

  1. Choose items
  2. Select Browse
  3. Navigate to \PIPC\PISDK and select the file OSIsoft.PISDK.Controls.PISDKCtrlDlg.dll
  4. Now you should be able to add [ServPickList / OSIsoft.PISDK.Controls] to your toolbox

Let’s code!

  1. Go to File, New Project, Windows Form Application
  2. Add the following controls to the Main Form
    • ServPickList (srvPickList1)
    • Button (buttonConnect)
    • ComboBox (tagComboBox)
    • 2 DateTimePicker (dateTimePickerStart, dateTimePickerStop)
    • PictureBox (pictureBox1)
    • [optional] Timer (timer1)
      The beauty of using the PI SDK's Server Pick List control is that we do not need to do anything else: just 'drag and drop' it into the form and that's all we need to make it work.

  3. Now we want to assign/create the "click" action to the 'buttonConnect'
    • Check if the selected server is connected, open a connection if it is not and enable the tag's control
    • Clear the Tag's control
    • Populate the ComboBox control with all the tags available from the selected server.

private void buttonConnect_Click(object sender, EventArgs e)
    //Check if the SelectedServer is connected, if it is not, then
    //open a connection to the server and enable the tagComboBox control.

    if (!servPickList1.SelectedServer.Connected)
        //open selected server
        //enable tag combobox.
        tagComboBox.Enabled = true;

    //Clear the tagComboBox (in case it is not the first click we get)

    //Populate the ComboBox with all the tags available from the selected server
    foreach (PISDK.PIPoint tmpPoint in servPickList1.SelectedServer.PIPoints)
        //Add point name to tag combo box


The result should look like this:

  1. We need to set the date of the date pickers so let's set the start time to 24 hours ago (*-1d) and the stop time to now (*). Note that we could make it the "now" change dynamically but this is beyond the scope of this article.

    Add the following lines in the Form1's "Load" event:

//Set the start date to 24 hours ago
dateTimePickerStart.Value = DateTime.Now.AddDays(-1);
//Set the stop date to now
dateTimePickerStop.Value = DateTime.Now;

  1. Call In the ComboBox we will need to change the "SelectedIndexChanged" event and add a function called update_trend() which we will define in the following step, for now it is enough to prototype it like:

private void update_trend(){}


//Index changed should contain this:

  1. The DateTimePicker controls should show the exact selected date with the seconds... so, let's change their properties to do so:
    • Change the 'Format' property to 'Custom'
    • Then change the 'CustomFormat' property to something meaningful (I'll use the standard PI format: dd-MMM-yy HH:mm:ss)
      Make sure you do this for both date time pickers.

  2. Finally (and for the most fun) we need to create a PictureBox (to output the trend we will be creating...), I'll allow the PictureBox to occupy most of the space left on my form and I'll set the 'Anchor' property to all borders (Top, Bottom, Left, Right) this will make the image resize as the user resizes the window.
  3. Now we need some global variables:
    (integers) XX and YY (size of the bitmap in pixels - horizontally and vertically, respectively)
    (Bitmap) mybitmap - The image we will be using and setting to the pictureBox1
    (Grpahics) myGraphic - Encapsulator, I'll use this to expose extra methods (DrawLine, FillRectangle, Clear)

  int XX = 640; //Horizontal resolution
  int YY = 480; //Vertical resolution
  Bitmap myBitmap; //The bitmap image I'll be using to display the trend
  Graphics myGraphic; //The graphics device needed to expose
                      //some additional methods (Clear, FillRectangle and DrawLine)

  1. Let's now define the update_trend() method,
    Note that this method may be a bit complex if you have never made custom graphics.

    Essentially, the update method needs to:
    • Check if we are connected to the selected PI Server and if the user has selected a tag
    • Create variables for the maximum and minimum values
    • Get the 'zero' and 'span' attributes for this particular tag
    • Determine the dimensions of the trend given the values to graph
    • Create a Graphics object from the Bitmap and clear it with a white background.
    • Set 'zero' to be the Maximum value of zero or pointMin
    • Set 'span' to be the Minimum value of (zero + span) or pointMax
    • Fill a rectangle from (0, span - zero) to (Horizontal size of the bitmap, (Vertical size of the bitmap - zero) / range)
    • Get all necesary values from the tag in the pi server, pair them and draw lines between them if both are numeric values (of type "System.Single")
    • Find the last numerico value from the list and draw a red rectangle at the right end of the graphic with that value
    • Show the bitmap

  private void update_trend()
   //Check if we are connected to the selected server in the server picklist and if
   //the user has selected a tag in the ComboBox
   if (servPickList1.SelectedServer.Connected)
    //Get the tag name from the selected item in the ComboBox
    string tagname = tagComboBox.SelectedItem.ToString();
    //Get the point object
    PISDK.PIPoint point = servPickList1.SelectedServer.PIPoints[tagname];
    //Get values from the PI Server
    PISDK.PIValues myValues = point.Data.PlotValues(dateTimePickerStart.Value,

    //Variables to help define the height of the trend
    float? NpointMax = null, NpointMin = null;
    float pointMax = 0, pointMin = 0;

    //Get zero from PI Points Database
    float zero = (float)point.PointAttributes["zero"].Value;
    //Get span from PI Points Database
    float span = (float)point.PointAttributes["span"].Value;

    //Check all values to find out the maximum and minimum values
    foreach (PISDK.PIValue tmpValue in myValues)
     //If it has a numeric value
     if (tmpValue.Value.GetType().ToString() == "System.Single")
      //Ff vertical maximum has a value
      if (NpointMax.HasValue)
       //Then keep the highest one (NpointMax or the current item value)
       NpointMax = Math.Max((float)NpointMax, (float)tmpValue.Value);
       //Ff no value, set it to the tag's value
       NpointMax = (float)tmpValue.Value;

      //If vertical minimum has a value
      if (NpointMin.HasValue)
       //Then keep the lowest one (NpointMin or the current item value)
       NpointMin = Math.Min((float)NpointMin, (float)tmpValue.Value);
       //If no value, set it to the tag's value
       NpointMin = (float)tmpValue.Value;

    //if NpointMax is not null
    if (NpointMax.HasValue)
     //then set pointMax to NpointMax
     pointMax = (float)NpointMax;

    //if NpoinMin is not null
    if (NpointMin.HasValue)
     //then set pointMin to Npointmin
     pointMin = (float)NpointMin;

    //Calculate the range
    float range = pointMax - pointMin;
    //Add 3% of Span up
    pointMax += Convert.ToSingle(range * 0.03);
    //Add 3% of Span down
    pointMin -= Convert.ToSingle(range * 0.03);
    //Minimum range is 1, range is always positive
    range = Math.Max(1, pointMax - pointMin);
    //Should be constant used for the graphics part
    long ticks = dateTimePickerStop.Value.Ticks - dateTimePickerStart.Value.Ticks;

    //Encapsulate the Bitmap into a Graphics object so we can use the Draw* methods
    myGraphic = Graphics.FromImage(myBitmap);
    //Set the Graphics background to white (using the encapsulation as a mean)

    //Get zero value from PI Database's zero
    zero = Math.Max(zero, pointMin);
    //Get span value from PI Database's span
    span = Math.Min(span, pointMax);

    //Fill the rectangle area that is included in the painted area
    //From left, top
    0, YY - (span - pointMin) * YY / range,
    //To right, bottom
    XX, (YY - (zero - pointMin) * YY / range) - (YY - (span - pointMin) * YY / range) );

    //Loop through the values to display them
    for (int x = 1; x < myValues.Count; x++)
     //If this value and the next one are numerical
     if (myValues[x].Value.GetType().ToString() == "System.Single" &&
         myValues[x + 1].Value.GetType().ToString() == "System.Single")
      //Draw a line between two points
       //Current date ticks minus first date ticks times pixels divided by ticks
       (myValues[x].TimeStamp.LocalDate.Ticks - myValues[1].TimeStamp.LocalDate.Ticks) * XX / ticks,
       //Image size vertically minus minimum point minus current value
       //times Image size vertically divided by range

       YY - ((float)myValues[x].Value - pointMin) * YY / range,
       //current date ticks minus next date ticks times pixels divided by ticks
       (myValues[x + 1].TimeStamp.LocalDate.Ticks - myValues[1].TimeStamp.LocalDate.Ticks) * XX / ticks,
       //Image size vertically minus minimum point minus next value times Image
       //size vertically divided by range

       YY - ((float)myValues[x + 1].Value - pointMin) * YY / range);
       //The previous lines are necessary to flip, scale, move and translate the
       //graph to an usable region on the graphics device.


    //Find the last numerical value
    for (int  x = myValues.Count; x > 0; x--) {
     if (myValues[x].Value.GetType().ToString() == "System.Single")
      //draw a rectangle in that position
      myGraphic.FillRectangle(Brushes.Red, XX - 2,
                              YY - 2 - ((float)myValues[myValues.Count].Value - pointMin) * YY / range,
                              2, 3);

    //Set the image source of the picture box to the current bitmap.
    pictureBox1.Image = myBitmap;

  1. Now down to the final details: prepare the trend area when the form loads.

  private void Form1_Load(object sender, EventArgs e)
  //Set the width of the picture box
   XX = pictureBox1.Width;
   //Set the height of the picture box
   YY = pictureBox1.Height;
   //Create an empty bitmap
   myBitmap = newBitmap(XX, YY);

  1. That's it... now we can run it and see if it works!
  2. And... for the final act, you can polish by adding this to the resize function:

private void Form1_Resize(object sender, EventArgs e)
   //Set the width of the picture box
   XX = pictureBox1.Width;
   //Set the height of the picture box
   YY = pictureBox1.Height;
   //Create a new bitmap at least 1 in size
   myBitmap = newBitmap(Math.Max(1,XX), Math.Max(1,YY));
   //Redraw trend

  1. Optionally, add a (5 seconds?) timer and add this to its 'Tick' event:

private void timer1_Tick(object sender, EventArgs e) {
  //Update the "end" date time picker
  dateTimePickerStop.Value = DateTime.Now;


Thanks for reading and stay tuned for more!

Last bits

  • As you can see custom trending in your own application/device/console is not difficult.
  • There are a lot of resources for trending and shoThis code does not check for errors, server changes, sign up for updates nor does it handle digital states in tags.
  • This can be used as an image in a web service, or any other kind of application. (More to come about that).. (More to come about that).


Last time edits and corrections

  • Andreas suggested the use of PlotValues instead of Recorded values as PlotValues get the least number of values needed to draw a trend at a given horizontal-pixel resolution.
  • Steve suggested the use of Tagsearch instead of a dropdown list for large systems. ComboBox control remained but usability was changed
  • Error checks are not performed
  • Some objects changed their text value (like the button1) sorry about that, the name in code was not changed though.
  • Wait for PART 2 of this post where it is going to be shown how to use and catch digital state values.
  • Project file is attached in the post.


Microsoft's out of band patch for Visual Studio (MS09-035) is a classic example of how much can go awry when shared library security vulnerabilities are discovered. OSIsoft announced affected products in a 3-Aug-2009 technical support security alert.


Active Template Library is a relatively mature technology for unmanaged code and often used in development of ActiveX and COM based projects. The ATL moniker is better known to frequent fliers as Hartsfield International; but like air travel it's not always obvious if your flight depends on ATL. The same is true in context of the MS09-035 defects.


Because defects can involve ATL methods that are statically linked by Visual Studio, we need to do more than typical Microsoft Patch compatibility testing. The process to check if code is affected by the ATL vulnerabilities is described on MSDN


The remedial steps for MS09-035 require ISVs patch Visual Studio, then build and distribute a new version (CERT's vendor notification specifically requested such a response). Of course, end users need to apply the upgrade; in some cases this might be more than a patch.  This is the captain speaking... I'm sorry to report bad weather in ATLANTA; our service agents are working to arrange alternate flights. Again we're sorry for this delay.


Microsoft published an interesting blog entry describing the Security Development Lifecycle in context of MS09-035.  Out of millions of lines of code it never ceases to amaze me how many issues boil down to a single byte. Anyway, for all of you developing value added products for the PI infrastructure, please apply the Visual Studio patch. 


Since this issue could also be hidden in 3rd party components it's appropriate to review your software supply chain.  Embedded software and ‘Giblets' are especially onerous problems. What if the fix is a major version upgrade or otherwise results in cascading upgrades with problems involving supported platforms?  Software lifecycle and patching can be a complex issue.  Our preliminary findings are just that; it will take more time to fully understand the scope and remedial plan for the Active Template Library vulnerabilities.


I do want to take this opportunity to confirm the PI-SDK has been reviewed and is *NOT* affected by the ATL defect.

Filter Blog

By date: By tag: